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