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