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