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