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