]> git.saurik.com Git - cycript.git/blobdiff - Execute.cpp
Avoid using NULL-terminated strings when possible.
[cycript.git] / Execute.cpp
index c724259c8996a79057389b7cf70a30bb0999b7fd..c71bc9f92044bfd2322fded09726238dd2fc8116 100644 (file)
@@ -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 {{{ */
 **/
 /* }}} */
 
-#include "Internal.hpp"
+#include "cycript.hpp"
+
+#include <iostream>
+#include <set>
+#include <map>
+#include <iomanip>
+#include <sstream>
+#include <cmath>
 
 #include <dlfcn.h>
 #include <dirent.h>
 #include <fcntl.h>
 #include <unistd.h>
 
-#include "cycript.hpp"
-
-#include "sig/parse.hpp"
-#include "sig/ffi_type.hpp"
-
-#include "Pooling.hpp"
-#include "Execute.hpp"
-
 #include <sys/mman.h>
 #include <sys/stat.h>
 
-#include <iostream>
-#include <set>
-#include <map>
-#include <iomanip>
-#include <sstream>
-#include <cmath>
+#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<CYHook *> &GetHooks() {
@@ -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<const char *, Type_privateData *, CYCStringLess> 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);
 }
 
@@ -346,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);
@@ -423,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<double>(reinterpret_cast<uintptr_t>(&objects)))};
+        JSValueRef arguments[1] = {CYCastJSValue(context, reinterpret_cast<uintptr_t>(&objects))};
         JSValueRef value(CYCallAsFunction(context, (JSObjectRef) toCYON, object, 1, arguments));
         _assert(value != NULL);
         return CYPoolCString(pool, context, value);
@@ -534,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;
@@ -576,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: {
@@ -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<Closure_privateData *>(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 *>(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,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) {
@@ -986,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");
@@ -1004,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);
 }
 
@@ -1458,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<Type_privateData *>(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);
@@ -1532,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)
@@ -1584,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<void *> 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;
@@ -1694,6 +1717,7 @@ void CYInitializeDynamic() {
     toJSON_s = JSStringCreateWithUTF8CString("toJSON");
     toPointer_s = JSStringCreateWithUTF8CString("toPointer");
     toString_s = JSStringCreateWithUTF8CString("toString");
+    weak_s = JSStringCreateWithUTF8CString("weak");
 
     Result_ = JSStringCreateWithUTF8CString("_");
 
@@ -1713,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, ...) {
@@ -1739,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<Context *>(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;
 
@@ -1826,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();
 
@@ -1850,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);
 
@@ -1873,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));
@@ -1913,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);
 
@@ -1925,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<uintptr_t>(weak)));
+    }
+#endif
+
     if (CYBridgeEntry *entry = CYBridgeHash("1dlerror", 8))
         entry->cache_ = new cy::Functor(entry->value_, reinterpret_cast<void (*)()>(&dlerror));
 
@@ -1937,7 +1975,7 @@ extern "C" void CYSetupContext(JSGlobalContextRef context) {
 
 static JSGlobalContextRef context_;
 
-JSGlobalContextRef CYGetJSContext() {
+_visible JSGlobalContextRef CYGetJSContext() {
     CYInitializeDynamic();
 
     if (context_ == NULL) {
@@ -1948,7 +1986,7 @@ JSGlobalContextRef CYGetJSContext() {
     return context_;
 }
 
-void CYDestroyContext() {
+_visible void CYDestroyContext() {
     if (context_ == NULL)
         return;
     JSGlobalContextRelease(context_);