]> git.saurik.com Git - cycript.git/blobdiff - Execute.cpp
Fix quote when stringifying C/Objective-C strings.
[cycript.git] / Execute.cpp
index 11fada2bdcd55ab3ade523561891d5b504f51719..1a3a2df9a2fabcdfd917de91c0904eff6a30c8b8 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() {
@@ -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<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 +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<Type_>(static_cast<double>(value)) == value); \
         return JSValueMakeNumber(context, static_cast<double>(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<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);
@@ -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<void *>(static_cast<uintptr_t>(static_cast<long long>(number)));
+            if (guess == NULL)
                 throw CYJSError(context, "cannot convert value to pointer");
-            return reinterpret_cast<void *>(static_cast<uintptr_t>(static_cast<long long>(number)));
+            else {
+                *guess = true;
+                return NULL;
+            }
     }
 }
 
@@ -640,10 +689,12 @@ void CYPoolFFI(CYPool *pool, JSContextRef context, sig::Type *type, ffi_type *ff
             *reinterpret_cast<void **>(data) = CYCastPointer<void *>(context, value);
         break;
 
-        case sig::string_P:
-            _assert(pool != NULL);
-            *reinterpret_cast<const char **>(data) = CYPoolCString(*pool, context, value);
-        break;
+        case sig::string_P: {
+            bool guess(false);
+            *reinterpret_cast<const char **>(data) = CYCastPointer<const char *>(context, value, &guess);
+            if (guess && pool != NULL)
+                *reinterpret_cast<const char **>(data) = CYPoolCString(*pool, context, value);
+        } break;
 
         case sig::struct_P: {
             uint8_t *base(reinterpret_cast<uint8_t *>(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<char **>(data))
-                return CYCastJSValue(context, utf8);
+            if (char *pointer = *reinterpret_cast<char **>(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<Closure_privateData *>(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 *>(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<CString *>(JSObjectGetPrivate(object)));
+    char *string(static_cast<char *>(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<CString *>(JSObjectGetPrivate(object)));
+    char *string(static_cast<char *>(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<char *>(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<Type_privateData *>(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<CString *>(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<cy::Functor *>(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<CYValue *>(JSObjectGetPrivate(_this)));
     return CYCastJSValue(context, reinterpret_cast<uintptr_t>(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<CString *>(JSObjectGetPrivate(object)));
+    char *string(static_cast<char *>(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<Pointer *>(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<Pointer *>(JSObjectGetPrivate(_this)));
+    const char *string(static_cast<const char *>(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<Pointer *>(JSObjectGetPrivate(_this)));
+    const char *string(static_cast<const char *>(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<cy::Functor *>(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<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);
+    (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<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;
@@ -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<Context *>(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<CYFile *>(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<char *>(CYPoolFile(pool, path, &data.size));
+    return data;
+}
 
+static const char *CYPoolLibraryPath(CYPool &pool) {
     Dl_info addr;
-    _assert(dladdr(reinterpret_cast<void *>(&require), &addr) != 0);
+    _assert(dladdr(reinterpret_cast<void *>(&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<char *>(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<uintptr_t>(weak)));
+    }
+#endif
+
     if (CYBridgeEntry *entry = CYBridgeHash("1dlerror", 8))
         entry->cache_ = new cy::Functor(entry->value_, reinterpret_cast<void (*)()>(&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_);