X-Git-Url: https://git.saurik.com/cycript.git/blobdiff_plain/c2e810225149d40d9545905c649fd14ac978da4d..18d654b39fecc7d3efd6e1f1828a38f8d3b08886:/Execute.cpp diff --git a/Execute.cpp b/Execute.cpp index 11fada2..1a3a2df 100644 --- a/Execute.cpp +++ b/Execute.cpp @@ -1,5 +1,5 @@ /* Cycript - Optimizing JavaScript Compiler/Runtime - * Copyright (C) 2009-2014 Jay Freeman (saurik) + * Copyright (C) 2009-2015 Jay Freeman (saurik) */ /* GNU Affero General Public License, Version 3 {{{ */ @@ -19,35 +19,33 @@ **/ /* }}} */ -#include "Internal.hpp" +#include "cycript.hpp" + +#include +#include +#include +#include +#include +#include #include #include #include #include -#include "cycript.hpp" - -#include "sig/parse.hpp" -#include "sig/ffi_type.hpp" - -#include "Pooling.hpp" -#include "Execute.hpp" - #include #include -#include -#include -#include -#include -#include -#include +#include "sig/parse.hpp" +#include "sig/ffi_type.hpp" #include "Code.hpp" #include "Decode.hpp" #include "Error.hpp" +#include "Execute.hpp" +#include "Internal.hpp" #include "JavaScript.hpp" +#include "Pooling.hpp" #include "String.hpp" static std::vector &GetHooks() { @@ -86,7 +84,7 @@ void CYSetProperty(JSContextRef context, JSObjectRef object, JSStringRef name, J void CYSetPrototype(JSContextRef context, JSObjectRef object, JSValueRef value) { JSObjectSetPrototype(context, object, value); - _assert(JSObjectGetPrototype(context, object) == value); + _assert(CYIsStrictEqual(context, JSObjectGetPrototype(context, object), value)); } /* }}} */ /* JavaScript Strings {{{ */ @@ -99,8 +97,19 @@ JSStringRef CYCopyJSString(JSStringRef value) { } JSStringRef CYCopyJSString(CYUTF8String value) { - // XXX: this is very wrong; it needs to convert to UTF16 and then create from there - return CYCopyJSString(value.data); + if (memchr(value.data, '\0', value.size) != NULL) { + CYPool pool; + return CYCopyJSString(pool.memdup(value.data, value.size)); + } else if (value.data[value.size] != '\0') { + CYPool pool; + return CYCopyJSString(CYPoolUTF16String(pool, value)); + } else { + return CYCopyJSString(value.data); + } +} + +JSStringRef CYCopyJSString(CYUTF16String value) { + return JSStringCreateWithCharacters(value.data, value.size); } JSStringRef CYCopyJSString(JSContextRef context, JSValueRef value) { @@ -135,6 +144,7 @@ size_t CYGetIndex(CYPool &pool, JSContextRef context, JSStringRef value) { static JSClassRef All_; static JSClassRef Context_; +static JSClassRef CString_; JSClassRef Functor_; static JSClassRef Global_; static JSClassRef Pointer_; @@ -154,6 +164,7 @@ JSStringRef toCYON_s; JSStringRef toJSON_s; JSStringRef toPointer_s; JSStringRef toString_s; +JSStringRef weak_s; static JSStringRef Result_; @@ -216,6 +227,15 @@ struct Context : } }; +struct CString : + CYOwned +{ + CString(char *value, JSContextRef context, JSObjectRef owner) : + CYOwned(value, context, owner) + { + } +}; + struct Pointer : CYOwned { @@ -241,9 +261,6 @@ struct Struct_privateData : } }; -typedef std::map TypeMap; -static TypeMap Types_; - JSObjectRef CYMakeStruct(JSContextRef context, void *data, sig::Type *type, ffi_type *ffi, JSObjectRef owner) { Struct_privateData *internal(new Struct_privateData(context, owner)); CYPool &pool(*internal->pool_); @@ -263,6 +280,10 @@ JSObjectRef CYMakeStruct(JSContextRef context, void *data, sig::Type *type, ffi_ } static void *CYCastSymbol(const char *name) { + for (CYHook *hook : GetHooks()) + if (hook->CastSymbol != NULL) + if (void *value = (*hook->CastSymbol)(name)) + return value; return dlsym(RTLD_DEFAULT, name); } @@ -276,6 +297,7 @@ JSValueRef CYCastJSValue(JSContextRef context, double value) { #define CYCastJSValue_(Type_) \ JSValueRef CYCastJSValue(JSContextRef context, Type_ value) { \ + _assert(static_cast(static_cast(value)) == value); \ return JSValueMakeNumber(context, static_cast(value)); \ } @@ -322,6 +344,14 @@ bool CYIsCallable(JSContextRef context, JSValueRef value) { return value != NULL && JSValueIsObject(context, value) && JSObjectIsFunction(context, (JSObjectRef) value); } +bool CYIsEqual(JSContextRef context, JSValueRef lhs, JSValueRef rhs) { + return _jsccall(JSValueIsEqual, context, lhs, rhs); +} + +bool CYIsStrictEqual(JSContextRef context, JSValueRef lhs, JSValueRef rhs) { + return JSValueIsStrictEqual(context, lhs, rhs); +} + size_t CYArrayLength(JSContextRef context, JSObjectRef array) { return CYCastDouble(context, CYGetProperty(context, array, length_s)); } @@ -338,30 +368,34 @@ void CYArrayPush(JSContextRef context, JSObjectRef array, JSValueRef value) { } static JSValueRef System_print(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { + FILE *file(stdout); + if (count == 0) - printf("\n"); + fputc('\n', file); else { CYPool pool; - printf("%s\n", CYPoolCString(pool, context, arguments[0])); + CYUTF8String string(CYPoolUTF8String(pool, context, CYJSString(context, arguments[0]))); + fwrite(string.data, string.size, 1, file); } + fflush(file); return CYJSUndefined(context); } CYCatch(NULL) } -static size_t Nonce_(0); - -static JSValueRef $cyq(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { - CYPool pool; - const char *name(pool.strcat(CYPoolCString(pool, context, arguments[0]), pool.itoa(Nonce_++), NULL)); - return CYCastJSValue(context, name); -} CYCatch(NULL) } - static void (*JSSynchronousGarbageCollectForDebugging$)(JSContextRef); -void CYGarbageCollect(JSContextRef context) { +_visible void CYGarbageCollect(JSContextRef context) { (JSSynchronousGarbageCollectForDebugging$ ?: &JSGarbageCollect)(context); } +static JSValueRef Cycript_compile_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { + CYPool pool; + CYUTF8String before(CYPoolUTF8String(pool, context, CYJSString(context, arguments[0]))); + std::stringbuf value(std::string(before.data, before.size)); + CYUTF8String after(CYPoolCode(pool, value)); + return CYCastJSValue(context, CYJSString(after)); +} CYCatch_(NULL, "SyntaxError") } + static JSValueRef Cycript_gc_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { CYGarbageCollect(context); return CYJSUndefined(context); @@ -415,7 +449,7 @@ const char *CYPoolCCYON(CYPool &pool, JSContextRef context, JSObjectRef object, JSValueRef toCYON(CYGetProperty(context, object, toCYON_s)); if (CYIsCallable(context, toCYON)) { // XXX: this needs to be abstracted behind some kind of function - JSValueRef arguments[1] = {CYCastJSValue(context, static_cast(reinterpret_cast(&objects)))}; + JSValueRef arguments[1] = {CYCastJSValue(context, reinterpret_cast(&objects))}; JSValueRef value(CYCallAsFunction(context, (JSObjectRef) toCYON, object, 1, arguments)); _assert(value != NULL); return CYPoolCString(pool, context, value); @@ -542,6 +576,11 @@ JSObjectRef CYMakePointer(JSContextRef context, void *pointer, size_t length, si return JSObjectMake(context, Pointer_, internal); } +JSObjectRef CYMakeCString(JSContextRef context, char *pointer, JSObjectRef owner) { + CString *internal(new CString(pointer, context, owner)); + return JSObjectMake(context, CString_, internal); +} + static JSObjectRef CYMakeFunctor(JSContextRef context, void (*function)(), const sig::Signature &signature) { return JSObjectMake(context, Functor_, new cy::Functor(signature, function)); } @@ -567,8 +606,10 @@ static bool CYGetOffset(CYPool &pool, JSContextRef context, JSStringRef value, s return CYGetOffset(CYPoolCString(pool, context, value), index); } -void *CYCastPointer_(JSContextRef context, JSValueRef value) { - switch (JSValueGetType(context, value)) { +void *CYCastPointer_(JSContextRef context, JSValueRef value, bool *guess) { + if (value == NULL) + return NULL; + else switch (JSValueGetType(context, value)) { case kJSTypeNull: return NULL; case kJSTypeObject: { @@ -581,13 +622,21 @@ void *CYCastPointer_(JSContextRef context, JSValueRef value) { if (CYIsCallable(context, toPointer)) { JSValueRef value(CYCallAsFunction(context, (JSObjectRef) toPointer, object, 0, NULL)); _assert(value != NULL); - return CYCastPointer_(context, value); + return CYCastPointer_(context, value, guess); } } default: + if (guess != NULL) + *guess = true; + case kJSTypeNumber: double number(CYCastDouble(context, value)); - if (std::isnan(number)) + if (!std::isnan(number)) + return reinterpret_cast(static_cast(static_cast(number))); + if (guess == NULL) throw CYJSError(context, "cannot convert value to pointer"); - return reinterpret_cast(static_cast(static_cast(number))); + else { + *guess = true; + return NULL; + } } } @@ -640,10 +689,12 @@ void CYPoolFFI(CYPool *pool, JSContextRef context, sig::Type *type, ffi_type *ff *reinterpret_cast(data) = CYCastPointer(context, value); break; - case sig::string_P: - _assert(pool != NULL); - *reinterpret_cast(data) = CYPoolCString(*pool, context, value); - break; + case sig::string_P: { + bool guess(false); + *reinterpret_cast(data) = CYCastPointer(context, value, &guess); + if (guess && pool != NULL) + *reinterpret_cast(data) = CYPoolCString(*pool, context, value); + } break; case sig::struct_P: { uint8_t *base(reinterpret_cast(data)); @@ -719,8 +770,8 @@ JSValueRef CYFromFFI(JSContextRef context, sig::Type *type, ffi_type *ffi, void else goto null; case sig::string_P: - if (char *utf8 = *reinterpret_cast(data)) - return CYCastJSValue(context, utf8); + if (char *pointer = *reinterpret_cast(data)) + return CYMakeCString(context, pointer, owner); else goto null; case sig::struct_P: @@ -740,7 +791,7 @@ JSValueRef CYFromFFI(JSContextRef context, sig::Type *type, ffi_type *ffi, void } } -void CYExecuteClosure(ffi_cif *cif, void *result, void **arguments, void *arg, JSValueRef (*adapter)(JSContextRef, size_t, JSValueRef[], JSObjectRef)) { +void CYExecuteClosure(ffi_cif *cif, void *result, void **arguments, void *arg) { Closure_privateData *internal(reinterpret_cast(arg)); JSContextRef context(internal->context_); @@ -751,7 +802,7 @@ void CYExecuteClosure(ffi_cif *cif, void *result, void **arguments, void *arg, J for (size_t index(0); index != count; ++index) values[index] = CYFromFFI(context, internal->signature_.elements[1 + index].type, internal->cif_.arg_types[index], arguments[index]); - JSValueRef value(adapter(context, count, values, internal->function_)); + JSValueRef value(internal->adapter_(context, count, values, internal->function_)); CYPoolFFI(NULL, context, internal->signature_.elements[0].type, internal->cif_.rtype, result, value); } @@ -759,20 +810,16 @@ static JSValueRef FunctionAdapter_(JSContextRef context, size_t count, JSValueRe return CYCallAsFunction(context, function, NULL, count, values); } -static void FunctionClosure_(ffi_cif *cif, void *result, void **arguments, void *arg) { - CYExecuteClosure(cif, result, arguments, arg, &FunctionAdapter_); -} - -Closure_privateData *CYMakeFunctor_(JSContextRef context, JSObjectRef function, const sig::Signature &signature, void (*callback)(ffi_cif *, void *, void **, void *)) { +Closure_privateData *CYMakeFunctor_(JSContextRef context, JSObjectRef function, const sig::Signature &signature, JSValueRef (*adapter)(JSContextRef, size_t, JSValueRef[], JSObjectRef)) { // XXX: in case of exceptions this will leak // XXX: in point of fact, this may /need/ to leak :( - Closure_privateData *internal(new Closure_privateData(context, function, signature)); + Closure_privateData *internal(new Closure_privateData(context, function, adapter, signature)); #if defined(__APPLE__) && (defined(__arm__) || defined(__arm64__)) void *executable; ffi_closure *writable(reinterpret_cast(ffi_closure_alloc(sizeof(ffi_closure), &executable))); - ffi_status status(ffi_prep_closure_loc(writable, &internal->cif_, callback, internal, executable)); + ffi_status status(ffi_prep_closure_loc(writable, &internal->cif_, &CYExecuteClosure, internal, executable)); _assert(status == FFI_OK); internal->value_ = executable; @@ -783,7 +830,7 @@ Closure_privateData *CYMakeFunctor_(JSContextRef context, JSObjectRef function, -1, 0 ))); - ffi_status status(ffi_prep_closure(closure, &internal->cif_, callback, internal)); + ffi_status status(ffi_prep_closure(closure, &internal->cif_, &CYExecuteClosure, internal)); _assert(status == FFI_OK); _syscall(mprotect(closure, sizeof(*closure), PROT_READ | PROT_EXEC)); @@ -795,15 +842,19 @@ Closure_privateData *CYMakeFunctor_(JSContextRef context, JSObjectRef function, } static JSObjectRef CYMakeFunctor(JSContextRef context, JSObjectRef function, const sig::Signature &signature) { - Closure_privateData *internal(CYMakeFunctor_(context, function, signature, &FunctionClosure_)); + Closure_privateData *internal(CYMakeFunctor_(context, function, signature, &FunctionAdapter_)); JSObjectRef object(JSObjectMake(context, Functor_, internal)); // XXX: see above notes about needing to leak JSValueProtect(CYGetJSContext(context), object); return object; } +JSValueRef CYGetCachedValue(JSContextRef context, JSStringRef name) { + return CYGetProperty(context, CYCastJSObject(context, CYGetProperty(context, CYGetGlobalObject(context), cy_s)), name); +} + JSObjectRef CYGetCachedObject(JSContextRef context, JSStringRef name) { - return CYCastJSObject(context, CYGetProperty(context, CYCastJSObject(context, CYGetProperty(context, CYGetGlobalObject(context), cy_s)), name)); + return CYCastJSObject(context, CYGetCachedValue(context, name)); } static JSObjectRef CYMakeFunctor(JSContextRef context, JSValueRef value, const sig::Signature &signature) { @@ -819,6 +870,32 @@ static JSObjectRef CYMakeFunctor(JSContextRef context, JSValueRef value, const s } } +static JSValueRef CString_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry { + CYPool pool; + CString *internal(reinterpret_cast(JSObjectGetPrivate(object))); + char *string(static_cast(internal->value_)); + + ssize_t offset; + if (!CYGetOffset(pool, context, property, offset)) + return NULL; + + return CYCastJSValue(context, CYJSString(CYUTF8String(&string[offset], 1))); +} CYCatch(NULL) } + +static bool CString_setProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef value, JSValueRef *exception) { CYTry { + CYPool pool; + CString *internal(reinterpret_cast(JSObjectGetPrivate(object))); + char *string(static_cast(internal->value_)); + + ssize_t offset; + if (!CYGetOffset(pool, context, property, offset)) + return false; + + const char *data(CYPoolCString(pool, context, value)); + string[offset] = *data; + return true; +} CYCatch(false) } + static bool Index_(CYPool &pool, JSContextRef context, Struct_privateData *internal, JSStringRef property, ssize_t &index, uint8_t *&base) { Type_privateData *typical(internal->type_); sig::Type *type(typical->type_); @@ -978,6 +1055,10 @@ static void Struct_getPropertyNames(JSContextRef context, JSObjectRef object, JS } } +void CYCallFunction(CYPool &pool, JSContextRef context, ffi_cif *cif, void (*function)(), void *value, void **values) { + ffi_call(cif, function, value, values); +} + JSValueRef CYCallFunction(CYPool &pool, JSContextRef context, size_t setups, void *setup[], size_t count, const JSValueRef arguments[], bool initialize, sig::Signature *signature, ffi_cif *cif, void (*function)()) { if (setups + count != signature->count - 1) throw CYJSError(context, "incorrect number of arguments to ffi function"); @@ -996,15 +1077,13 @@ JSValueRef CYCallFunction(CYPool &pool, JSContextRef context, size_t setups, voi uint8_t value[cif->rtype->size]; + void (*call)(CYPool &, JSContextRef, ffi_cif *, void (*)(), void *, void **) = &CYCallFunction; + // XXX: this only supports one hook, but it is a bad idea anyway for (CYHook *hook : GetHooks()) - if (hook->CallFunction != NULL) { - // XXX: this only supports one hook, but it is a bad idea anyway - (*hook->CallFunction)(context, cif, function, value, values); - goto from; - } - ffi_call(cif, function, value, values); + if (hook->CallFunction != NULL) + call = hook->CallFunction; - from: + call(pool, context, cif, function, value, values); return CYFromFFI(context, signature->elements[0].type, cif->rtype, value, initialize); } @@ -1128,6 +1207,13 @@ static void All_getPropertyNames(JSContextRef context, JSObjectRef object, JSPro } } +static JSObjectRef CString_new(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { + if (count != 1) + throw CYJSError(context, "incorrect number of arguments to CString constructor"); + char *value(CYCastPointer(context, arguments[0])); + return CYMakeCString(context, value, NULL); +} CYCatch(NULL) } + static JSObjectRef Pointer_new(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { if (count != 2) throw CYJSError(context, "incorrect number of arguments to Pointer constructor"); @@ -1144,11 +1230,46 @@ static JSObjectRef Pointer_new(JSContextRef context, JSObjectRef object, size_t } CYCatch(NULL) } static JSObjectRef Type_new(JSContextRef context, JSObjectRef object, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { - if (count != 1) - throw CYJSError(context, "incorrect number of arguments to Type constructor"); CYPool pool; - const char *type(CYPoolCString(pool, context, arguments[0])); - return CYMakeType(context, type); + + if (false) { + } else if (count == 1) { + const char *type(CYPoolCString(pool, context, arguments[0])); + return CYMakeType(context, type); + } else if (count == 2) { + JSObjectRef types(CYCastJSObject(context, arguments[0])); + size_t count(CYArrayLength(context, types)); + + JSObjectRef names(CYCastJSObject(context, arguments[1])); + + sig::Type type; + type.name = NULL; + type.flags = 0; + + type.primitive = sig::struct_P; + type.data.signature.elements = new(pool) sig::Element[count]; + type.data.signature.count = count; + + for (size_t i(0); i != count; ++i) { + sig::Element &element(type.data.signature.elements[i]); + element.offset = _not(size_t); + + JSValueRef name(CYArrayGet(context, names, i)); + if (JSValueIsUndefined(context, name)) + element.name = NULL; + else + element.name = CYPoolCString(pool, context, name); + + JSObjectRef object(CYCastJSObject(context, CYArrayGet(context, types, i))); + _assert(JSValueIsObjectOfClass(context, object, Type_privateData::Class_)); + Type_privateData *internal(reinterpret_cast(JSObjectGetPrivate(object))); + element.type = internal->type_; + } + + return CYMakeType(context, &type); + } else { + throw CYJSError(context, "incorrect number of arguments to Type constructor"); + } } CYCatch(NULL) } static JSValueRef Type_callAsFunction_$With(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], sig::Primitive primitive, JSValueRef *exception) { CYTry { @@ -1376,6 +1497,38 @@ static JSObjectRef Functor_new(JSContextRef context, JSObjectRef object, size_t return CYMakeFunctor(context, arguments[0], signature); } CYCatch(NULL) } +static JSValueRef CString_callAsFunction_toPointer(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { + CString *internal(reinterpret_cast(JSObjectGetPrivate(_this))); + + sig::Type type; + type.name = NULL; + type.flags = 0; + + type.primitive = sig::char_P; + type.data.data.type = NULL; + type.data.data.size = 0; + + return CYMakePointer(context, internal->value_, _not(size_t), &type, NULL, NULL); +} CYCatch(NULL) } + +static JSValueRef Functor_callAsFunction_toPointer(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { + CYPool pool; + cy::Functor *internal(reinterpret_cast(JSObjectGetPrivate(_this))); + + sig::Type type; + type.name = NULL; + type.flags = 0; + + type.primitive = sig::function_P; + sig::Copy(pool, type.data.signature, internal->signature_); + + return CYMakePointer(context, internal->value_, _not(size_t), &type, NULL, NULL); +} CYCatch(NULL) } + +static JSValueRef Pointer_callAsFunction_toPointer(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { + return _this; +} CYCatch(NULL) } + static JSValueRef CYValue_callAsFunction_valueOf(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { CYValue *internal(reinterpret_cast(JSObjectGetPrivate(_this))); return CYCastJSValue(context, reinterpret_cast(internal->value_)); @@ -1401,10 +1554,25 @@ static JSValueRef Pointer_callAsFunction_toCYON(JSContextRef context, JSObjectRe JSObjectRef toCYON(CYCastJSObject(context, CYGetProperty(context, Array, toCYON_s))); return CYCallAsFunction(context, toCYON, _this, count, arguments); } else if (internal->type_->type_ == NULL) pointer: { - char string[32]; - sprintf(string, "%p", internal->value_); - return CYCastJSValue(context, string); - } try { + CYLocalPool pool; + std::ostringstream str; + + sig::Type type; + type.name = NULL; + type.flags = 0; + + type.primitive = sig::pointer_P; + type.data.data.type = internal->type_->type_; + type.data.data.size = 0; + + CYOptions options; + CYOutput output(*str.rdbuf(), options); + (new(pool) CYTypeExpression(Decode(pool, &type)))->Output(output, CYNoFlags); + + str << "(" << internal->value_ << ")"; + std::string value(str.str()); + return CYCastJSValue(context, CYJSString(CYUTF8String(value.c_str(), value.size()))); + } else try { JSValueRef value(CYGetProperty(context, _this, cyi_s)); if (JSValueIsUndefined(context, value)) goto pointer; @@ -1415,11 +1583,45 @@ static JSValueRef Pointer_callAsFunction_toCYON(JSContextRef context, JSObjectRe } } CYCatch(NULL) } +static JSValueRef CString_getProperty_length(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry { + CString *internal(reinterpret_cast(JSObjectGetPrivate(object))); + char *string(static_cast(internal->value_)); + return CYCastJSValue(context, strlen(string)); +} CYCatch(NULL) } + +static JSValueRef CString_getProperty_type(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry { + sig::Type type; + type.name = NULL; + type.flags = 0; + + type.primitive = sig::char_P; + type.data.data.type = NULL; + type.data.data.size = 0; + + return CYMakeType(context, &type); +} CYCatch(NULL) } + static JSValueRef Pointer_getProperty_type(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry { Pointer *internal(reinterpret_cast(JSObjectGetPrivate(object))); return CYMakeType(context, internal->type_->type_); } CYCatch(NULL) } +static JSValueRef CString_callAsFunction_toCYON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { + Pointer *internal(reinterpret_cast(JSObjectGetPrivate(_this))); + const char *string(static_cast(internal->value_)); + std::ostringstream str; + str << "&"; + CYStringify(str, string, strlen(string), true); + std::string value(str.str()); + return CYCastJSValue(context, CYJSString(CYUTF8String(value.c_str(), value.size()))); +} CYCatch(NULL) } + +static JSValueRef CString_callAsFunction_toString(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { + Pointer *internal(reinterpret_cast(JSObjectGetPrivate(_this))); + const char *string(static_cast(internal->value_)); + return CYCastJSValue(context, string); +} CYCatch(NULL) } + static JSValueRef Functor_getProperty_type(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry { cy::Functor *internal(reinterpret_cast(JSObjectGetPrivate(object))); return CYMakeType(context, &internal->signature_); @@ -1450,10 +1652,10 @@ static JSValueRef Type_callAsFunction_toString(JSContextRef context, JSObjectRef static JSValueRef Type_callAsFunction_toCYON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { Type_privateData *internal(reinterpret_cast(JSObjectGetPrivate(_this))); CYLocalPool pool; - std::ostringstream out; + std::stringbuf out; CYOptions options; CYOutput output(out, options); - (new(pool) CYEncodedType(Decode(pool, internal->type_)))->Output(output, CYNoFlags); + (new(pool) CYTypeExpression(Decode(pool, internal->type_)))->Output(output, CYNoFlags); return CYCastJSValue(context, CYJSString(out.str().c_str())); } CYCatch(NULL) } @@ -1461,9 +1663,25 @@ static JSValueRef Type_callAsFunction_toJSON(JSContextRef context, JSObjectRef o return Type_callAsFunction_toString(context, object, _this, count, arguments, exception); } -static JSStaticFunction Pointer_staticFunctions[4] = { +static JSStaticFunction CString_staticFunctions[6] = { + {"toCYON", &CString_callAsFunction_toCYON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"toJSON", &CYValue_callAsFunction_toJSON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"toPointer", &CString_callAsFunction_toPointer, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"toString", &CString_callAsFunction_toString, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"valueOf", &CString_callAsFunction_toString, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {NULL, NULL, 0} +}; + +static JSStaticValue CString_staticValues[3] = { + {"length", &CString_getProperty_length, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"type", &CString_getProperty_type, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {NULL, NULL, NULL, 0} +}; + +static JSStaticFunction Pointer_staticFunctions[5] = { {"toCYON", &Pointer_callAsFunction_toCYON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, {"toJSON", &CYValue_callAsFunction_toJSON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"toPointer", &Pointer_callAsFunction_toPointer, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, {"valueOf", &CYValue_callAsFunction_valueOf, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, {NULL, NULL, 0} }; @@ -1478,9 +1696,10 @@ static JSStaticFunction Struct_staticFunctions[2] = { {NULL, NULL, 0} }; -static JSStaticFunction Functor_staticFunctions[4] = { +static JSStaticFunction Functor_staticFunctions[5] = { {"toCYON", &CYValue_callAsFunction_toCYON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, {"toJSON", &CYValue_callAsFunction_toJSON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, + {"toPointer", &Functor_callAsFunction_toPointer, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, {"valueOf", &CYValue_callAsFunction_valueOf, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete}, {NULL, NULL, 0} }; @@ -1524,7 +1743,7 @@ static JSStaticFunction Type_staticFunctions[14] = { static JSObjectRef (*JSObjectMakeArray$)(JSContextRef, size_t, const JSValueRef[], JSValueRef *); -void CYSetArgs(int argc, const char *argv[]) { +_visible void CYSetArgs(int argc, const char *argv[]) { JSContextRef context(CYGetJSContext()); JSValueRef args[argc]; for (int i(0); i != argc; ++i) @@ -1576,36 +1795,36 @@ class ExecutionHandle { } }; -const char *CYExecute(JSContextRef context, CYPool &pool, CYUTF8String code) { - JSValueRef exception(NULL); +static volatile bool cancel_; - ExecutionHandle handle(context); +static bool CYShouldTerminate(JSContextRef context, void *arg) { + return cancel_; +} - JSValueRef result; try { - result = JSEvaluateScript(context, CYJSString(code), NULL, NULL, 0, &exception); - } catch (const char *error) { - return error; - } +_visible const char *CYExecute(JSContextRef context, CYPool &pool, CYUTF8String code) { + ExecutionHandle handle(context); - if (exception != NULL) error: - return CYPoolCString(pool, context, CYJSString(context, exception)); + cancel_ = false; + if (&JSContextGroupSetExecutionTimeLimit != NULL) + JSContextGroupSetExecutionTimeLimit(JSContextGetGroup(context), 0.5, &CYShouldTerminate, NULL); - if (JSValueIsUndefined(context, result)) - return NULL; + try { + JSValueRef result(_jsccall(JSEvaluateScript, context, CYJSString(code), NULL, NULL, 0)); + if (JSValueIsUndefined(context, result)) + return NULL; - const char *json; try { std::set objects; - json = CYPoolCCYON(pool, context, result, objects, &exception); - } catch (const char *error) { - return error; - } - - if (exception != NULL) - goto error; + const char *json(_jsccall(CYPoolCCYON, pool, context, result, objects)); + CYSetProperty(context, CYGetGlobalObject(context), Result_, result); - CYSetProperty(context, CYGetGlobalObject(context), Result_, result); + return json; + } catch (const CYException &error) { + return pool.strcat("throw ", error.PoolCString(pool), NULL); + } +} - return json; +_visible void CYCancel() { + cancel_ = true; } static bool initialized_ = false; @@ -1632,6 +1851,15 @@ void CYInitializeDynamic() { definition.finalize = &CYFinalize; Context_ = JSClassCreate(&definition); + definition = kJSClassDefinitionEmpty; + definition.className = "CString"; + definition.staticFunctions = CString_staticFunctions; + definition.staticValues = CString_staticValues; + definition.getProperty = &CString_getProperty; + definition.setProperty = &CString_setProperty; + definition.finalize = &CYFinalize; + CString_ = JSClassCreate(&definition); + definition = kJSClassDefinitionEmpty; definition.className = "Functor"; definition.staticFunctions = cy::Functor::StaticFunctions; @@ -1686,6 +1914,7 @@ void CYInitializeDynamic() { toJSON_s = JSStringCreateWithUTF8CString("toJSON"); toPointer_s = JSStringCreateWithUTF8CString("toPointer"); toString_s = JSStringCreateWithUTF8CString("toString"); + weak_s = JSStringCreateWithUTF8CString("weak"); Result_ = JSStringCreateWithUTF8CString("_"); @@ -1705,19 +1934,19 @@ const char *CYJSError::PoolCString(CYPool &pool) const { return CYPoolCCYON(pool, context_, value_, objects); } -JSValueRef CYJSError::CastJSValue(JSContextRef context) const { - // XXX: what if the context is different? +JSValueRef CYJSError::CastJSValue(JSContextRef context, const char *name) const { + // XXX: what if the context is different? or the name? I dunno. ("epic" :/) return value_; } -JSValueRef CYCastJSError(JSContextRef context, const char *message) { - JSObjectRef Error(CYGetCachedObject(context, CYJSString("Error"))); +JSValueRef CYCastJSError(JSContextRef context, const char *name, const char *message) { + JSObjectRef Error(CYGetCachedObject(context, CYJSString(name))); JSValueRef arguments[1] = {CYCastJSValue(context, message)}; return _jsccall(JSObjectCallAsConstructor, context, Error, 1, arguments); } -JSValueRef CYPoolError::CastJSValue(JSContextRef context) const { - return CYCastJSError(context, message_); +JSValueRef CYPoolError::CastJSValue(JSContextRef context, const char *name) const { + return CYCastJSError(context, name, message_); } CYJSError::CYJSError(JSContextRef context, const char *format, ...) { @@ -1731,17 +1960,31 @@ CYJSError::CYJSError(JSContextRef context, const char *format, ...) { const char *message(pool.vsprintf(64, format, args)); va_end(args); - value_ = CYCastJSError(context, message); + value_ = CYCastJSError(context, "Error", message); } JSGlobalContextRef CYGetJSContext(JSContextRef context) { return reinterpret_cast(JSObjectGetPrivate(CYCastJSObject(context, CYGetProperty(context, CYGetGlobalObject(context), cy_s))))->context_; } -extern "C" bool CydgetMemoryParse(const uint16_t **data, size_t *size); +struct CYFile { + void *data_; + size_t size_; -void *CYMapFile(const char *path, size_t *psize) { - int fd(_syscall_(open(path, O_RDONLY), 1, {ENOENT})); + CYFile(void *data, size_t size) : + data_(data), + size_(size) + { + } +}; + +static void CYFileExit(void *data) { + CYFile *file(reinterpret_cast(data)); + _syscall(munmap(file->data_, file->size_)); +} + +static void *CYPoolFile(CYPool &pool, const char *path, size_t *psize) { + int fd(_syscall_(open(path, O_RDONLY), 1, ENOENT)); if (fd == -1) return NULL; @@ -1754,22 +1997,37 @@ void *CYMapFile(const char *path, size_t *psize) { void *base; _syscall(base = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0)); + CYFile *file(new (pool) CYFile(base, size)); + pool.atexit(&CYFileExit, file); + _syscall(close(fd)); return base; } -static JSValueRef require(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { - _assert(count == 1); - CYPool pool; +static CYUTF8String CYPoolFileUTF8String(CYPool &pool, const char *path) { + CYUTF8String data; + data.data = reinterpret_cast(CYPoolFile(pool, path, &data.size)); + return data; +} +static const char *CYPoolLibraryPath(CYPool &pool) { Dl_info addr; - _assert(dladdr(reinterpret_cast(&require), &addr) != 0); + _assert(dladdr(reinterpret_cast(&CYPoolLibraryPath), &addr) != 0); char *lib(pool.strdup(addr.dli_fname)); char *slash(strrchr(lib, '/')); _assert(slash != NULL); *slash = '\0'; + return lib; +} + +static JSValueRef require(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { + _assert(count == 1); + CYPool pool; + + const char *lib(CYPoolLibraryPath(pool)); + CYJSString property("exports"); JSObjectRef module; @@ -1783,8 +2041,7 @@ static JSValueRef require(JSContextRef context, JSObjectRef object, JSObjectRef if (!JSValueIsUndefined(context, cache)) module = CYCastJSObject(context, cache); else { - CYUTF8String code; - code.data = reinterpret_cast(CYMapFile(path, &code.size)); + CYUTF8String code(CYPoolFileUTF8String(pool, path)); if (code.data == NULL) { if (strchr(name, '/') == NULL && ( @@ -1806,7 +2063,7 @@ static JSValueRef require(JSContextRef context, JSObjectRef object, JSObjectRef std::stringstream wrap; wrap << "(function (exports, require, module) { " << code << "\n});"; - code = CYPoolCode(pool, wrap); + code = CYPoolCode(pool, *wrap.rdbuf()); JSValueRef value(_jsccall(JSEvaluateScript, context, CYJSString(code), NULL, NULL, 0)); JSObjectRef function(CYCastJSObject(context, value)); @@ -1818,6 +2075,21 @@ static JSValueRef require(JSContextRef context, JSObjectRef object, JSObjectRef return CYGetProperty(context, module, property); } CYCatch(NULL) } +static bool CYRunScript(JSGlobalContextRef context, const char *path) { + CYPool pool; + CYUTF8String code(CYPoolFileUTF8String(pool, path)); + if (code.data == NULL) + return false; + + CYStream stream(code.data, code.data + code.size); + code = CYPoolCode(pool, stream); + _jsccall(JSEvaluateScript, context, CYJSString(code), NULL, NULL, 0); + return true; +} + +extern "C" void CYDestroyWeak(JSWeakObjectMapRef weak, void *data) { +} + extern "C" void CYSetupContext(JSGlobalContextRef context) { CYInitializeDynamic(); @@ -1865,6 +2137,9 @@ extern "C" void CYSetupContext(JSGlobalContextRef context) { JSObjectRef String_prototype(CYCastJSObject(context, CYGetProperty(context, String, prototype_s))); CYSetProperty(context, cy, CYJSString("String_prototype"), String_prototype); + + JSObjectRef SyntaxError(CYCastJSObject(context, CYGetProperty(context, global, CYJSString("SyntaxError")))); + CYSetProperty(context, cy, CYJSString("SyntaxError"), SyntaxError); /* }}} */ CYSetProperty(context, Array_prototype, toCYON_s, &Array_callAsFunction_toCYON, kJSPropertyAttributeDontEnum); @@ -1872,8 +2147,13 @@ extern "C" void CYSetupContext(JSGlobalContextRef context) { JSObjectRef cycript(JSObjectMake(context, NULL, NULL)); CYSetProperty(context, global, CYJSString("Cycript"), cycript); + CYSetProperty(context, cycript, CYJSString("compile"), &Cycript_compile_callAsFunction); CYSetProperty(context, cycript, CYJSString("gc"), &Cycript_gc_callAsFunction); + JSObjectRef CString(JSObjectMakeConstructor(context, CString_, &CString_new)); + CYSetPrototype(context, CYCastJSObject(context, CYGetProperty(context, CString, prototype_s)), String_prototype); + CYSetProperty(context, cycript, CYJSString("CString"), CString); + JSObjectRef Functor(JSObjectMakeConstructor(context, Functor_, &Functor_new)); CYSetPrototype(context, CYCastJSObject(context, CYGetProperty(context, Functor, prototype_s)), Function_prototype); CYSetProperty(context, cycript, CYJSString("Functor"), Functor); @@ -1905,8 +2185,6 @@ extern "C" void CYSetupContext(JSGlobalContextRef context) { CYSetPrototype(context, last, all); } - CYSetProperty(context, global, CYJSString("$cyq"), &$cyq, kJSPropertyAttributeDontEnum); - JSObjectRef System(JSObjectMake(context, NULL, NULL)); CYSetProperty(context, cy, CYJSString("System"), System); @@ -1917,9 +2195,18 @@ extern "C" void CYSetupContext(JSGlobalContextRef context) { //CYSetProperty(context, System, CYJSString("global"), global); CYSetProperty(context, System, CYJSString("print"), &System_print); +#ifdef __APPLE__ + if (&JSWeakObjectMapCreate != NULL) { + JSWeakObjectMapRef weak(JSWeakObjectMapCreate(context, NULL, &CYDestroyWeak)); + CYSetProperty(context, cy, weak_s, CYCastJSValue(context, reinterpret_cast(weak))); + } +#endif + if (CYBridgeEntry *entry = CYBridgeHash("1dlerror", 8)) entry->cache_ = new cy::Functor(entry->value_, reinterpret_cast(&dlerror)); + CYRunScript(context, "libcycript.cy"); + for (CYHook *hook : GetHooks()) if (hook->SetupContext != NULL) (*hook->SetupContext)(context); @@ -1929,7 +2216,7 @@ extern "C" void CYSetupContext(JSGlobalContextRef context) { static JSGlobalContextRef context_; -JSGlobalContextRef CYGetJSContext() { +_visible JSGlobalContextRef CYGetJSContext() { CYInitializeDynamic(); if (context_ == NULL) { @@ -1940,7 +2227,7 @@ JSGlobalContextRef CYGetJSContext() { return context_; } -void CYDestroyContext() { +_visible void CYDestroyContext() { if (context_ == NULL) return; JSGlobalContextRelease(context_);