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