]> git.saurik.com Git - cycript.git/blob - Execute.cpp
Fixed some CYInitialize issues noticed back on the iPhone.
[cycript.git] / Execute.cpp
1 /* Cycript - Inlining/Optimizing JavaScript Compiler
2 * Copyright (C) 2009 Jay Freeman (saurik)
3 */
4
5 /* Modified BSD License {{{ */
6 /*
7 * Redistribution and use in source and binary
8 * forms, with or without modification, are permitted
9 * provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the
12 * above copyright notice, this list of conditions
13 * and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the
15 * above copyright notice, this list of conditions
16 * and the following disclaimer in the documentation
17 * and/or other materials provided with the
18 * distribution.
19 * 3. The name of the author may not be used to endorse
20 * or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
25 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
34 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38 /* }}} */
39
40 #include <sqlite3.h>
41
42 #include "Internal.hpp"
43
44 #include <dlfcn.h>
45 #include <iconv.h>
46
47 #include "cycript.hpp"
48
49 #include "sig/parse.hpp"
50 #include "sig/ffi_type.hpp"
51
52 #include "Pooling.hpp"
53
54 #include <sys/mman.h>
55
56 #include <iostream>
57 #include <ext/stdio_filebuf.h>
58 #include <set>
59 #include <map>
60 #include <iomanip>
61 #include <sstream>
62 #include <cmath>
63
64 #include "Parser.hpp"
65 #include "Cycript.tab.hh"
66
67 #include "Error.hpp"
68 #include "JavaScript.hpp"
69 #include "String.hpp"
70
71 char *sqlite3_column_pooled(apr_pool_t *pool, sqlite3_stmt *stmt, int n) {
72 if (const unsigned char *value = sqlite3_column_text(stmt, n))
73 return apr_pstrdup(pool, (const char *) value);
74 else return NULL;
75 }
76
77 struct CYHooks *hooks_;
78
79 /* JavaScript Properties {{{ */
80 JSValueRef CYGetProperty(JSContextRef context, JSObjectRef object, size_t index) {
81 JSValueRef exception(NULL);
82 JSValueRef value(JSObjectGetPropertyAtIndex(context, object, index, &exception));
83 CYThrow(context, exception);
84 return value;
85 }
86
87 JSValueRef CYGetProperty(JSContextRef context, JSObjectRef object, JSStringRef name) {
88 JSValueRef exception(NULL);
89 JSValueRef value(JSObjectGetProperty(context, object, name, &exception));
90 CYThrow(context, exception);
91 return value;
92 }
93
94 void CYSetProperty(JSContextRef context, JSObjectRef object, size_t index, JSValueRef value) {
95 JSValueRef exception(NULL);
96 JSObjectSetPropertyAtIndex(context, object, index, value, &exception);
97 CYThrow(context, exception);
98 }
99
100 void CYSetProperty(JSContextRef context, JSObjectRef object, JSStringRef name, JSValueRef value, JSPropertyAttributes attributes) {
101 JSValueRef exception(NULL);
102 JSObjectSetProperty(context, object, name, value, attributes, &exception);
103 CYThrow(context, exception);
104 }
105
106 void CYSetProperty(JSContextRef context, JSObjectRef object, JSStringRef name, JSValueRef (*callback)(JSContextRef, JSObjectRef, JSObjectRef, size_t, const JSValueRef[], JSValueRef *), JSPropertyAttributes attributes) {
107 CYSetProperty(context, object, name, JSObjectMakeFunctionWithCallback(context, name, callback), attributes);
108 }
109 /* }}} */
110 /* JavaScript Strings {{{ */
111 JSStringRef CYCopyJSString(const char *value) {
112 return value == NULL ? NULL : JSStringCreateWithUTF8CString(value);
113 }
114
115 JSStringRef CYCopyJSString(JSStringRef value) {
116 return value == NULL ? NULL : JSStringRetain(value);
117 }
118
119 JSStringRef CYCopyJSString(CYUTF8String value) {
120 // XXX: this is very wrong; it needs to convert to UTF16 and then create from there
121 return CYCopyJSString(value.data);
122 }
123
124 JSStringRef CYCopyJSString(JSContextRef context, JSValueRef value) {
125 if (JSValueIsNull(context, value))
126 return NULL;
127 JSValueRef exception(NULL);
128 JSStringRef string(JSValueToStringCopy(context, value, &exception));
129 CYThrow(context, exception);
130 return string;
131 }
132
133 static CYUTF16String CYCastUTF16String(JSStringRef value) {
134 return CYUTF16String(JSStringGetCharactersPtr(value), JSStringGetLength(value));
135 }
136
137 CYUTF8String CYPoolUTF8String(apr_pool_t *pool, JSContextRef context, JSStringRef value) {
138 return CYPoolUTF8String(pool, CYCastUTF16String(value));
139 }
140
141 const char *CYPoolCString(apr_pool_t *pool, JSContextRef context, JSStringRef value) {
142 CYUTF8String utf8(CYPoolUTF8String(pool, context, value));
143 _assert(memchr(utf8.data, '\0', utf8.size) == NULL);
144 return utf8.data;
145 }
146
147 const char *CYPoolCString(apr_pool_t *pool, JSContextRef context, JSValueRef value) {
148 return JSValueIsNull(context, value) ? NULL : CYPoolCString(pool, context, CYJSString(context, value));
149 }
150 /* }}} */
151 /* Index Offsets {{{ */
152 size_t CYGetIndex(apr_pool_t *pool, JSContextRef context, JSStringRef value) {
153 return CYGetIndex(CYPoolUTF8String(pool, context, value));
154 }
155 /* }}} */
156
157 static JSClassRef All_;
158 static JSClassRef Context_;
159 static JSClassRef Functor_;
160 static JSClassRef Global_;
161 static JSClassRef Pointer_;
162 static JSClassRef Struct_;
163
164 JSStringRef Array_s;
165 JSStringRef cy_s;
166 JSStringRef length_s;
167 JSStringRef message_s;
168 JSStringRef name_s;
169 JSStringRef pop_s;
170 JSStringRef prototype_s;
171 JSStringRef push_s;
172 JSStringRef splice_s;
173 JSStringRef toCYON_s;
174 JSStringRef toJSON_s;
175
176 static JSStringRef Result_;
177
178 sqlite3 *Bridge_;
179
180 void CYFinalize(JSObjectRef object) {
181 delete reinterpret_cast<CYData *>(JSObjectGetPrivate(object));
182 }
183
184 struct CStringMapLess :
185 std::binary_function<const char *, const char *, bool>
186 {
187 _finline bool operator ()(const char *lhs, const char *rhs) const {
188 return strcmp(lhs, rhs) < 0;
189 }
190 };
191
192 void Structor_(apr_pool_t *pool, sig::Type *&type) {
193 if (
194 type->primitive == sig::pointer_P &&
195 type->data.data.type != NULL &&
196 type->data.data.type->primitive == sig::struct_P &&
197 strcmp(type->data.data.type->name, "_objc_class") == 0
198 ) {
199 type->primitive = sig::typename_P;
200 type->data.data.type = NULL;
201 return;
202 }
203
204 if (type->primitive != sig::struct_P || type->name == NULL)
205 return;
206
207 sqlite3_stmt *statement;
208
209 _sqlcall(sqlite3_prepare(Bridge_,
210 "select "
211 "\"bridge\".\"mode\", "
212 "\"bridge\".\"value\" "
213 "from \"bridge\" "
214 "where"
215 " \"bridge\".\"mode\" in (3, 4) and"
216 " \"bridge\".\"name\" = ?"
217 " limit 1"
218 , -1, &statement, NULL));
219
220 _sqlcall(sqlite3_bind_text(statement, 1, type->name, -1, SQLITE_STATIC));
221
222 int mode;
223 const char *value;
224
225 if (_sqlcall(sqlite3_step(statement)) == SQLITE_DONE) {
226 mode = -1;
227 value = NULL;
228 } else {
229 mode = sqlite3_column_int(statement, 0);
230 value = sqlite3_column_pooled(pool, statement, 1);
231 }
232
233 _sqlcall(sqlite3_finalize(statement));
234
235 switch (mode) {
236 default:
237 _assert(false);
238 case -1:
239 break;
240
241 case 3: {
242 sig::Parse(pool, &type->data.signature, value, &Structor_);
243 } break;
244
245 case 4: {
246 sig::Signature signature;
247 sig::Parse(pool, &signature, value, &Structor_);
248 type = signature.elements[0].type;
249 } break;
250 }
251 }
252
253 JSClassRef Type_privateData::Class_;
254
255 struct Context :
256 CYData
257 {
258 JSGlobalContextRef context_;
259
260 Context(JSGlobalContextRef context) :
261 context_(context)
262 {
263 }
264 };
265
266 struct Pointer :
267 CYOwned
268 {
269 Type_privateData *type_;
270 size_t length_;
271
272 Pointer(void *value, JSContextRef context, JSObjectRef owner, size_t length, sig::Type *type) :
273 CYOwned(value, context, owner),
274 type_(new(pool_) Type_privateData(type)),
275 length_(length)
276 {
277 }
278 };
279
280 struct Struct_privateData :
281 CYOwned
282 {
283 Type_privateData *type_;
284
285 Struct_privateData(JSContextRef context, JSObjectRef owner) :
286 CYOwned(NULL, context, owner)
287 {
288 }
289 };
290
291 typedef std::map<const char *, Type_privateData *, CStringMapLess> TypeMap;
292 static TypeMap Types_;
293
294 JSObjectRef CYMakeStruct(JSContextRef context, void *data, sig::Type *type, ffi_type *ffi, JSObjectRef owner) {
295 Struct_privateData *internal(new Struct_privateData(context, owner));
296 apr_pool_t *pool(internal->pool_);
297 Type_privateData *typical(new(pool) Type_privateData(type, ffi));
298 internal->type_ = typical;
299
300 if (owner != NULL)
301 internal->value_ = data;
302 else {
303 size_t size(typical->GetFFI()->size);
304 void *copy(apr_palloc(internal->pool_, size));
305 memcpy(copy, data, size);
306 internal->value_ = copy;
307 }
308
309 return JSObjectMake(context, Struct_, internal);
310 }
311
312 JSValueRef CYCastJSValue(JSContextRef context, bool value) {
313 return JSValueMakeBoolean(context, value);
314 }
315
316 JSValueRef CYCastJSValue(JSContextRef context, double value) {
317 return JSValueMakeNumber(context, value);
318 }
319
320 #define CYCastJSValue_(Type_) \
321 JSValueRef CYCastJSValue(JSContextRef context, Type_ value) { \
322 return JSValueMakeNumber(context, static_cast<double>(value)); \
323 }
324
325 CYCastJSValue_(int)
326 CYCastJSValue_(unsigned int)
327 CYCastJSValue_(long int)
328 CYCastJSValue_(long unsigned int)
329 CYCastJSValue_(long long int)
330 CYCastJSValue_(long long unsigned int)
331
332 JSValueRef CYJSUndefined(JSContextRef context) {
333 return JSValueMakeUndefined(context);
334 }
335
336 double CYCastDouble(JSContextRef context, JSValueRef value) {
337 JSValueRef exception(NULL);
338 double number(JSValueToNumber(context, value, &exception));
339 CYThrow(context, exception);
340 return number;
341 }
342
343 bool CYCastBool(JSContextRef context, JSValueRef value) {
344 return JSValueToBoolean(context, value);
345 }
346
347 JSValueRef CYJSNull(JSContextRef context) {
348 return JSValueMakeNull(context);
349 }
350
351 JSValueRef CYCastJSValue(JSContextRef context, JSStringRef value) {
352 return value == NULL ? CYJSNull(context) : JSValueMakeString(context, value);
353 }
354
355 JSValueRef CYCastJSValue(JSContextRef context, const char *value) {
356 return CYCastJSValue(context, CYJSString(value));
357 }
358
359 JSObjectRef CYCastJSObject(JSContextRef context, JSValueRef value) {
360 JSValueRef exception(NULL);
361 JSObjectRef object(JSValueToObject(context, value, &exception));
362 CYThrow(context, exception);
363 return object;
364 }
365
366 JSValueRef CYCallAsFunction(JSContextRef context, JSObjectRef function, JSObjectRef _this, size_t count, const JSValueRef arguments[]) {
367 JSValueRef exception(NULL);
368 JSValueRef value(JSObjectCallAsFunction(context, function, _this, count, arguments, &exception));
369 CYThrow(context, exception);
370 return value;
371 }
372
373 bool CYIsCallable(JSContextRef context, JSValueRef value) {
374 return value != NULL && JSValueIsObject(context, value) && JSObjectIsFunction(context, (JSObjectRef) value);
375 }
376
377 static JSValueRef System_print(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
378 if (count == 0)
379 printf("\n");
380 else {
381 CYPool pool;
382 printf("%s\n", CYPoolCString(pool, context, arguments[0]));
383 }
384
385 return CYJSUndefined(context);
386 } CYCatch }
387
388 static size_t Nonce_(0);
389
390 static JSValueRef $cyq(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
391 CYPool pool;
392 const char *name(apr_psprintf(pool, "%s%"APR_SIZE_T_FMT"", CYPoolCString(pool, context, arguments[0]), Nonce_++));
393 return CYCastJSValue(context, name);
394 }
395
396 static JSValueRef Cycript_gc_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
397 JSGarbageCollect(context);
398 return CYJSUndefined(context);
399 }
400
401 const char *CYPoolCCYON(apr_pool_t *pool, JSContextRef context, JSValueRef value, JSValueRef *exception) { CYTry {
402 switch (JSType type = JSValueGetType(context, value)) {
403 case kJSTypeUndefined:
404 return "undefined";
405 case kJSTypeNull:
406 return "null";
407 case kJSTypeBoolean:
408 return CYCastBool(context, value) ? "true" : "false";
409
410 case kJSTypeNumber: {
411 std::ostringstream str;
412 CYNumerify(str, CYCastDouble(context, value));
413 std::string value(str.str());
414 return apr_pstrmemdup(pool, value.c_str(), value.size());
415 } break;
416
417 case kJSTypeString: {
418 std::ostringstream str;
419 CYUTF8String string(CYPoolUTF8String(pool, context, CYJSString(context, value)));
420 CYStringify(str, string.data, string.size);
421 std::string value(str.str());
422 return apr_pstrmemdup(pool, value.c_str(), value.size());
423 } break;
424
425 case kJSTypeObject:
426 return CYPoolCCYON(pool, context, (JSObjectRef) value);
427 default:
428 throw CYJSError(context, "JSValueGetType() == 0x%x", type);
429 }
430 } CYCatch }
431
432 const char *CYPoolCCYON(apr_pool_t *pool, JSContextRef context, JSValueRef value) {
433 JSValueRef exception(NULL);
434 const char *cyon(CYPoolCCYON(pool, context, value, &exception));
435 CYThrow(context, exception);
436 return cyon;
437 }
438
439 const char *CYPoolCCYON(apr_pool_t *pool, JSContextRef context, JSObjectRef object) {
440 JSValueRef toCYON(CYGetProperty(context, object, toCYON_s));
441 if (CYIsCallable(context, toCYON)) {
442 JSValueRef value(CYCallAsFunction(context, (JSObjectRef) toCYON, object, 0, NULL));
443 _assert(value != NULL);
444 return CYPoolCString(pool, context, value);
445 }
446
447 JSValueRef toJSON(CYGetProperty(context, object, toJSON_s));
448 if (CYIsCallable(context, toJSON)) {
449 JSValueRef arguments[1] = {CYCastJSValue(context, CYJSString(""))};
450 JSValueRef exception(NULL);
451 const char *cyon(CYPoolCCYON(pool, context, CYCallAsFunction(context, (JSObjectRef) toJSON, object, 1, arguments), &exception));
452 CYThrow(context, exception);
453 return cyon;
454 }
455
456 std::ostringstream str;
457
458 str << '{';
459
460 // XXX: this is, sadly, going to leak
461 JSPropertyNameArrayRef names(JSObjectCopyPropertyNames(context, object));
462
463 bool comma(false);
464
465 for (size_t index(0), count(JSPropertyNameArrayGetCount(names)); index != count; ++index) {
466 JSStringRef name(JSPropertyNameArrayGetNameAtIndex(names, index));
467 JSValueRef value(CYGetProperty(context, object, name));
468
469 if (comma)
470 str << ',';
471 else
472 comma = true;
473
474 CYUTF8String string(CYPoolUTF8String(pool, context, name));
475 if (CYIsKey(string))
476 str << string.data;
477 else
478 CYStringify(str, string.data, string.size);
479
480 str << ':' << CYPoolCCYON(pool, context, value);
481 }
482
483 str << '}';
484
485 JSPropertyNameArrayRelease(names);
486
487 std::string string(str.str());
488 return apr_pstrmemdup(pool, string.c_str(), string.size());
489 }
490
491 static JSValueRef Array_callAsFunction_toCYON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
492 CYPool pool;
493 std::ostringstream str;
494
495 str << '[';
496
497 JSValueRef length(CYGetProperty(context, _this, length_s));
498 bool comma(false);
499
500 for (size_t index(0), count(CYCastDouble(context, length)); index != count; ++index) {
501 JSValueRef value(CYGetProperty(context, _this, index));
502
503 if (comma)
504 str << ',';
505 else
506 comma = true;
507
508 if (!JSValueIsUndefined(context, value))
509 str << CYPoolCCYON(pool, context, value);
510 else {
511 str << ',';
512 comma = false;
513 }
514 }
515
516 str << ']';
517
518 std::string value(str.str());
519 return CYCastJSValue(context, CYJSString(CYUTF8String(value.c_str(), value.size())));
520 } CYCatch }
521
522 JSObjectRef CYMakePointer(JSContextRef context, void *pointer, size_t length, sig::Type *type, ffi_type *ffi, JSObjectRef owner) {
523 Pointer *internal(new Pointer(pointer, context, owner, length, type));
524 return JSObjectMake(context, Pointer_, internal);
525 }
526
527 static JSObjectRef CYMakeFunctor(JSContextRef context, void (*function)(), const char *type) {
528 cy::Functor *internal(new cy::Functor(type, function));
529 return JSObjectMake(context, Functor_, internal);
530 }
531
532 static bool CYGetOffset(apr_pool_t *pool, JSContextRef context, JSStringRef value, ssize_t &index) {
533 return CYGetOffset(CYPoolCString(pool, context, value), index);
534 }
535
536 void *CYCastPointer_(JSContextRef context, JSValueRef value) {
537 switch (JSValueGetType(context, value)) {
538 case kJSTypeNull:
539 return NULL;
540 /*case kJSTypeObject:
541 if (JSValueIsObjectOfClass(context, value, Pointer_)) {
542 Pointer *internal(reinterpret_cast<Pointer *>(JSObjectGetPrivate((JSObjectRef) value)));
543 return internal->value_;
544 }*/
545 default:
546 double number(CYCastDouble(context, value));
547 if (std::isnan(number))
548 throw CYJSError(context, "cannot convert value to pointer");
549 return reinterpret_cast<void *>(static_cast<uintptr_t>(static_cast<long long>(number)));
550 }
551 }
552
553 void CYPoolFFI(apr_pool_t *pool, JSContextRef context, sig::Type *type, ffi_type *ffi, void *data, JSValueRef value) {
554 switch (type->primitive) {
555 case sig::boolean_P:
556 *reinterpret_cast<bool *>(data) = JSValueToBoolean(context, value);
557 break;
558
559 #define CYPoolFFI_(primitive, native) \
560 case sig::primitive ## _P: \
561 *reinterpret_cast<native *>(data) = CYCastDouble(context, value); \
562 break;
563
564 CYPoolFFI_(uchar, unsigned char)
565 CYPoolFFI_(char, char)
566 CYPoolFFI_(ushort, unsigned short)
567 CYPoolFFI_(short, short)
568 CYPoolFFI_(ulong, unsigned long)
569 CYPoolFFI_(long, long)
570 CYPoolFFI_(uint, unsigned int)
571 CYPoolFFI_(int, int)
572 CYPoolFFI_(ulonglong, unsigned long long)
573 CYPoolFFI_(longlong, long long)
574 CYPoolFFI_(float, float)
575 CYPoolFFI_(double, double)
576
577 case sig::array_P: {
578 uint8_t *base(reinterpret_cast<uint8_t *>(data));
579 JSObjectRef aggregate(JSValueIsObject(context, value) ? (JSObjectRef) value : NULL);
580 for (size_t index(0); index != type->data.data.size; ++index) {
581 ffi_type *field(ffi->elements[index]);
582
583 JSValueRef rhs;
584 if (aggregate == NULL)
585 rhs = value;
586 else {
587 rhs = CYGetProperty(context, aggregate, index);
588 if (JSValueIsUndefined(context, rhs))
589 throw CYJSError(context, "unable to extract array value");
590 }
591
592 CYPoolFFI(pool, context, type->data.data.type, field, base, rhs);
593 // XXX: alignment?
594 base += field->size;
595 }
596 } break;
597
598 case sig::pointer_P:
599 *reinterpret_cast<void **>(data) = CYCastPointer<void *>(context, value);
600 break;
601
602 case sig::string_P:
603 *reinterpret_cast<const char **>(data) = CYPoolCString(pool, context, value);
604 break;
605
606 case sig::struct_P: {
607 uint8_t *base(reinterpret_cast<uint8_t *>(data));
608 JSObjectRef aggregate(JSValueIsObject(context, value) ? (JSObjectRef) value : NULL);
609 for (size_t index(0); index != type->data.signature.count; ++index) {
610 sig::Element *element(&type->data.signature.elements[index]);
611 ffi_type *field(ffi->elements[index]);
612
613 JSValueRef rhs;
614 if (aggregate == NULL)
615 rhs = value;
616 else {
617 rhs = CYGetProperty(context, aggregate, index);
618 if (JSValueIsUndefined(context, rhs)) {
619 if (element->name != NULL)
620 rhs = CYGetProperty(context, aggregate, CYJSString(element->name));
621 else
622 goto undefined;
623 if (JSValueIsUndefined(context, rhs)) undefined:
624 throw CYJSError(context, "unable to extract structure value");
625 }
626 }
627
628 CYPoolFFI(pool, context, element->type, field, base, rhs);
629 // XXX: alignment?
630 base += field->size;
631 }
632 } break;
633
634 case sig::void_P:
635 break;
636
637 default:
638 if (hooks_ != NULL && hooks_->PoolFFI != NULL)
639 if ((*hooks_->PoolFFI)(pool, context, type, ffi, data, value))
640 return;
641
642 CYThrow("unimplemented signature code: '%c''\n", type->primitive);
643 }
644 }
645
646 JSValueRef CYFromFFI(JSContextRef context, sig::Type *type, ffi_type *ffi, void *data, bool initialize, JSObjectRef owner) {
647 switch (type->primitive) {
648 case sig::boolean_P:
649 return CYCastJSValue(context, *reinterpret_cast<bool *>(data));
650
651 #define CYFromFFI_(primitive, native) \
652 case sig::primitive ## _P: \
653 return CYCastJSValue(context, *reinterpret_cast<native *>(data)); \
654
655 CYFromFFI_(uchar, unsigned char)
656 CYFromFFI_(char, char)
657 CYFromFFI_(ushort, unsigned short)
658 CYFromFFI_(short, short)
659 CYFromFFI_(ulong, unsigned long)
660 CYFromFFI_(long, long)
661 CYFromFFI_(uint, unsigned int)
662 CYFromFFI_(int, int)
663 CYFromFFI_(ulonglong, unsigned long long)
664 CYFromFFI_(longlong, long long)
665 CYFromFFI_(float, float)
666 CYFromFFI_(double, double)
667
668 case sig::array_P:
669 if (void *pointer = data)
670 return CYMakePointer(context, pointer, type->data.data.size, type->data.data.type, NULL, owner);
671 else goto null;
672
673 case sig::pointer_P:
674 if (void *pointer = *reinterpret_cast<void **>(data))
675 return CYMakePointer(context, pointer, _not(size_t), type->data.data.type, NULL, owner);
676 else goto null;
677
678 case sig::string_P:
679 if (char *utf8 = *reinterpret_cast<char **>(data))
680 return CYCastJSValue(context, utf8);
681 else goto null;
682
683 case sig::struct_P:
684 return CYMakeStruct(context, data, type, ffi, owner);
685 case sig::void_P:
686 return CYJSUndefined(context);
687
688 null:
689 return CYJSNull(context);
690 default:
691 if (hooks_ != NULL && hooks_->FromFFI != NULL)
692 if (JSValueRef value = (*hooks_->FromFFI)(context, type, ffi, data, initialize, owner))
693 return value;
694
695 CYThrow("unimplemented signature code: '%c''\n", type->primitive);
696 }
697 }
698
699 static void FunctionClosure_(ffi_cif *cif, void *result, void **arguments, void *arg) {
700 Closure_privateData *internal(reinterpret_cast<Closure_privateData *>(arg));
701
702 JSContextRef context(internal->context_);
703
704 size_t count(internal->cif_.nargs);
705 JSValueRef values[count];
706
707 for (size_t index(0); index != count; ++index)
708 values[index] = CYFromFFI(context, internal->signature_.elements[1 + index].type, internal->cif_.arg_types[index], arguments[index]);
709
710 JSValueRef value(CYCallAsFunction(context, internal->function_, NULL, count, values));
711 CYPoolFFI(NULL, context, internal->signature_.elements[0].type, internal->cif_.rtype, result, value);
712 }
713
714 Closure_privateData *CYMakeFunctor_(JSContextRef context, JSObjectRef function, const char *type, void (*callback)(ffi_cif *, void *, void **, void *)) {
715 // XXX: in case of exceptions this will leak
716 // XXX: in point of fact, this may /need/ to leak :(
717 Closure_privateData *internal(new Closure_privateData(context, function, type));
718
719 ffi_closure *closure((ffi_closure *) _syscall(mmap(
720 NULL, sizeof(ffi_closure),
721 PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
722 -1, 0
723 )));
724
725 ffi_status status(ffi_prep_closure(closure, &internal->cif_, callback, internal));
726 _assert(status == FFI_OK);
727
728 _syscall(mprotect(closure, sizeof(*closure), PROT_READ | PROT_EXEC));
729
730 internal->value_ = closure;
731
732 return internal;
733 }
734
735 static JSObjectRef CYMakeFunctor(JSContextRef context, JSObjectRef function, const char *type) {
736 Closure_privateData *internal(CYMakeFunctor_(context, function, type, &FunctionClosure_));
737 JSObjectRef object(JSObjectMake(context, Functor_, internal));
738 // XXX: see above notes about needing to leak
739 JSValueProtect(CYGetJSContext(context), object);
740 return object;
741 }
742
743 JSObjectRef CYGetCachedObject(JSContextRef context, JSStringRef name) {
744 return CYCastJSObject(context, CYGetProperty(context, CYCastJSObject(context, CYGetProperty(context, CYGetGlobalObject(context), cy_s)), name));
745 }
746
747 static JSObjectRef CYMakeFunctor(JSContextRef context, JSValueRef value, const char *type) {
748 JSObjectRef Function(CYGetCachedObject(context, CYJSString("Function")));
749
750 JSValueRef exception(NULL);
751 bool function(JSValueIsInstanceOfConstructor(context, value, Function, &exception));
752 CYThrow(context, exception);
753
754 if (function) {
755 JSObjectRef function(CYCastJSObject(context, value));
756 return CYMakeFunctor(context, function, type);
757 } else {
758 void (*function)()(CYCastPointer<void (*)()>(context, value));
759 return CYMakeFunctor(context, function, type);
760 }
761 }
762
763 static bool Index_(apr_pool_t *pool, JSContextRef context, Struct_privateData *internal, JSStringRef property, ssize_t &index, uint8_t *&base) {
764 Type_privateData *typical(internal->type_);
765 sig::Type *type(typical->type_);
766 if (type == NULL)
767 return false;
768
769 const char *name(CYPoolCString(pool, context, property));
770 size_t length(strlen(name));
771 double number(CYCastDouble(name, length));
772
773 size_t count(type->data.signature.count);
774
775 if (std::isnan(number)) {
776 if (property == NULL)
777 return false;
778
779 sig::Element *elements(type->data.signature.elements);
780
781 for (size_t local(0); local != count; ++local) {
782 sig::Element *element(&elements[local]);
783 if (element->name != NULL && strcmp(name, element->name) == 0) {
784 index = local;
785 goto base;
786 }
787 }
788
789 return false;
790 } else {
791 index = static_cast<ssize_t>(number);
792 if (index != number || index < 0 || static_cast<size_t>(index) >= count)
793 return false;
794 }
795
796 base:
797 ffi_type **elements(typical->GetFFI()->elements);
798
799 base = reinterpret_cast<uint8_t *>(internal->value_);
800 for (ssize_t local(0); local != index; ++local)
801 base += elements[local]->size;
802
803 return true;
804 }
805
806 static JSValueRef Pointer_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
807 CYPool pool;
808 Pointer *internal(reinterpret_cast<Pointer *>(JSObjectGetPrivate(object)));
809
810 if (JSStringIsEqual(property, length_s))
811 return internal->length_ == _not(size_t) ? CYJSUndefined(context) : CYCastJSValue(context, internal->length_);
812
813 Type_privateData *typical(internal->type_);
814
815 if (typical->type_ == NULL)
816 return NULL;
817
818 ssize_t offset;
819 if (JSStringIsEqualToUTF8CString(property, "$cyi"))
820 offset = 0;
821 else if (!CYGetOffset(pool, context, property, offset))
822 return NULL;
823
824 ffi_type *ffi(typical->GetFFI());
825
826 uint8_t *base(reinterpret_cast<uint8_t *>(internal->value_));
827 base += ffi->size * offset;
828
829 JSObjectRef owner(internal->GetOwner() ?: object);
830 return CYFromFFI(context, typical->type_, ffi, base, false, owner);
831 } CYCatch }
832
833 static bool Pointer_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) { CYTry {
834 CYPool pool;
835 Pointer *internal(reinterpret_cast<Pointer *>(JSObjectGetPrivate(object)));
836 Type_privateData *typical(internal->type_);
837
838 if (typical->type_ == NULL)
839 return NULL;
840
841 ssize_t offset;
842 if (JSStringIsEqualToUTF8CString(property, "$cyi"))
843 offset = 0;
844 else if (!CYGetOffset(pool, context, property, offset))
845 return NULL;
846
847 ffi_type *ffi(typical->GetFFI());
848
849 uint8_t *base(reinterpret_cast<uint8_t *>(internal->value_));
850 base += ffi->size * offset;
851
852 CYPoolFFI(NULL, context, typical->type_, ffi, base, value);
853 return true;
854 } CYCatch }
855
856 static JSValueRef Struct_callAsFunction_$cya(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
857 Struct_privateData *internal(reinterpret_cast<Struct_privateData *>(JSObjectGetPrivate(_this)));
858 Type_privateData *typical(internal->type_);
859 return CYMakePointer(context, internal->value_, _not(size_t), typical->type_, typical->ffi_, _this);
860 }
861
862 static JSValueRef Struct_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
863 CYPool pool;
864 Struct_privateData *internal(reinterpret_cast<Struct_privateData *>(JSObjectGetPrivate(object)));
865 Type_privateData *typical(internal->type_);
866
867 ssize_t index;
868 uint8_t *base;
869
870 if (!Index_(pool, context, internal, property, index, base))
871 return NULL;
872
873 JSObjectRef owner(internal->GetOwner() ?: object);
874
875 return CYFromFFI(context, typical->type_->data.signature.elements[index].type, typical->GetFFI()->elements[index], base, false, owner);
876 } CYCatch }
877
878 static bool Struct_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) { CYTry {
879 CYPool pool;
880 Struct_privateData *internal(reinterpret_cast<Struct_privateData *>(JSObjectGetPrivate(object)));
881 Type_privateData *typical(internal->type_);
882
883 ssize_t index;
884 uint8_t *base;
885
886 if (!Index_(pool, context, internal, property, index, base))
887 return false;
888
889 CYPoolFFI(NULL, context, typical->type_->data.signature.elements[index].type, typical->GetFFI()->elements[index], base, value);
890 return true;
891 } CYCatch }
892
893 static void Struct_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) {
894 Struct_privateData *internal(reinterpret_cast<Struct_privateData *>(JSObjectGetPrivate(object)));
895 Type_privateData *typical(internal->type_);
896 sig::Type *type(typical->type_);
897
898 if (type == NULL)
899 return;
900
901 size_t count(type->data.signature.count);
902 sig::Element *elements(type->data.signature.elements);
903
904 char number[32];
905
906 for (size_t index(0); index != count; ++index) {
907 const char *name;
908 name = elements[index].name;
909
910 if (name == NULL) {
911 sprintf(number, "%zu", index);
912 name = number;
913 }
914
915 JSPropertyNameAccumulatorAddName(names, CYJSString(name));
916 }
917 }
918
919 JSValueRef CYCallFunction(apr_pool_t *pool, JSContextRef context, size_t setups, void *setup[], size_t count, const JSValueRef arguments[], bool initialize, JSValueRef *exception, sig::Signature *signature, ffi_cif *cif, void (*function)()) { CYTry {
920 if (setups + count != signature->count - 1)
921 throw CYJSError(context, "incorrect number of arguments to ffi function");
922
923 size_t size(setups + count);
924 void *values[size];
925 memcpy(values, setup, sizeof(void *) * setups);
926
927 for (size_t index(setups); index != size; ++index) {
928 sig::Element *element(&signature->elements[index + 1]);
929 ffi_type *ffi(cif->arg_types[index]);
930 // XXX: alignment?
931 values[index] = new(pool) uint8_t[ffi->size];
932 CYPoolFFI(pool, context, element->type, ffi, values[index], arguments[index - setups]);
933 }
934
935 uint8_t value[cif->rtype->size];
936
937 if (hooks_ != NULL && hooks_->CallFunction != NULL)
938 (*hooks_->CallFunction)(context, cif, function, value, values);
939 else
940 ffi_call(cif, function, value, values);
941
942 return CYFromFFI(context, signature->elements[0].type, cif->rtype, value, initialize);
943 } CYCatch }
944
945 static JSValueRef Functor_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
946 CYPool pool;
947 cy::Functor *internal(reinterpret_cast<cy::Functor *>(JSObjectGetPrivate(object)));
948 return CYCallFunction(pool, context, 0, NULL, count, arguments, false, exception, &internal->signature_, &internal->cif_, internal->GetValue());
949 }
950
951 static JSObjectRef CYMakeType(JSContextRef context, const char *type) {
952 Type_privateData *internal(new Type_privateData(type));
953 return JSObjectMake(context, Type_privateData::Class_, internal);
954 }
955
956 static JSObjectRef CYMakeType(JSContextRef context, sig::Type *type) {
957 Type_privateData *internal(new Type_privateData(type));
958 return JSObjectMake(context, Type_privateData::Class_, internal);
959 }
960
961 static void *CYCastSymbol(const char *name) {
962 return dlsym(RTLD_DEFAULT, name);
963 }
964
965 static JSValueRef All_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
966 JSObjectRef global(CYGetGlobalObject(context));
967 JSObjectRef cycript(CYCastJSObject(context, CYGetProperty(context, global, CYJSString("Cycript"))));
968 if (JSValueRef value = CYGetProperty(context, cycript, property))
969 if (!JSValueIsUndefined(context, value))
970 return value;
971
972 CYPool pool;
973 CYUTF8String name(CYPoolUTF8String(pool, context, property));
974
975 if (hooks_ != NULL && hooks_->RuntimeProperty != NULL)
976 if (JSValueRef value = (*hooks_->RuntimeProperty)(context, name))
977 return value;
978
979 sqlite3_stmt *statement;
980
981 _sqlcall(sqlite3_prepare(Bridge_,
982 "select "
983 "\"bridge\".\"mode\", "
984 "\"bridge\".\"value\" "
985 "from \"bridge\" "
986 "where"
987 " \"bridge\".\"name\" = ?"
988 " limit 1"
989 , -1, &statement, NULL));
990
991 _sqlcall(sqlite3_bind_text(statement, 1, name.data, name.size, SQLITE_STATIC));
992
993 int mode;
994 const char *value;
995
996 if (_sqlcall(sqlite3_step(statement)) == SQLITE_DONE) {
997 mode = -1;
998 value = NULL;
999 } else {
1000 mode = sqlite3_column_int(statement, 0);
1001 value = sqlite3_column_pooled(pool, statement, 1);
1002 }
1003
1004 _sqlcall(sqlite3_finalize(statement));
1005
1006 switch (mode) {
1007 default:
1008 CYThrow("invalid mode from bridge table: %d", mode);
1009 case -1:
1010 return NULL;
1011
1012 case 0:
1013 return JSEvaluateScript(CYGetJSContext(context), CYJSString(value), NULL, NULL, 0, NULL);
1014
1015 case 1:
1016 if (void (*symbol)() = reinterpret_cast<void (*)()>(CYCastSymbol(name.data)))
1017 return CYMakeFunctor(context, symbol, value);
1018 else return NULL;
1019
1020 case 2:
1021 if (void *symbol = CYCastSymbol(name.data)) {
1022 // XXX: this is horrendously inefficient
1023 sig::Signature signature;
1024 sig::Parse(pool, &signature, value, &Structor_);
1025 ffi_cif cif;
1026 sig::sig_ffi_cif(pool, &sig::ObjectiveC, &signature, &cif);
1027 return CYFromFFI(context, signature.elements[0].type, cif.rtype, symbol);
1028 } else return NULL;
1029
1030 // XXX: implement case 3
1031 case 4:
1032 return CYMakeType(context, value);
1033 }
1034 } CYCatch }
1035
1036 static JSObjectRef Pointer_new(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
1037 if (count != 2)
1038 throw CYJSError(context, "incorrect number of arguments to Functor constructor");
1039
1040 CYPool pool;
1041
1042 void *value(CYCastPointer<void *>(context, arguments[0]));
1043 const char *type(CYPoolCString(pool, context, arguments[1]));
1044
1045 sig::Signature signature;
1046 sig::Parse(pool, &signature, type, &Structor_);
1047
1048 return CYMakePointer(context, value, _not(size_t), signature.elements[0].type, NULL, NULL);
1049 } CYCatch }
1050
1051 static JSObjectRef Type_new(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
1052 if (count != 1)
1053 throw CYJSError(context, "incorrect number of arguments to Type constructor");
1054 CYPool pool;
1055 const char *type(CYPoolCString(pool, context, arguments[0]));
1056 return CYMakeType(context, type);
1057 } CYCatch }
1058
1059 static JSValueRef Type_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
1060 Type_privateData *internal(reinterpret_cast<Type_privateData *>(JSObjectGetPrivate(object)));
1061
1062 sig::Type type;
1063
1064 if (JSStringIsEqualToUTF8CString(property, "$cyi")) {
1065 type.primitive = sig::pointer_P;
1066 type.data.data.size = 0;
1067 } else {
1068 CYPool pool;
1069 size_t index(CYGetIndex(pool, context, property));
1070 if (index == _not(size_t))
1071 return NULL;
1072 type.primitive = sig::array_P;
1073 type.data.data.size = index;
1074 }
1075
1076 type.name = NULL;
1077 type.flags = 0;
1078
1079 type.data.data.type = internal->type_;
1080
1081 return CYMakeType(context, &type);
1082 } CYCatch }
1083
1084 static JSValueRef Type_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
1085 Type_privateData *internal(reinterpret_cast<Type_privateData *>(JSObjectGetPrivate(object)));
1086
1087 if (count != 1)
1088 throw CYJSError(context, "incorrect number of arguments to type cast function");
1089 sig::Type *type(internal->type_);
1090 ffi_type *ffi(internal->GetFFI());
1091 // XXX: alignment?
1092 uint8_t value[ffi->size];
1093 CYPool pool;
1094 CYPoolFFI(pool, context, type, ffi, value, arguments[0]);
1095 return CYFromFFI(context, type, ffi, value);
1096 } CYCatch }
1097
1098 static JSObjectRef Type_callAsConstructor(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
1099 if (count != 0)
1100 throw CYJSError(context, "incorrect number of arguments to type cast function");
1101 Type_privateData *internal(reinterpret_cast<Type_privateData *>(JSObjectGetPrivate(object)));
1102
1103 sig::Type *type(internal->type_);
1104 size_t length;
1105
1106 if (type->primitive != sig::array_P)
1107 length = _not(size_t);
1108 else {
1109 length = type->data.data.size;
1110 type = type->data.data.type;
1111 }
1112
1113 void *value(malloc(internal->GetFFI()->size));
1114 return CYMakePointer(context, value, length, type, NULL, NULL);
1115 } CYCatch }
1116
1117 static JSObjectRef Functor_new(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
1118 if (count != 2)
1119 throw CYJSError(context, "incorrect number of arguments to Functor constructor");
1120 CYPool pool;
1121 const char *type(CYPoolCString(pool, context, arguments[1]));
1122 return CYMakeFunctor(context, arguments[0], type);
1123 } CYCatch }
1124
1125 static JSValueRef CYValue_callAsFunction_valueOf(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
1126 CYValue *internal(reinterpret_cast<CYValue *>(JSObjectGetPrivate(_this)));
1127 return CYCastJSValue(context, reinterpret_cast<uintptr_t>(internal->value_));
1128 } CYCatch }
1129
1130 static JSValueRef CYValue_callAsFunction_toJSON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
1131 return CYValue_callAsFunction_valueOf(context, object, _this, count, arguments, exception);
1132 }
1133
1134 static JSValueRef CYValue_callAsFunction_toCYON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
1135 CYValue *internal(reinterpret_cast<CYValue *>(JSObjectGetPrivate(_this)));
1136 char string[32];
1137 sprintf(string, "%p", internal->value_);
1138 return CYCastJSValue(context, string);
1139 } CYCatch }
1140
1141 static JSValueRef Pointer_callAsFunction_toCYON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
1142 Pointer *internal(reinterpret_cast<Pointer *>(JSObjectGetPrivate(_this)));
1143 if (internal->length_ != _not(size_t)) {
1144 JSObjectRef Array(CYGetCachedObject(context, Array_s));
1145 JSObjectRef toCYON(CYCastJSObject(context, CYGetProperty(context, Array, toCYON_s)));
1146 return CYCallAsFunction(context, toCYON, _this, count, arguments);
1147 } else {
1148 char string[32];
1149 sprintf(string, "%p", internal->value_);
1150 return CYCastJSValue(context, string);
1151 }
1152 } CYCatch }
1153
1154 static JSValueRef Type_callAsFunction_toString(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
1155 Type_privateData *internal(reinterpret_cast<Type_privateData *>(JSObjectGetPrivate(_this)));
1156 CYPool pool;
1157 const char *type(sig::Unparse(pool, internal->type_));
1158 return CYCastJSValue(context, CYJSString(type));
1159 } CYCatch }
1160
1161 static JSValueRef Type_callAsFunction_toCYON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
1162 Type_privateData *internal(reinterpret_cast<Type_privateData *>(JSObjectGetPrivate(_this)));
1163 CYPool pool;
1164 const char *type(sig::Unparse(pool, internal->type_));
1165 size_t size(strlen(type));
1166 char *cyon(new(pool) char[12 + size + 1]);
1167 memcpy(cyon, "new Type(\"", 10);
1168 cyon[12 + size] = '\0';
1169 cyon[12 + size - 2] = '"';
1170 cyon[12 + size - 1] = ')';
1171 memcpy(cyon + 10, type, size);
1172 return CYCastJSValue(context, CYJSString(cyon));
1173 } CYCatch }
1174
1175 static JSValueRef Type_callAsFunction_toJSON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) {
1176 return Type_callAsFunction_toString(context, object, _this, count, arguments, exception);
1177 }
1178
1179 static JSStaticFunction Pointer_staticFunctions[4] = {
1180 {"toCYON", &Pointer_callAsFunction_toCYON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
1181 {"toJSON", &CYValue_callAsFunction_toJSON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
1182 {"valueOf", &CYValue_callAsFunction_valueOf, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
1183 {NULL, NULL, 0}
1184 };
1185
1186 static JSStaticFunction Struct_staticFunctions[2] = {
1187 {"$cya", &Struct_callAsFunction_$cya, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
1188 {NULL, NULL, 0}
1189 };
1190
1191 static JSStaticFunction Functor_staticFunctions[4] = {
1192 {"toCYON", &CYValue_callAsFunction_toCYON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
1193 {"toJSON", &CYValue_callAsFunction_toJSON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
1194 {"valueOf", &CYValue_callAsFunction_valueOf, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
1195 {NULL, NULL, 0}
1196 };
1197
1198 namespace cy {
1199 JSStaticFunction const * const Functor::StaticFunctions = Functor_staticFunctions;
1200 }
1201
1202 static JSStaticFunction Type_staticFunctions[4] = {
1203 {"toCYON", &Type_callAsFunction_toCYON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
1204 {"toJSON", &Type_callAsFunction_toJSON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
1205 {"toString", &Type_callAsFunction_toString, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
1206 {NULL, NULL, 0}
1207 };
1208
1209 static JSObjectRef (*JSObjectMakeArray$)(JSContextRef, size_t, const JSValueRef[], JSValueRef *);
1210
1211 void CYSetArgs(int argc, const char *argv[]) {
1212 JSContextRef context(CYGetJSContext());
1213 JSValueRef args[argc];
1214 for (int i(0); i != argc; ++i)
1215 args[i] = CYCastJSValue(context, argv[i]);
1216
1217 JSObjectRef array;
1218 if (JSObjectMakeArray$ != NULL) {
1219 JSValueRef exception(NULL);
1220 array = (*JSObjectMakeArray$)(context, argc, args, &exception);
1221 CYThrow(context, exception);
1222 } else {
1223 JSObjectRef Array(CYGetCachedObject(context, CYJSString("Array")));
1224 JSValueRef value(CYCallAsFunction(context, Array, NULL, argc, args));
1225 array = CYCastJSObject(context, value);
1226 }
1227
1228 JSObjectRef System(CYGetCachedObject(context, CYJSString("System")));
1229 CYSetProperty(context, System, CYJSString("args"), array);
1230 }
1231
1232 JSObjectRef CYGetGlobalObject(JSContextRef context) {
1233 return JSContextGetGlobalObject(context);
1234 }
1235
1236 const char *CYExecute(apr_pool_t *pool, const char *code) {
1237 JSContextRef context(CYGetJSContext());
1238 JSValueRef exception(NULL), result;
1239
1240 void *handle;
1241 if (hooks_ != NULL && hooks_->ExecuteStart != NULL)
1242 handle = (*hooks_->ExecuteStart)(context);
1243 else
1244 handle = NULL;
1245
1246 const char *json;
1247
1248 try {
1249 result = JSEvaluateScript(context, CYJSString(code), NULL, NULL, 0, &exception);
1250 } catch (const char *error) {
1251 return error;
1252 }
1253
1254 if (exception != NULL) { error:
1255 result = exception;
1256 exception = NULL;
1257 }
1258
1259 if (JSValueIsUndefined(context, result))
1260 return NULL;
1261
1262 try {
1263 json = CYPoolCCYON(pool, context, result, &exception);
1264 } catch (const char *error) {
1265 return error;
1266 }
1267
1268 if (exception != NULL)
1269 goto error;
1270
1271 CYSetProperty(context, CYGetGlobalObject(context), Result_, result);
1272
1273 if (hooks_ != NULL && hooks_->ExecuteEnd != NULL)
1274 (*hooks_->ExecuteEnd)(context, handle);
1275 return json;
1276 }
1277
1278 extern "C" void CydgetSetupContext(JSGlobalContextRef context) {
1279 CYSetupContext(context);
1280 }
1281
1282 static bool initialized_ = false;
1283
1284 void CYInitializeDynamic() {
1285 if (!initialized_)
1286 initialized_ = true;
1287 else return;
1288
1289 CYInitializeStatic();
1290
1291 _sqlcall(sqlite3_open("/usr/lib/libcycript.db", &Bridge_));
1292
1293 JSObjectMakeArray$ = reinterpret_cast<JSObjectRef (*)(JSContextRef, size_t, const JSValueRef[], JSValueRef *)>(dlsym(RTLD_DEFAULT, "JSObjectMakeArray"));
1294
1295 JSClassDefinition definition;
1296
1297 definition = kJSClassDefinitionEmpty;
1298 definition.className = "All";
1299 definition.getProperty = &All_getProperty;
1300 All_ = JSClassCreate(&definition);
1301
1302 definition = kJSClassDefinitionEmpty;
1303 definition.className = "Context";
1304 definition.finalize = &CYFinalize;
1305 Context_ = JSClassCreate(&definition);
1306
1307 definition = kJSClassDefinitionEmpty;
1308 definition.className = "Functor";
1309 definition.staticFunctions = cy::Functor::StaticFunctions;
1310 definition.callAsFunction = &Functor_callAsFunction;
1311 definition.finalize = &CYFinalize;
1312 Functor_ = JSClassCreate(&definition);
1313
1314 definition = kJSClassDefinitionEmpty;
1315 definition.className = "Pointer";
1316 definition.staticFunctions = Pointer_staticFunctions;
1317 definition.getProperty = &Pointer_getProperty;
1318 definition.setProperty = &Pointer_setProperty;
1319 definition.finalize = &CYFinalize;
1320 Pointer_ = JSClassCreate(&definition);
1321
1322 definition = kJSClassDefinitionEmpty;
1323 definition.className = "Struct";
1324 definition.staticFunctions = Struct_staticFunctions;
1325 definition.getProperty = &Struct_getProperty;
1326 definition.setProperty = &Struct_setProperty;
1327 definition.getPropertyNames = &Struct_getPropertyNames;
1328 definition.finalize = &CYFinalize;
1329 Struct_ = JSClassCreate(&definition);
1330
1331 definition = kJSClassDefinitionEmpty;
1332 definition.className = "Type";
1333 definition.staticFunctions = Type_staticFunctions;
1334 definition.getProperty = &Type_getProperty;
1335 definition.callAsFunction = &Type_callAsFunction;
1336 definition.callAsConstructor = &Type_callAsConstructor;
1337 definition.finalize = &CYFinalize;
1338 Type_privateData::Class_ = JSClassCreate(&definition);
1339
1340 definition = kJSClassDefinitionEmpty;
1341 //definition.getProperty = &Global_getProperty;
1342 Global_ = JSClassCreate(&definition);
1343
1344 Array_s = JSStringCreateWithUTF8CString("Array");
1345 cy_s = JSStringCreateWithUTF8CString("$cy");
1346 length_s = JSStringCreateWithUTF8CString("length");
1347 message_s = JSStringCreateWithUTF8CString("message");
1348 name_s = JSStringCreateWithUTF8CString("name");
1349 pop_s = JSStringCreateWithUTF8CString("pop");
1350 prototype_s = JSStringCreateWithUTF8CString("prototype");
1351 push_s = JSStringCreateWithUTF8CString("push");
1352 splice_s = JSStringCreateWithUTF8CString("splice");
1353 toCYON_s = JSStringCreateWithUTF8CString("toCYON");
1354 toJSON_s = JSStringCreateWithUTF8CString("toJSON");
1355
1356 Result_ = JSStringCreateWithUTF8CString("_");
1357
1358 if (hooks_ != NULL && hooks_->Initialize != NULL)
1359 (*hooks_->Initialize)();
1360 }
1361
1362 void CYThrow(JSContextRef context, JSValueRef value) {
1363 if (value != NULL)
1364 throw CYJSError(context, value);
1365 }
1366
1367 const char *CYJSError::PoolCString(apr_pool_t *pool) const {
1368 // XXX: this used to be CYPoolCString
1369 return CYPoolCCYON(pool, context_, value_);
1370 }
1371
1372 JSValueRef CYJSError::CastJSValue(JSContextRef context) const {
1373 // XXX: what if the context is different?
1374 return value_;
1375 }
1376
1377 JSValueRef CYCastJSError(JSContextRef context, const char *message) {
1378 JSObjectRef Error(CYGetCachedObject(context, CYJSString("Error")));
1379
1380 JSValueRef arguments[1] = {CYCastJSValue(context, message)};
1381
1382 JSValueRef exception(NULL);
1383 JSValueRef value(JSObjectCallAsConstructor(context, Error, 1, arguments, &exception));
1384 CYThrow(context, exception);
1385
1386 return value;
1387 }
1388
1389 JSValueRef CYPoolError::CastJSValue(JSContextRef context) const {
1390 return CYCastJSError(context, message_);
1391 }
1392
1393 CYJSError::CYJSError(JSContextRef context, const char *format, ...) {
1394 _assert(context != NULL);
1395
1396 CYPool pool;
1397
1398 va_list args;
1399 va_start(args, format);
1400 const char *message(apr_pvsprintf(pool, format, args));
1401 va_end(args);
1402
1403 value_ = CYCastJSError(context, message);
1404 }
1405
1406 JSGlobalContextRef CYGetJSContext(JSContextRef context) {
1407 return reinterpret_cast<Context *>(JSObjectGetPrivate(CYCastJSObject(context, CYGetProperty(context, CYGetGlobalObject(context), cy_s))))->context_;
1408 }
1409
1410 extern "C" void CYSetupContext(JSGlobalContextRef context) {
1411 CYInitializeDynamic();
1412
1413 JSObjectRef global(CYGetGlobalObject(context));
1414
1415 JSObjectRef cy(JSObjectMake(context, Context_, new Context(context)));
1416 CYSetProperty(context, global, cy_s, cy, kJSPropertyAttributeDontEnum);
1417
1418 /* Cache Globals {{{ */
1419 JSObjectRef Array(CYCastJSObject(context, CYGetProperty(context, global, CYJSString("Array"))));
1420 CYSetProperty(context, cy, CYJSString("Array"), Array);
1421
1422 JSObjectRef Array_prototype(CYCastJSObject(context, CYGetProperty(context, Array, prototype_s)));
1423 CYSetProperty(context, cy, CYJSString("Array_prototype"), Array_prototype);
1424
1425 JSObjectRef Error(CYCastJSObject(context, CYGetProperty(context, global, CYJSString("Error"))));
1426 CYSetProperty(context, cy, CYJSString("Error"), Error);
1427
1428 JSObjectRef Function(CYCastJSObject(context, CYGetProperty(context, global, CYJSString("Function"))));
1429 CYSetProperty(context, cy, CYJSString("Function"), Function);
1430
1431 JSObjectRef Function_prototype(CYCastJSObject(context, CYGetProperty(context, Function, prototype_s)));
1432 CYSetProperty(context, cy, CYJSString("Function_prototype"), Function_prototype);
1433
1434 JSObjectRef Object(CYCastJSObject(context, CYGetProperty(context, global, CYJSString("Object"))));
1435 CYSetProperty(context, cy, CYJSString("Object"), Object);
1436
1437 JSObjectRef Object_prototype(CYCastJSObject(context, CYGetProperty(context, Object, prototype_s)));
1438 CYSetProperty(context, cy, CYJSString("Object_prototype"), Object_prototype);
1439
1440 JSObjectRef String(CYCastJSObject(context, CYGetProperty(context, global, CYJSString("String"))));
1441 CYSetProperty(context, cy, CYJSString("String"), String);
1442 /* }}} */
1443
1444 CYSetProperty(context, Array_prototype, toCYON_s, &Array_callAsFunction_toCYON, kJSPropertyAttributeDontEnum);
1445
1446 JSObjectRef cycript(JSObjectMake(context, NULL, NULL));
1447 CYSetProperty(context, global, CYJSString("Cycript"), cycript);
1448 CYSetProperty(context, cycript, CYJSString("gc"), &Cycript_gc_callAsFunction);
1449
1450 JSObjectRef Functor(JSObjectMakeConstructor(context, Functor_, &Functor_new));
1451 JSObjectSetPrototype(context, CYCastJSObject(context, CYGetProperty(context, Functor, prototype_s)), Function_prototype);
1452 CYSetProperty(context, cycript, CYJSString("Functor"), Functor);
1453
1454 CYSetProperty(context, cycript, CYJSString("Pointer"), JSObjectMakeConstructor(context, Pointer_, &Pointer_new));
1455 CYSetProperty(context, cycript, CYJSString("Type"), JSObjectMakeConstructor(context, Type_privateData::Class_, &Type_new));
1456
1457 JSObjectRef all(JSObjectMake(context, All_, NULL));
1458 CYSetProperty(context, cycript, CYJSString("all"), all);
1459
1460 JSObjectRef last(NULL), curr(global);
1461
1462 goto next; for (JSValueRef next;;) {
1463 if (JSValueIsNull(context, next))
1464 break;
1465 last = curr;
1466 curr = CYCastJSObject(context, next);
1467 next:
1468 next = JSObjectGetPrototype(context, curr);
1469 }
1470
1471 JSObjectSetPrototype(context, last, all);
1472
1473 CYSetProperty(context, global, CYJSString("$cyq"), &$cyq);
1474
1475 JSObjectRef System(JSObjectMake(context, NULL, NULL));
1476 CYSetProperty(context, cy, CYJSString("System"), Function);
1477
1478 CYSetProperty(context, global, CYJSString("system"), System);
1479 CYSetProperty(context, System, CYJSString("args"), CYJSNull(context));
1480 //CYSetProperty(context, System, CYJSString("global"), global);
1481 CYSetProperty(context, System, CYJSString("print"), &System_print);
1482
1483 if (hooks_ != NULL && hooks_->SetupContext != NULL)
1484 (*hooks_->SetupContext)(context);
1485 }
1486
1487 JSGlobalContextRef CYGetJSContext() {
1488 CYInitializeDynamic();
1489
1490 static JSGlobalContextRef context_;
1491
1492 if (context_ == NULL) {
1493 context_ = JSGlobalContextCreate(Global_);
1494 CYSetupContext(context_);
1495 }
1496
1497 return context_;
1498 }