]> git.saurik.com Git - cycript.git/blobdiff - Execute.cpp
Make an array builder to keep JSValueRef on stack.
[cycript.git] / Execute.cpp
index 1c6b7bae89ce4de181af5b0b20d1bb4dd841fb0c..d27183c76a0f0eb5acd7c4cdfcd71fa0ed6e297f 100644 (file)
 #include "Pooling.hpp"
 #include "String.hpp"
 
+const char *sqlite3_column_string(sqlite3_stmt *stmt, int n) {
+    return reinterpret_cast<const char *>(sqlite3_column_text(stmt, n));
+}
+
 char *sqlite3_column_pooled(CYPool &pool, sqlite3_stmt *stmt, int n) {
-    if (const unsigned char *value = sqlite3_column_text(stmt, n))
-        return pool.strdup(reinterpret_cast<const char *>(value));
+    if (const char *value = sqlite3_column_string(stmt, n))
+        return pool.strdup(value);
     else return NULL;
 }
 
@@ -150,6 +154,18 @@ size_t CYGetIndex(CYPool &pool, JSContextRef context, JSStringRef value) {
 }
 /* }}} */
 
+static JSObjectRef (*JSObjectMakeArray$)(JSContextRef, size_t, const JSValueRef[], JSValueRef *);
+
+static JSObjectRef CYObjectMakeArray(JSContextRef context, size_t length, const JSValueRef values[]) {
+    if (JSObjectMakeArray$ != NULL)
+        return _jsccall(*JSObjectMakeArray$, context, length, values);
+    else {
+        JSObjectRef Array(CYGetCachedObject(context, CYJSString("Array")));
+        JSValueRef value(CYCallAsFunction(context, Array, NULL, length, values));
+        return CYCastJSObject(context, value);
+    }
+}
+
 static JSClassRef All_;
 static JSClassRef Context_;
 static JSClassRef CString_;
@@ -356,13 +372,52 @@ JSValueRef CYArrayGet(JSContextRef context, JSObjectRef array, size_t index) {
     return _jsccall(JSObjectGetPropertyAtIndex, context, array, index);
 }
 
