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