X-Git-Url: https://git.saurik.com/cycript.git/blobdiff_plain/56f57e5b36301ac84c1ca5d4eb7fa363f79c81f6..b23692f3038296c94d25c84c55ed4c1bb49619cf:/Execute.cpp diff --git a/Execute.cpp b/Execute.cpp index 522aca4..c71bc9f 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() { @@ -261,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); } @@ -344,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); @@ -421,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); @@ -532,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; @@ -748,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_); @@ -759,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); } @@ -767,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; @@ -791,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)); @@ -803,7 +820,7 @@ 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); @@ -990,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"); @@ -1008,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); } @@ -1462,7 +1481,7 @@ 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); @@ -1536,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) @@ -1588,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; - } + const char *json(_jsccall(CYPoolCCYON, pool, context, result, objects)); + CYSetProperty(context, CYGetGlobalObject(context), Result_, result); - if (exception != NULL) - goto error; - - 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; @@ -1718,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, ...) { @@ -1744,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; @@ -1831,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(); @@ -1855,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); @@ -1878,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)); @@ -1918,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); @@ -1930,10 +1956,12 @@ 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, 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)); @@ -1947,7 +1975,7 @@ extern "C" void CYSetupContext(JSGlobalContextRef context) { static JSGlobalContextRef context_; -JSGlobalContextRef CYGetJSContext() { +_visible JSGlobalContextRef CYGetJSContext() { CYInitializeDynamic(); if (context_ == NULL) { @@ -1958,7 +1986,7 @@ JSGlobalContextRef CYGetJSContext() { return context_; } -void CYDestroyContext() { +_visible void CYDestroyContext() { if (context_ == NULL) return; JSGlobalContextRelease(context_);