-void CYArrayPush(JSContextRef context, JSObjectRef array, JSValueRef value) {
-    JSValueRef arguments[1];
-    arguments[0] = value;
+void CYArrayPush(JSContextRef context, JSObjectRef array, size_t length, const JSValueRef arguments[]) {
     JSObjectRef Array(CYGetCachedObject(context, CYJSString("Array_prototype")));
-    _jsccall(JSObjectCallAsFunction, context, CYCastJSObject(context, CYGetProperty(context, Array, push_s)), array, 1, arguments);
+    _jsccall(JSObjectCallAsFunction, context, CYCastJSObject(context, CYGetProperty(context, Array, push_s)), array, length, arguments);
 }
 
+void CYArrayPush(JSContextRef context, JSObjectRef array, JSValueRef value) {
+    return CYArrayPush(context, array, 1, &value);
+}
+
+template <size_t Size_>
+class CYArrayBuilder {
+  private:
+    JSContextRef context_;
+    JSObjectRef &array_;
+    size_t size_;
+    JSValueRef values_[Size_];
+
+    void flush() {
+        if (array_ == NULL)
+            array_ = CYObjectMakeArray(context_, size_, values_);
+        else
+            CYArrayPush(context_, array_, size_, values_);
+    }
+
+  public:
+    CYArrayBuilder(JSContextRef context, JSObjectRef &array) :
+        context_(context),
+        array_(array),
+        size_(0)
+    {
+    }
+
+    ~CYArrayBuilder() {
+        flush();
+    }
+
+    void operator ()(JSValueRef value) {
+        if (size_ == Size_) {
+            flush();
+            size_ = 0;
+        }
+
+        values_[size_++] = value;
+    }
+};
+
 static JSValueRef System_print(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
     FILE *file(stdout);
 
@@ -646,7 +701,7 @@ void CYPoolFFI(CYPool *pool, JSContextRef context, sig::Type *type, ffi_type *ff
         break;
 
         CYPoolFFI_(uchar, unsigned char)
-        CYPoolFFI_(char, char)
+        CYPoolFFI_(schar, signed char)
         CYPoolFFI_(ushort, unsigned short)
         CYPoolFFI_(short, short)
         CYPoolFFI_(ulong, unsigned long)
@@ -721,6 +776,9 @@ void CYPoolFFI(CYPool *pool, JSContextRef context, sig::Type *type, ffi_type *ff
         case sig::void_P:
         break;
 
+        // XXX: implement a conversion from a single character string?
+        CYPoolFFI_(char, char)
+
         default:
             for (CYHook *hook : GetHooks())
                 if (hook->PoolFFI != NULL)
@@ -741,7 +799,7 @@ JSValueRef CYFromFFI(JSContextRef context, sig::Type *type, ffi_type *ffi, void
             return CYCastJSValue(context, *reinterpret_cast<native *>(data)); \
 
         CYFromFFI_(uchar, unsigned char)
-        CYFromFFI_(char, char)
+        CYFromFFI_(schar, signed char)
         CYFromFFI_(ushort, unsigned short)
         CYFromFFI_(short, short)
         CYFromFFI_(ulong, unsigned long)
@@ -773,6 +831,8 @@ JSValueRef CYFromFFI(JSContextRef context, sig::Type *type, ffi_type *ffi, void
         case sig::void_P:
             return CYJSUndefined(context);
 
+        CYFromFFI_(char, char)
+
         null:
             return CYJSNull(context);
         default:
@@ -1098,8 +1158,8 @@ static JSValueRef Pointer_callAsFunction(JSContextRef context, JSObjectRef objec
     return CYCallAsFunction(context, functor, _this, count, arguments);
 } CYCatch(NULL) }
 
-JSObjectRef CYMakeType(JSContextRef context, const char *encoding) {
-    Type_privateData *internal(new Type_privateData(encoding));
+JSObjectRef CYMakeType(JSContextRef context, sig::Primitive primitive) {
+    Type_privateData *internal(new Type_privateData(primitive));
     return JSObjectMake(context, Type_privateData::Class_, internal);
 }
 
@@ -1121,12 +1181,13 @@ JSObjectRef CYMakeType(JSContextRef context, sig::Signature *signature) {
     return CYMakeType(context, &type);
 }
 
-extern "C" const char *CYBridgeHash(CYPool &pool, CYUTF8String name) {
+extern "C" bool CYBridgeHash(CYPool &pool, CYUTF8String name, const char *&code, unsigned &flags) {
     sqlite3_stmt *statement;
 
     _sqlcall(sqlite3_prepare(database_,
         "select "
-            "\"cache\".\"value\" "
+            "\"cache\".\"code\", "
+            "\"cache\".\"flags\" "
         "from \"cache\" "
         "where"
             " \"cache\".\"system\" & " CY_SYSTEM " == " CY_SYSTEM " and"
@@ -1136,17 +1197,23 @@ extern "C" const char *CYBridgeHash(CYPool &pool, CYUTF8String name) {
 
     _sqlcall(sqlite3_bind_text(statement, 1, name.data, name.size, SQLITE_STATIC));
 
-    const char *value;
+    bool success;
     if (_sqlcall(sqlite3_step(statement)) == SQLITE_DONE)
-        value = NULL;
-    else
-        value = sqlite3_column_pooled(pool, statement, 0);
+        success = false;
+    else {
+        success = true;
+        code = sqlite3_column_pooled(pool, statement, 0);
+        flags = sqlite3_column_int(statement, 1);
+    }
 
     _sqlcall(sqlite3_finalize(statement));
-    return value;
+    return success;
 }
 
 static bool All_hasProperty(JSContextRef context, JSObjectRef object, JSStringRef property) {
+    if (JSStringIsEqualToUTF8CString(property, "errno"))
+        return true;
+
     JSObjectRef global(CYGetGlobalObject(context));
     JSObjectRef cycript(CYCastJSObject(context, CYGetProperty(context, global, CYJSString("Cycript"))));
     JSObjectRef alls(CYCastJSObject(context, CYGetProperty(context, cycript, CYJSString("alls"))));
@@ -1157,13 +1224,18 @@ static bool All_hasProperty(JSContextRef context, JSObjectRef object, JSStringRe
                 return true;
 
     CYPool pool;
-    if (CYBridgeHash(pool, CYPoolUTF8String(pool, context, property)) != NULL)
+    const char *code;
+    unsigned flags;
+    if (CYBridgeHash(pool, CYPoolUTF8String(pool, context, property), code, flags))
         return true;
 
     return false;
 }
 
 static JSValueRef All_getProperty(JSContextRef context, JSObjectRef object, JSStringRef property, JSValueRef *exception) { CYTry {
+    if (JSStringIsEqualToUTF8CString(property, "errno"))
+        return CYCastJSValue(context, errno);
+
     JSObjectRef global(CYGetGlobalObject(context));
     JSObjectRef cycript(CYCastJSObject(context, CYGetProperty(context, global, CYJSString("Cycript"))));
     JSObjectRef alls(CYCastJSObject(context, CYGetProperty(context, cycript, CYJSString("alls"))));
@@ -1175,16 +1247,76 @@ static JSValueRef All_getProperty(JSContextRef context, JSObjectRef object, JSSt
                     return value;
 
     CYPool pool;
-    if (const char *code = CYBridgeHash(pool, CYPoolUTF8String(pool, context, property))) {
-        JSValueRef result(_jsccall(JSEvaluateScript, context, CYJSString(CYPoolCode(pool, code)), NULL, NULL, 0));
-        JSObjectRef cache(CYGetCachedObject(context, CYJSString("cache")));
-        CYSetProperty(context, cache, property, result);
+    const char *code;
+    unsigned flags;
+    if (CYBridgeHash(pool, CYPoolUTF8String(pool, context, property), code, flags)) {
+        CYUTF8String parsed;
+
+        try {
+            parsed = CYPoolCode(pool, code);
+        } catch (const CYException &error) {
+            CYThrow("%s", pool.strcat("error caching ", CYPoolCString(pool, context, property), ": ", error.PoolCString(pool), NULL));
+        }
+
+        JSValueRef result(_jsccall(JSEvaluateScript, context, CYJSString(parsed), NULL, NULL, 0));
+
+        if (flags == 0) {
+            JSObjectRef cache(CYGetCachedObject(context, CYJSString("cache")));
+            CYSetProperty(context, cache, property, result);
+        }
+
         return result;
     }
 
     return NULL;
 } CYCatch(NULL) }
 
+static JSValueRef All_complete_callAsFunction(JSContextRef context, JSObjectRef object, JSObjectRef _this, size_t count, const JSValueRef arguments[], JSValueRef *exception) { CYTry {
+    _assert(count == 1);
+    CYPool pool;
+    CYUTF8String prefix(CYPoolUTF8String(pool, context, CYJSString(context, arguments[0])));
+
+    JSObjectRef array(NULL);
+
+    {
+    CYArrayBuilder<1024> values(context, array);
+
+    sqlite3_stmt *statement;
+
+    if (prefix.size == 0)
+        _sqlcall(sqlite3_prepare(database_,
+            "select "
+                "\"cache\".\"name\" "
+            "from \"cache\" "
+            "where"
+                " \"cache\".\"system\" & " CY_SYSTEM " == " CY_SYSTEM
+        , -1, &statement, NULL));
+    else {
+        _sqlcall(sqlite3_prepare(database_,
+            "select "
+                "\"cache\".\"name\" "
+            "from \"cache\" "
+            "where"
+                " \"cache\".\"name\" >= ? and \"cache\".\"name\" < ? and "
+                " \"cache\".\"system\" & " CY_SYSTEM " == " CY_SYSTEM
+        , -1, &statement, NULL));
+
+        _sqlcall(sqlite3_bind_text(statement, 1, prefix.data, prefix.size, SQLITE_STATIC));
+
+        char *after(pool.strndup(prefix.data, prefix.size));
+        ++after[prefix.size - 1];
+        _sqlcall(sqlite3_bind_text(statement, 2, after, prefix.size, SQLITE_STATIC));
+    }
+
+    while (_sqlcall(sqlite3_step(statement)) != SQLITE_DONE)
+        values(CYCastJSValue(context, CYJSString(sqlite3_column_string(statement, 0))));
+
+    _sqlcall(sqlite3_finalize(statement));
+    }
+
+    return array;
+} CYCatch(NULL) }
+
 static void All_getPropertyNames(JSContextRef context, JSObjectRef object, JSPropertyNameAccumulatorRef names) {
     JSObjectRef global(CYGetGlobalObject(context));
     JSObjectRef cycript(CYCastJSObject(context, CYGetProperty(context, global, CYJSString("Cycript"))));
@@ -1226,8 +1358,10 @@ static JSObjectRef Type_new(JSContextRef context, JSObjectRef object, size_t cou
 
     if (false) {
     } else if (count == 1) {
-        const char *type(CYPoolCString(pool, context, arguments[0]));
-        return CYMakeType(context, type);
+        const char *encoding(CYPoolCString(pool, context, arguments[0]));
+        sig::Signature signature;
+        sig::Parse(pool, &signature, encoding, &Structor_);
+        return CYMakeType(context, signature.elements[0].type);
     } else if (count == 2) {
         JSObjectRef types(CYCastJSObject(context, arguments[0]));
         size_t count(CYArrayLength(context, types));
@@ -1373,7 +1507,7 @@ static JSValueRef Type_callAsFunction_signed(JSContextRef context, JSObjectRef o
     sig::Type type(*internal->type_);
 
     switch (type.primitive) {
-        case sig::char_P: case sig::uchar_P: type.primitive = sig::char_P; break;
+        case sig::char_P: case sig::schar_P: case sig::uchar_P: type.primitive = sig::schar_P; break;
         case sig::short_P: case sig::ushort_P: type.primitive = sig::short_P; break;
         case sig::int_P: case sig::uint_P: type.primitive = sig::int_P; break;
         case sig::long_P: case sig::ulong_P: type.primitive = sig::long_P; break;
@@ -1392,7 +1526,7 @@ static JSValueRef Type_callAsFunction_unsigned(JSContextRef context, JSObjectRef
     sig::Type type(*internal->type_);
 
     switch (type.primitive) {
-        case sig::char_P: case sig::uchar_P: type.primitive = sig::uchar_P; break;
+        case sig::char_P: case sig::schar_P: case sig::uchar_P: type.primitive = sig::uchar_P; break;
         case sig::short_P: case sig::ushort_P: type.primitive = sig::ushort_P; break;
         case sig::int_P: case sig::uint_P: type.primitive = sig::uint_P; break;
         case sig::long_P: case sig::ulong_P: type.primitive = sig::ulong_P; break;
@@ -1669,6 +1803,11 @@ static JSValueRef Type_callAsFunction_toJSON(JSContextRef context, JSObjectRef o
     return Type_callAsFunction_toString(context, object, _this, count, arguments, exception);
 }
 
+static JSStaticFunction All_staticFunctions[2] = {
+    {"cy$complete", &All_complete_callAsFunction, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
+    {NULL, NULL, 0}
+};
+
 static JSStaticFunction CString_staticFunctions[6] = {
     {"toCYON", &CString_callAsFunction_toCYON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
     {"toJSON", &CYValue_callAsFunction_toJSON, kJSPropertyAttributeDontEnum | kJSPropertyAttributeDontDelete},
@@ -1747,23 +1886,13 @@ static JSStaticFunction Type_staticFunctions[14] = {
     {NULL, NULL, 0}
 };
 
-static JSObjectRef (*JSObjectMakeArray$)(JSContextRef, size_t, const JSValueRef[], JSValueRef *);
-
 _visible void CYSetArgs(int argc, const char *argv[]) {
     JSContextRef context(CYGetJSContext());
     JSValueRef args[argc];
     for (int i(0); i != argc; ++i)
         args[i] = CYCastJSValue(context, argv[i]);
 
-    JSObjectRef array;
-    if (JSObjectMakeArray$ != NULL)
-        array = _jsccall(*JSObjectMakeArray$, context, argc, args);
-    else {
-        JSObjectRef Array(CYGetCachedObject(context, CYJSString("Array")));
-        JSValueRef value(CYCallAsFunction(context, Array, NULL, argc, args));
-        array = CYCastJSObject(context, value);
-    }
-
+    JSObjectRef array(CYObjectMakeArray(context, argc, args));
     JSObjectRef System(CYGetCachedObject(context, CYJSString("System")));
     CYSetProperty(context, System, CYJSString("args"), array);
 }
@@ -1853,6 +1982,7 @@ void CYInitializeDynamic() {
 
     definition = kJSClassDefinitionEmpty;
     definition.className = "All";
+    definition.staticFunctions = All_staticFunctions;
     definition.hasProperty = &All_hasProperty;
     definition.getProperty = &All_getProperty;
     definition.getPropertyNames = &All_getPropertyNames;
@@ -1989,6 +2119,10 @@ static const char *CYPoolLibraryPath(CYPool &pool) {
     _assert(slash != NULL);
     *slash = '\0';
 
+    slash = strrchr(lib, '/');
+    if (slash != NULL && strcmp(slash, "/.libs") == 0)
+        *slash = '\0';
+
     return lib;
 }
 
@@ -2052,7 +2186,7 @@ static JSValueRef require_callAsFunction(JSContextRef context, JSObjectRef objec
 
 static bool CYRunScript(JSGlobalContextRef context, const char *path) {
     CYPool pool;
-    CYUTF8String code(CYPoolFileUTF8String(pool, path));
+    CYUTF8String code(CYPoolFileUTF8String(pool, pool.strcat(CYPoolLibraryPath(pool), path, NULL)));
     if (code.data == NULL)
         return false;
 
@@ -2169,7 +2303,7 @@ extern "C" void CYSetupContext(JSGlobalContextRef context) {
     JSObjectRef System(JSObjectMake(context, NULL, NULL));
     CYSetProperty(context, cy, CYJSString("System"), System);
 
-    CYSetProperty(context, all, CYJSString("require"), &require_callAsFunction, kJSPropertyAttributeDontEnum);
+    CYSetProperty(context, global, CYJSString("require"), &require_callAsFunction, kJSPropertyAttributeDontEnum);
 
     CYSetProperty(context, global, CYJSString("system"), System);
     CYSetProperty(context, System, CYJSString("args"), CYJSNull(context));
@@ -2190,21 +2324,21 @@ extern "C" void CYSetupContext(JSGlobalContextRef context) {
 
     CYSetProperty(context, cache, CYJSString("NULL"), CYJSNull(context), kJSPropertyAttributeDontEnum);
 
-    CYSetProperty(context, cache, CYJSString("bool"), CYMakeType(context, "B"), kJSPropertyAttributeDontEnum);
-    CYSetProperty(context, cache, CYJSString("char"), CYMakeType(context, "c"), kJSPropertyAttributeDontEnum);
-    CYSetProperty(context, cache, CYJSString("short"), CYMakeType(context, "s"), kJSPropertyAttributeDontEnum);
-    CYSetProperty(context, cache, CYJSString("int"), CYMakeType(context, "i"), kJSPropertyAttributeDontEnum);
-    CYSetProperty(context, cache, CYJSString("long"), CYMakeType(context, "l"), kJSPropertyAttributeDontEnum);
-    CYSetProperty(context, cache, CYJSString("float"), CYMakeType(context, "f"), kJSPropertyAttributeDontEnum);
-    CYSetProperty(context, cache, CYJSString("double"), CYMakeType(context, "d"), kJSPropertyAttributeDontEnum);
-
-    CYRunScript(context, "libcycript.cy");
+    CYSetProperty(context, cache, CYJSString("bool"), CYMakeType(context, sig::boolean_P), kJSPropertyAttributeDontEnum);
+    CYSetProperty(context, cache, CYJSString("char"), CYMakeType(context, sig::char_P), kJSPropertyAttributeDontEnum);
+    CYSetProperty(context, cache, CYJSString("short"), CYMakeType(context, sig::short_P), kJSPropertyAttributeDontEnum);
+    CYSetProperty(context, cache, CYJSString("int"), CYMakeType(context, sig::int_P), kJSPropertyAttributeDontEnum);
+    CYSetProperty(context, cache, CYJSString("long"), CYMakeType(context, sig::long_P), kJSPropertyAttributeDontEnum);
+    CYSetProperty(context, cache, CYJSString("float"), CYMakeType(context, sig::float_P), kJSPropertyAttributeDontEnum);
+    CYSetProperty(context, cache, CYJSString("double"), CYMakeType(context, sig::double_P), kJSPropertyAttributeDontEnum);
 
     for (CYHook *hook : GetHooks())
         if (hook->SetupContext != NULL)
             (*hook->SetupContext)(context);
 
     CYArrayPush(context, alls, cycript);
+
+    CYRunScript(context, "/libcycript.cy");
 }
 
 static JSGlobalContextRef context_;