]> git.saurik.com Git - cycript.git/blob - libcycript.cy
f5f63ea99bab3894649f54b6a24c9b8659e852a6
[cycript.git] / libcycript.cy
1 /* Cycript - Optimizing JavaScript Compiler/Runtime
2 * Copyright (C) 2009-2015 Jay Freeman (saurik)
3 */
4
5 /* GNU Affero General Public License, Version 3 {{{ */
6 /*
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.
11
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.
16
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/>.
19 **/
20 /* }}} */
21
22 var process = {
23 env: {},
24 };
25
26 (function() {
27
28 this.typeid = function(object) {
29 return object.$cyt;
30 };
31
32 let $cy_set = function(object, properties) {
33 for (const name in properties)
34 Object.defineProperty(object, name, {
35 configurable: true,
36 enumerable: false,
37 writable: true,
38 value: properties[name],
39 });
40 };
41
42 $cy_set(Boolean.prototype, {
43 toCYON: function() {
44 return `new Boolean(${this.toString()})`;
45 },
46 });
47
48 $cy_set(Date.prototype, {
49 toCYON: function() {
50 return `new ${this.constructor.name}(${this.toUTCString().toCYON()})`;
51 },
52 });
53
54 $cy_set(Error.prototype, {
55 toCYON: function() {
56 let stack = this.stack;
57 if (typeof stack == 'undefined')
58 stack = '';
59 else {
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} */`;
67 }
68 return `new ${this.constructor.name}(${this.message.toCYON()})${stack}`;
69 },
70 });
71
72 $cy_set(Number.prototype, {
73 toCYON: function() {
74 if ("$cyt" in this)
75 return `${this.$cyt.toCYON()}(${this.toString()})`;
76 return `new Number(${this.toString()})`;
77 },
78 });
79
80 $cy_set(RegExp.prototype, {
81 toCYON: function() {
82 return this.toString();
83 },
84 });
85
86 if ("Java" in Cycript) {
87 $cy_set(java.lang.Boolean.prototype, {
88 toCYON: function() {
89 return `new java.lang.Boolean(${this->value})`;
90 },
91 });
92
93 $cy_set(java.lang.Byte.prototype, {
94 toCYON: function() {
95 return `new java.lang.Byte(${this->value})`;
96 },
97 });
98
99 $cy_set(java.lang.Character.prototype, {
100 toCYON: function() {
101 return `new java.lang.Character(${this->value})`;
102 },
103 });
104
105 $cy_set(java.lang.Short.prototype, {
106 toCYON: function() {
107 return `new java.lang.Short(${this->value})`;
108 },
109 });
110
111 $cy_set(java.lang.Integer.prototype, {
112 toCYON: function() {
113 return `new java.lang.Integer(${this->value})`;
114 },
115 });
116
117 $cy_set(java.lang.Long.prototype, {
118 toCYON: function() {
119 return `new java.lang.Long(${this->value})`;
120 },
121 });
122
123 $cy_set(java.lang.Float.prototype, {
124 toCYON: function() {
125 return `new java.lang.Float(${this->value})`;
126 },
127 });
128
129 $cy_set(java.lang.Double.prototype, {
130 toCYON: function() {
131 return `new java.lang.Double(${this->value})`;
132 },
133 });
134
135 $cy_set(java.lang.Object.prototype, {
136 // XXX: due to lack of interface prototypes :(
137 $cyg: function(key) {
138 return this.get(key);
139 },
140
141 // XXX: due to lack of interface prototypes :(
142 $cys: function(key, value) {
143 if ("set" in this)
144 this.set(key, value);
145 else
146 this.put(key, value);
147 },
148 });
149 }
150
151 if ("ObjectiveC" in Cycript) {
152 $cy_set(NSArray.prototype, {
153 $cyg: function(key) {
154 return objc_msgSend(this, "objectAtIndex:", key);
155 },
156
157 $cys: function(key, value) {
158 return objc_msgSend(this, "setObject:atIndex:", value, key);
159 },
160 });
161
162 $cy_set(NSDictionary.prototype, {
163 $cyg: function(key) {
164 return objc_msgSend(this, "objectForKey:", key);
165 },
166
167 $cys: function(key, value) {
168 return objc_msgSend(this, "setObject:forKey:", value, key);
169 },
170 });
171 }
172
173 let IsFile = function(path) {
174 // XXX: this doesn't work on symlinks, but I don't want to fix stat :/
175 return access(path, F_OK) == 0 && access(path + '/', F_OK) == -1;
176 };
177
178 let StartsWith = function(lhs, rhs) {
179 return lhs.substring(0, rhs.length) == rhs;
180 };
181
182 let ResolveFile = function(exact, name) {
183 if (exact && IsFile(name))
184 return name;
185 for (let suffix of ['.js', '.json'])
186 if (IsFile(name + suffix))
187 return name + suffix;
188 return null;
189 };
190
191
192 let GetLibraryPath = function() {
193 let handle = dlopen("/usr/lib/libcycript.dylib", RTLD_NOLOAD);
194 if (handle == null)
195 return null;
196
197 try {
198 let CYHandleServer = dlsym(handle, "CYHandleServer");
199 if (CYHandleServer == null)
200 return null;
201
202 let info = new Dl_info;
203 if (dladdr(CYHandleServer, info) == 0)
204 return null;
205
206 let path = info->dli_fname;
207 let slash = path.lastIndexOf('/');
208 if (slash == -1)
209 return null;
210
211 path = path.substr(0, slash);
212
213 GetLibraryPath = function() {
214 return path;
215 };
216
217 return GetLibraryPath();
218 } finally {
219 dlclose(handle);
220 }
221 };
222
223 let ResolveFolder = function(name) {
224 if (access(name + '/', F_OK) == -1)
225 return null;
226
227 if (IsFile(name + "/package.json")) {
228 let package = require(name + "/package.json");
229 let path = ResolveFile(true, name + "/" + package.main);
230 if (path != null)
231 return path;
232 }
233
234 return ResolveFile(false, name + "/index");
235 };
236
237 let ResolveEither = function(name) {
238 let path = null;
239 if (path == null)
240 path = ResolveFile(true, name);
241 if (path == null)
242 path = ResolveFolder(name);
243 return path;
244 };
245
246 require.resolve = function(name) {
247 if (StartsWith(name, '/')) {
248 let path = ResolveEither(name);
249 if (path != null)
250 return path;
251 } else {
252 let cwd = new (typedef char[1024]);
253 cwd = getcwd(cwd, cwd.length).toString();
254 cwd = cwd.split('/');
255
256 if (StartsWith(name, './') || StartsWith(name, '../')) {
257 let path = ResolveEither(cwd + '/' + name);
258 if (path != null)
259 return path;
260 } else {
261 for (let i = cwd.length; i != 0; --i) {
262 let modules = cwd.slice(0, i).concat("node_modules").join('/');
263 let path = ResolveEither(modules + "/" + name);
264 if (path != null)
265 return path;
266 }
267
268 let library = GetLibraryPath();
269 let path = ResolveFile(true, library + "/cycript0.9/" + name + ".cy");
270 if (path != null)
271 return path;
272 }
273 }
274
275 throw new Error("Cannot find module '" + name + "'");
276 };
277
278 var _syscall = function(value) {
279 if (value == -1)
280 throw new Error(strerror(errno));
281 };
282
283 var info = *new (struct stat);
284 if (false) {
285 } else if ("st_atim" in info) {
286 var st_atime = "st_atim";
287 var st_mtime = "st_mtim";
288 var st_ctime = "st_ctim";
289 } else if ("st_atimespec" in info) {
290 var st_atime = "st_atimespec";
291 var st_mtime = "st_mtimespec";
292 var st_ctime = "st_ctimespec";
293 } else {
294 var st_atime = "st_atime";
295 var st_mtime = "st_mtime";
296 var st_ctime = "st_ctime";
297 }
298
299 var toDate = function(timespec) {
300 return new Date(timespec.tv_sec * 1000 + timespec.tv_nsec / 1000);
301 };
302
303 var bindings = {};
304
305 process.binding = function(name) {
306 let binding = bindings[name];
307 if (typeof binding != 'undefined')
308 return binding;
309
310 switch (name) {
311 case 'buffer': binding = {
312 setupBufferJS() {
313 },
314 }; break;
315
316 case 'cares_wrap': binding = {
317 }; break;
318
319 case 'constants': binding = {
320 }; break;
321
322 case 'fs': binding = {
323 FSInitialize() {
324 },
325
326 lstat(path) {
327 var info = new (struct stat);
328 _syscall(lstat(path, info));
329
330 return {
331 dev: info->st_dev,
332 mode: info->st_mode,
333 nlink: info->st_nlink,
334 uid: info->st_uid,
335 gid: info->st_gid,
336 rdev: info->st_rdev,
337 blksize: info->st_blksize,
338 ino: info->st_ino,
339 size: info->st_size,
340 blocks: info->st_blocks,
341
342 atime: toDate(info->[st_atime]),
343 mtime: toDate(info->[st_mtime]),
344 ctime: toDate(info->[st_ctime]),
345
346 isSymbolicLink() {
347 return S_ISLNK(this.mode);
348 },
349 };
350 },
351 }; break;
352
353 case 'pipe_wrap': binding = {
354 }; break;
355
356 case 'smalloc': binding = {
357 alloc() {
358 },
359 }; break;
360
361 case 'stream_wrap': binding = {
362 }; break;
363
364 case 'tcp_wrap': binding = {
365 }; break;
366
367 case 'timer_wrap': binding = {
368 kOnTimeout: 0,
369 Timer: {
370 },
371 }; break;
372
373 case 'tty_wrap': binding = {
374 }; break;
375
376 case 'uv': binding = {
377 }; break;
378
379 default:
380 throw new Error('No such module: ' + name);
381 }
382
383 bindings[name] = binding;
384 return binding;
385 };
386
387 let environ = *(typedef char ***)(dlsym(RTLD_DEFAULT, "environ"));
388 for (let i = 0; environ[i] != null; ++i) {
389 let assign = environ[i];
390 let equal = assign.indexOf('=');
391 let name = assign.substr(0, equal);
392 let value = assign.substr(equal + 1);
393 process.env[name.toString()] = value;
394 }
395
396 process.pid = getpid();
397
398 })();