]> git.saurik.com Git - cycript.git/blobdiff - libcycript.cy
Support requiring simple modules installed by npm.
[cycript.git] / libcycript.cy
index e9a612f04549da3156eb4d65b182c82ff74ab371..6a751aa38ddd2a2ebc7c52e1bda835d67c612d87 100644 (file)
@@ -1,3 +1,9 @@
+var process = {
+    env: {},
+};
+
+(function() {
+
 let $cy_set = function(object, properties) {
     for (const name in properties)
         Object.defineProperty(object, name, {
@@ -8,6 +14,17 @@ let $cy_set = function(object, properties) {
         });
 };
 
+const F_OK = 0;
+const X_OK = (1<<0);
+const W_OK = (1<<1);
+const R_OK = (1<<2);
+
+typedef long size_t;
+
+extern "C" int access(const char *path, int amode);
+extern "C" char *getcwd(char *buf, size_t size);
+extern "C" int getpid();
+
 $cy_set(Date.prototype, {
     toCYON: function() {
         return `new ${this.constructor.name}(${this.toUTCString().toCYON()})`;
@@ -19,3 +36,177 @@ $cy_set(Error.prototype, {
         return `new ${this.constructor.name}(${this.message.toCYON()})`;
     },
 });
+
+let IsFile = function(path) {
+    // XXX: this doesn't work on symlinks, but I don't want to fix stat :/
+    return access(path, F_OK) == 0 && access(path + '/', F_OK) == -1;
+};
+
+let StartsWith = function(lhs, rhs) {
+    return lhs.substring(0, rhs.length) == rhs;
+};
+
+let ResolveFile = function(exact, name) {
+    if (exact && IsFile(name))
+        return name;
+    for (let suffix of ['.js', '.json'])
+        if (IsFile(name + suffix))
+            return name + suffix;
+    return null;
+};
+
+
+let GetLibraryPath = function() {
+    let handle = dlopen("/usr/lib/libcycript.dylib", RTLD_NOLOAD);
+    if (handle == null)
+        return null;
+
+    try {
+        let CYHandleServer = dlsym(handle, "CYHandleServer");
+        if (CYHandleServer == null)
+            return null;
+
+        let info = new Dl_info;
+        if (dladdr(CYHandleServer, info) == 0)
+            return null;
+
+        let path = info->dli_fname;
+        let slash = path.lastIndexOf('/');
+        if (slash == -1)
+            return null;
+
+        return path.substr(0, slash);
+    } finally {
+        dlclose(handle);
+    }
+};
+
+let ResolveFolder = function(name) {
+    if (access(name + '/', F_OK) == -1)
+        return null;
+
+    if (IsFile(name + "/package.json")) {
+        let package = require(name + "/package.json");
+        let path = ResolveFile(true, name + "/" + package.main);
+        if (path != null)
+            return path;
+    }
+
+    return ResolveFile(false, name + "/index");
+};
+
+let ResolveEither = function(name) {
+    let path = null;
+    if (path == null)
+        path = ResolveFile(true, name);
+    if (path == null)
+        path = ResolveFolder(name);
+    return path;
+};
+
+require.resolve = function(name) {
+    if (StartsWith(name, '/')) {
+        let path = ResolveEither(name);
+        if (path != null)
+            return path;
+    } else {
+        let cwd = new (typedef char[1024]);
+        cwd = getcwd(cwd, cwd.length).toString();
+        cwd = cwd.split('/');
+
+        if (StartsWith(name, './') || StartsWith(name, '../')) {
+            let path = ResolveEither(cwd + '/' + name);
+            if (path != null)
+                return path;
+        } else {
+            for (let i = cwd.length; i != 0; --i) {
+                let modules = cwd.slice(0, i).concat("node_modules").join('/');
+                let path = ResolveEither(modules + "/" + name);
+                if (path != null)
+                    return path;
+            }
+
+            let library = GetLibraryPath();
+            let path = ResolveFile(true, library + "/cycript0.9/" + name + ".cy");
+            if (path != null)
+                return path;
+        }
+    }
+
+    throw new Error("Cannot find module '" + name + "'");
+};
+
+var bindings = {};
+
+process.binding = function(name) {
+    let binding = bindings[name];
+    if (typeof binding != 'undefined')
+        return binding;
+
+    switch (name) {
+        case 'buffer': binding = {
+            setupBufferJS() {
+            },
+        }; break;
+
+        case 'cares_wrap': binding = {
+        }; break;
+
+        case 'constants': binding = {
+        }; break;
+
+        case 'fs': binding = {
+            FSInitialize() {
+            },
+
+            lstat() {
+                throw new Error("stat(" + arguments[0] + ")");
+            },
+        }; break;
+
+        case 'pipe_wrap': binding = {
+        }; break;
+
+        case 'smalloc': binding = {
+            alloc() {
+            },
+        }; break;
+
+        case 'stream_wrap': binding = {
+        }; break;
+
+        case 'tcp_wrap': binding = {
+        }; break;
+
+        case 'timer_wrap': binding = {
+            kOnTimeout: 0,
+            Timer: {
+            },
+        }; break;
+
+        case 'tty_wrap': binding = {
+        }; break;
+
+        case 'uv': binding = {
+        }; break;
+
+        default:
+            throw new Error('No such module: ' + name);
+    }
+
+    bindings[name] = binding;
+    return binding;
+};
+
+let environ = *(typedef char ***)(dlsym(RTLD_DEFAULT, "environ"));
+for (let i = 0; environ[i] != null; ++i) {
+    let assign = environ[i];
+    let equal = assign.indexOf('=');
+    let name = assign.substr(0, equal);
+    let value = assign.substr(equal + 1);
+    process.env[name.toString()] = value;
+}
+
+process.pid = getpid();
+
+})();