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             if (stack.length == 0)
 
  68                 stack = stack.join('');
 
  69                 stack = ` /*${stack} */`;
 
  72         return `new ${this.constructor.name}(${this.message.toCYON()})${stack}`;
 
  76 $cy_set(Number.prototype, {
 
  79             return `${this.$cyt.toCYON()}(${this.toString()})`;
 
  80         return `new Number(${this.toString()})`;
 
  84 $cy_set(RegExp.prototype, {
 
  86         return this.toString();
 
  90 // XXX: Java should just always be available
 
  91 if ("Java" in Cycript) {
 
  93 // XXX: this is a half-assed EventEmitter
 
  94 // XXX: this doesn't even have the same semantics
 
  98 Java.on = function(event, handler) {
 
 100     if (event in this.handlers_)
 
 101         handlers = this.handlers_[event];
 
 104         this.handlers_[event] = handlers;
 
 107     if (this.handlers_ == null)
 
 110         handlers.push(handler);
 
 113 Java.emit = function(event) {
 
 114     if (event in this.handlers_) {
 
 115         var handlers = this.handlers_[event];
 
 116         if (handlers != null)
 
 117             for (var handler of handlers)
 
 121     this.handlers_[event] = null;
 
 124 Java.on('setup', function() {
 
 125     $cy_set(java.lang.Boolean.prototype, {
 
 127             return `new java.lang.Boolean(${this->value})`;
 
 131     $cy_set(java.lang.Byte.prototype, {
 
 133             return `new java.lang.Byte(${this->value})`;
 
 137     $cy_set(java.lang.Character.prototype, {
 
 139             return `new java.lang.Character(${this->value})`;
 
 143     $cy_set(java.lang.Short.prototype, {
 
 145             return `new java.lang.Short(${this->value})`;
 
 149     $cy_set(java.lang.Integer.prototype, {
 
 151             return `new java.lang.Integer(${this->value})`;
 
 155     $cy_set(java.lang.Long.prototype, {
 
 157             return `new java.lang.Long(${this->value})`;
 
 161     $cy_set(java.lang.Float.prototype, {
 
 163             return `new java.lang.Float(${this->value})`;
 
 167     $cy_set(java.lang.Double.prototype, {
 
 169             return `new java.lang.Double(${this->value})`;
 
 173     $cy_set(java.lang.Object.prototype, {
 
 174         toCYON: function(key) {
 
 175             return "#" + this.toString().toCYON();
 
 178         // XXX: due to lack of interface prototypes :(
 
 179         $cyg: function(key) {
 
 180             return this.get(key);
 
 183         // XXX: due to lack of interface prototypes :(
 
 184         $cys: function(key, value) {
 
 186                 this.set(key, value);
 
 188                 this.put(key, value);
 
 195 if ("ObjectiveC" in Cycript) {
 
 196     $cy_set(NSArray.prototype, {
 
 197         $cyg: function(key) {
 
 198             return objc_msgSend(this, "objectAtIndex:", key);
 
 201         $cys: function(key, value) {
 
 202             return objc_msgSend(this, "setObject:atIndex:", value, key);
 
 206     $cy_set(NSDictionary.prototype, {
 
 207         $cyg: function(key) {
 
 208             return objc_msgSend(this, "objectForKey:", key);
 
 211         $cys: function(key, value) {
 
 212             return objc_msgSend(this, "setObject:forKey:", value, key);
 
 217 let IsFile = function(path) {
 
 218     // XXX: this doesn't work on symlinks, but I don't want to fix stat :/
 
 219     return access(path, F_OK) == 0 && access(path + '/', F_OK) == -1;
 
 222 let StartsWith = function(lhs, rhs) {
 
 223     return lhs.substring(0, rhs.length) == rhs;
 
 226 let ResolveFile = function(exact, name) {
 
 227     if (exact && IsFile(name))
 
 229     for (let suffix of ['.js', '.json'])
 
 230         if (IsFile(name + suffix))
 
 231             return name + suffix;
 
 236 let GetLibraryPath = function() {
 
 237     let handle = dlopen("/usr/lib/libcycript.dylib", RTLD_NOLOAD);
 
 242         let CYHandleServer = dlsym(handle, "CYHandleServer");
 
 243         if (CYHandleServer == null)
 
 246         let info = new Dl_info;
 
 247         if (dladdr(CYHandleServer, info) == 0)
 
 250         let path = info->dli_fname;
 
 251         let slash = path.lastIndexOf('/');
 
 255         path = path.substr(0, slash);
 
 257         GetLibraryPath = function() {
 
 261         return GetLibraryPath();
 
 267 let ResolveFolder = function(name) {
 
 268     if (access(name + '/', F_OK) == -1)
 
 271     if (IsFile(name + "/package.json")) {
 
 272         let package = require(name + "/package.json");
 
 273         let path = ResolveFile(true, name + "/" + package.main);
 
 278     return ResolveFile(false, name + "/index");
 
 281 let ResolveEither = function(name) {
 
 284         path = ResolveFile(true, name);
 
 286         path = ResolveFolder(name);
 
 290 require.resolve = function(name) {
 
 291     if (StartsWith(name, '/')) {
 
 292         let path = ResolveEither(name);
 
 296         let cwd = new (typedef char[1024]);
 
 297         cwd = getcwd(cwd, cwd.length).toString();
 
 298         cwd = cwd.split('/');
 
 300         if (StartsWith(name, './') || StartsWith(name, '../')) {
 
 301             let path = ResolveEither(cwd + '/' + name);
 
 305             for (let i = cwd.length; i != 0; --i) {
 
 306                 let modules = cwd.slice(0, i).concat("node_modules").join('/');
 
 307                 let path = ResolveEither(modules + "/" + name);
 
 312             let library = GetLibraryPath();
 
 313             let path = ResolveFile(true, library + "/cycript0.9/" + name + ".cy");
 
 319     throw new Error("Cannot find module '" + name + "'");
 
 322 var _syscall = function(value) {
 
 324         throw new Error(strerror(errno));
 
 327 var info = *new (struct stat);
 
 329 } else if ("st_atim" in info) {
 
 330     var st_atime = "st_atim";
 
 331     var st_mtime = "st_mtim";
 
 332     var st_ctime = "st_ctim";
 
 333 } else if ("st_atimespec" in info) {
 
 334     var st_atime = "st_atimespec";
 
 335     var st_mtime = "st_mtimespec";
 
 336     var st_ctime = "st_ctimespec";
 
 338     var st_atime = "st_atime";
 
 339     var st_mtime = "st_mtime";
 
 340     var st_ctime = "st_ctime";
 
 343 var toDate = function(timespec) {
 
 344     return new Date(timespec.tv_sec * 1000 + timespec.tv_nsec / 1000);
 
 349 process.binding = function(name) {
 
 350     let binding = bindings[name];
 
 351     if (typeof binding != 'undefined')
 
 355         case 'buffer': binding = {
 
 360         case 'cares_wrap': binding = {
 
 363         case 'constants': binding = {
 
 366         case 'fs': binding = {
 
 371                 var info = new (struct stat);
 
 372                 _syscall(lstat(path, info));
 
 377                     nlink: info->st_nlink,
 
 381                     blksize: info->st_blksize,
 
 384                     blocks: info->st_blocks,
 
 386                     atime: toDate(info->[st_atime]),
 
 387                     mtime: toDate(info->[st_mtime]),
 
 388                     ctime: toDate(info->[st_ctime]),
 
 391                         return S_ISLNK(this.mode);
 
 397         case 'pipe_wrap': binding = {
 
 400         case 'smalloc': binding = {
 
 405         case 'stream_wrap': binding = {
 
 408         case 'tcp_wrap': binding = {
 
 411         case 'timer_wrap': binding = {
 
 417         case 'tty_wrap': binding = {
 
 420         case 'uv': binding = {
 
 424             throw new Error('No such module: ' + name);
 
 427     bindings[name] = binding;
 
 431 let environ = *(typedef char ***)(dlsym(RTLD_DEFAULT, "environ"));
 
 432 for (let i = 0; environ[i] != null; ++i) {
 
 433     let assign = environ[i];
 
 434     let equal = assign.indexOf('=');
 
 435     let name = assign.substr(0, equal);
 
 436     let value = assign.substr(equal + 1);
 
 437     process.env[name.toString()] = value;
 
 440 process.cwd = function() {
 
 441     let cwd = new (typedef char[1024]);
 
 442     return getcwd(cwd, cwd.length).toString();
 
 445 process.pid = getpid();