1 /* Cycript - The Truly Universal Scripting Language
 
   2  * Copyright (C) 2009-2016  Jay Freeman (saurik)
 
   5 /* GNU Affero General Public License, Version 3 {{{ */
 
   7  * This program is free software: you can redistribute it and/or modify
 
   8  * it under the terms of the GNU Affero General Public License as published by
 
   9  * the Free Software Foundation, either version 3 of the License, or
 
  10  * (at your option) any later version.
 
  12  * This program is distributed in the hope that it will be useful,
 
  13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
  14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
  15  * GNU Affero General Public License for more details.
 
  17  * You should have received a copy of the GNU Affero General Public License
 
  18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
  28 this.typeid = function(object) {
 
  32 let $cy_set = function(object, properties) {
 
  33     for (const name in properties)
 
  34         Object.defineProperty(object, name, {
 
  38             value: properties[name],
 
  42 $cy_set(Boolean.prototype, {
 
  44         return `new Boolean(${this.toString()})`;
 
  48 $cy_set(Date.prototype, {
 
  50         return `new ${this.constructor.name}(${this.toUTCString().toCYON()})`;
 
  54 $cy_set(Error.prototype, {
 
  56         let stack = this.stack;
 
  57         if (typeof stack == 'undefined')
 
  60             stack = stack.split('\n');
 
  61             if (stack.slice(-1)[0] == "global code")
 
  62                 stack = stack.slice(0, -1);
 
  63             for (let i = 0; i != stack.length; ++i)
 
  64                 stack[i] = '\n    ' + stack[i];
 
  65             stack = stack.join('');
 
  66             stack = ` /*${stack} */`;
 
  68         return `new ${this.constructor.name}(${this.message.toCYON()})${stack}`;
 
  72 $cy_set(Number.prototype, {
 
  75             return `${this.$cyt.toCYON()}(${this.toString()})`;
 
  76         return `new Number(${this.toString()})`;
 
  80 $cy_set(RegExp.prototype, {
 
  82         return this.toString();
 
  86 if ("Java" in Cycript) {
 
  87     $cy_set(java.lang.Boolean.prototype, {
 
  89             return `new java.lang.Boolean(${this->value})`;
 
  93     $cy_set(java.lang.Byte.prototype, {
 
  95             return `new java.lang.Byte(${this->value})`;
 
  99     $cy_set(java.lang.Character.prototype, {
 
 101             return `new java.lang.Character(${this->value})`;
 
 105     $cy_set(java.lang.Short.prototype, {
 
 107             return `new java.lang.Short(${this->value})`;
 
 111     $cy_set(java.lang.Integer.prototype, {
 
 113             return `new java.lang.Integer(${this->value})`;
 
 117     $cy_set(java.lang.Long.prototype, {
 
 119             return `new java.lang.Long(${this->value})`;
 
 123     $cy_set(java.lang.Float.prototype, {
 
 125             return `new java.lang.Float(${this->value})`;
 
 129     $cy_set(java.lang.Double.prototype, {
 
 131             return `new java.lang.Double(${this->value})`;
 
 135     $cy_set(java.lang.Object.prototype, {
 
 136         toCYON: function(key) {
 
 137             return "#" + this.toString().toCYON();
 
 140         // XXX: due to lack of interface prototypes :(
 
 141         $cyg: function(key) {
 
 142             return this.get(key);
 
 145         // XXX: due to lack of interface prototypes :(
 
 146         $cys: function(key, value) {
 
 148                 this.set(key, value);
 
 150                 this.put(key, value);
 
 155 if ("ObjectiveC" in Cycript) {
 
 156     $cy_set(NSArray.prototype, {
 
 157         $cyg: function(key) {
 
 158             return objc_msgSend(this, "objectAtIndex:", key);
 
 161         $cys: function(key, value) {
 
 162             return objc_msgSend(this, "setObject:atIndex:", value, key);
 
 166     $cy_set(NSDictionary.prototype, {
 
 167         $cyg: function(key) {
 
 168             return objc_msgSend(this, "objectForKey:", key);
 
 171         $cys: function(key, value) {
 
 172             return objc_msgSend(this, "setObject:forKey:", value, key);
 
 177 let IsFile = function(path) {
 
 178     // XXX: this doesn't work on symlinks, but I don't want to fix stat :/
 
 179     return access(path, F_OK) == 0 && access(path + '/', F_OK) == -1;
 
 182 let StartsWith = function(lhs, rhs) {
 
 183     return lhs.substring(0, rhs.length) == rhs;
 
 186 let ResolveFile = function(exact, name) {
 
 187     if (exact && IsFile(name))
 
 189     for (let suffix of ['.js', '.json'])
 
 190         if (IsFile(name + suffix))
 
 191             return name + suffix;
 
 196 let GetLibraryPath = function() {
 
 197     let handle = dlopen("/usr/lib/libcycript.dylib", RTLD_NOLOAD);
 
 202         let CYHandleServer = dlsym(handle, "CYHandleServer");
 
 203         if (CYHandleServer == null)
 
 206         let info = new Dl_info;
 
 207         if (dladdr(CYHandleServer, info) == 0)
 
 210         let path = info->dli_fname;
 
 211         let slash = path.lastIndexOf('/');
 
 215         path = path.substr(0, slash);
 
 217         GetLibraryPath = function() {
 
 221         return GetLibraryPath();
 
 227 let ResolveFolder = function(name) {
 
 228     if (access(name + '/', F_OK) == -1)
 
 231     if (IsFile(name + "/package.json")) {
 
 232         let package = require(name + "/package.json");
 
 233         let path = ResolveFile(true, name + "/" + package.main);
 
 238     return ResolveFile(false, name + "/index");
 
 241 let ResolveEither = function(name) {
 
 244         path = ResolveFile(true, name);
 
 246         path = ResolveFolder(name);
 
 250 require.resolve = function(name) {
 
 251     if (StartsWith(name, '/')) {
 
 252         let path = ResolveEither(name);
 
 256         let cwd = new (typedef char[1024]);
 
 257         cwd = getcwd(cwd, cwd.length).toString();
 
 258         cwd = cwd.split('/');
 
 260         if (StartsWith(name, './') || StartsWith(name, '../')) {
 
 261             let path = ResolveEither(cwd + '/' + name);
 
 265             for (let i = cwd.length; i != 0; --i) {
 
 266                 let modules = cwd.slice(0, i).concat("node_modules").join('/');
 
 267                 let path = ResolveEither(modules + "/" + name);
 
 272             let library = GetLibraryPath();
 
 273             let path = ResolveFile(true, library + "/cycript0.9/" + name + ".cy");
 
 279     throw new Error("Cannot find module '" + name + "'");
 
 282 var _syscall = function(value) {
 
 284         throw new Error(strerror(errno));
 
 287 var info = *new (struct stat);
 
 289 } else if ("st_atim" in info) {
 
 290     var st_atime = "st_atim";
 
 291     var st_mtime = "st_mtim";
 
 292     var st_ctime = "st_ctim";
 
 293 } else if ("st_atimespec" in info) {
 
 294     var st_atime = "st_atimespec";
 
 295     var st_mtime = "st_mtimespec";
 
 296     var st_ctime = "st_ctimespec";
 
 298     var st_atime = "st_atime";
 
 299     var st_mtime = "st_mtime";
 
 300     var st_ctime = "st_ctime";
 
 303 var toDate = function(timespec) {
 
 304     return new Date(timespec.tv_sec * 1000 + timespec.tv_nsec / 1000);
 
 309 process.binding = function(name) {
 
 310     let binding = bindings[name];
 
 311     if (typeof binding != 'undefined')
 
 315         case 'buffer': binding = {
 
 320         case 'cares_wrap': binding = {
 
 323         case 'constants': binding = {
 
 326         case 'fs': binding = {
 
 331                 var info = new (struct stat);
 
 332                 _syscall(lstat(path, info));
 
 337                     nlink: info->st_nlink,
 
 341                     blksize: info->st_blksize,
 
 344                     blocks: info->st_blocks,
 
 346                     atime: toDate(info->[st_atime]),
 
 347                     mtime: toDate(info->[st_mtime]),
 
 348                     ctime: toDate(info->[st_ctime]),
 
 351                         return S_ISLNK(this.mode);
 
 357         case 'pipe_wrap': binding = {
 
 360         case 'smalloc': binding = {
 
 365         case 'stream_wrap': binding = {
 
 368         case 'tcp_wrap': binding = {
 
 371         case 'timer_wrap': binding = {
 
 377         case 'tty_wrap': binding = {
 
 380         case 'uv': binding = {
 
 384             throw new Error('No such module: ' + name);
 
 387     bindings[name] = binding;
 
 391 let environ = *(typedef char ***)(dlsym(RTLD_DEFAULT, "environ"));
 
 392 for (let i = 0; environ[i] != null; ++i) {
 
 393     let assign = environ[i];
 
 394     let equal = assign.indexOf('=');
 
 395     let name = assign.substr(0, equal);
 
 396     let value = assign.substr(equal + 1);
 
 397     process.env[name.toString()] = value;
 
 400 process.pid = getpid();