X-Git-Url: https://git.saurik.com/cycript.git/blobdiff_plain/79492f212da2fcaa4ce44d00fc08530e1630d05b..64a505ff07b28093bb91a680f8a2c2292327e896:/Execute.cpp diff --git a/Execute.cpp b/Execute.cpp index f7ee724..bb337ba 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(CYIsEqual(context, JSObjectGetPrototype(context, object), value)); + _assert(CYIsStrictEqual(context, JSObjectGetPrototype(context, object), value)); } /* }}} */ /* JavaScript Strings {{{ */ @@ -154,6 +152,7 @@ JSStringRef toCYON_s; JSStringRef toJSON_s; JSStringRef toPointer_s; JSStringRef toString_s; +JSStringRef weak_s; static JSStringRef Result_; @@ -241,9 +240,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 +259,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); } @@ -326,6 +326,10 @@ 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)); } @@ -342,30 +346,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::stringstream 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); @@ -419,7 +427,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); @@ -530,6 +538,21 @@ static JSValueRef Array_callAsFunction_toCYON(JSContextRef context, JSObjectRef return CYCastJSValue(context, CYJSString(CYUTF8String(value.c_str(), value.size()))); } CYCatch(NULL) } +static JSValueRef Error_callAsFunction_toCYON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { + CYPool pool; + std::ostringstream str; + + str << "new " << CYPoolUTF8String(pool, context, CYJSString(context, CYGetProperty(context, _this, name_s))) << "("; + + CYUTF8String string(CYPoolUTF8String(pool, context, CYJSString(context, CYGetProperty(context, _this, message_s)))); + CYStringify(str, string.data, string.size); + + str << ")"; + + std::string value(str.str()); + return CYCastJSValue(context, CYJSString(CYUTF8String(value.c_str(), value.size()))); +} CYCatch(NULL) } + static JSValueRef String_callAsFunction_toCYON(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry { CYPool pool; std::ostringstream str; @@ -572,7 +595,9 @@ static bool CYGetOffset(CYPool &pool, JSContextRef context, JSStringRef value, s } void *CYCastPointer_(JSContextRef context, JSValueRef value) { - switch (JSValueGetType(context, value)) { + if (value == NULL) + return NULL; + else switch (JSValueGetType(context, value)) { case kJSTypeNull: return NULL; case kJSTypeObject: { @@ -744,7 +769,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_); @@ -755,7 +780,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); } @@ -763,20 +788,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; @@ -787,7 +808,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)); @@ -799,15 +820,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) { @@ -982,6 +1007,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"); @@ -1000,15 +1029,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); } @@ -1454,10 +1481,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) } @@ -1528,7 +1555,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) @@ -1580,36 +1607,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; @@ -1690,6 +1717,7 @@ void CYInitializeDynamic() { toJSON_s = JSStringCreateWithUTF8CString("toJSON"); toPointer_s = JSStringCreateWithUTF8CString("toPointer"); toString_s = JSStringCreateWithUTF8CString("toString"); + weak_s = JSStringCreateWithUTF8CString("weak"); Result_ = JSStringCreateWithUTF8CString("_"); @@ -1709,19 +1737,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, ...) { @@ -1735,17 +1763,15 @@ 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); - void *CYMapFile(const char *path, size_t *psize) { - int fd(_syscall_(open(path, O_RDONLY), 1, {ENOENT})); + int fd(_syscall_(open(path, O_RDONLY), 1, ENOENT)); if (fd == -1) return NULL; @@ -1822,6 +1848,9 @@ static JSValueRef require(JSContextRef context, JSObjectRef object, JSObjectRef return CYGetProperty(context, module, property); } CYCatch(NULL) } +extern "C" void CYDestroyWeak(JSWeakObjectMapRef weak, void *data) { +} + extern "C" void CYSetupContext(JSGlobalContextRef context) { CYInitializeDynamic(); @@ -1846,6 +1875,9 @@ extern "C" void CYSetupContext(JSGlobalContextRef context) { JSObjectRef Error(CYCastJSObject(context, CYGetProperty(context, global, CYJSString("Error")))); CYSetProperty(context, cy, CYJSString("Error"), Error); + JSObjectRef Error_prototype(CYCastJSObject(context, CYGetProperty(context, Error, prototype_s))); + CYSetProperty(context, cy, CYJSString("Error_prototype"), Error_prototype); + JSObjectRef Function(CYCastJSObject(context, CYGetProperty(context, global, CYJSString("Function")))); CYSetProperty(context, cy, CYJSString("Function"), Function); @@ -1869,13 +1901,18 @@ 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); + CYSetProperty(context, Error_prototype, toCYON_s, &Error_callAsFunction_toCYON, kJSPropertyAttributeDontEnum); CYSetProperty(context, String_prototype, toCYON_s, &String_callAsFunction_toCYON, kJSPropertyAttributeDontEnum); 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 Functor(JSObjectMakeConstructor(context, Functor_, &Functor_new)); @@ -1909,8 +1946,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); @@ -1921,6 +1956,13 @@ 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)); @@ -1933,7 +1975,7 @@ extern "C" void CYSetupContext(JSGlobalContextRef context) { static JSGlobalContextRef context_; -JSGlobalContextRef CYGetJSContext() { +_visible JSGlobalContextRef CYGetJSContext() { CYInitializeDynamic(); if (context_ == NULL) { @@ -1944,7 +1986,7 @@ JSGlobalContextRef CYGetJSContext() { return context_; } -void CYDestroyContext() { +_visible void CYDestroyContext() { if (context_ == NULL) return; JSGlobalContextRelease(context_);