28 |
29 | namespace sig {
30 |
31 | typedef void (*Callback)(apr_pool_t *pool, Type *&type);
32 | void Parse(apr_pool_t *pool, struct Signature *signature, const char *name, Callback callback);
33 |
34 | const char *Unparse(apr_pool_t *pool, struct Signature *signature);
35 | const char *Unparse(apr_pool_t *pool, struct Type *type);
36 |
37 | void Copy(apr_pool_t *pool, Type &lhs, Type &rhs);
38 | void Copy(apr_pool_t *pool, Signature &lhs, Signature &rhs);
39 | void Copy(apr_pool_t *pool, Type &lhs, Type &rhs);
40 |
41 | }
42 |
43 | #endif/*SIG_PARSE_H*/
44 |
--------------------------------------------------------------------------------
/sig/types.hpp:
--------------------------------------------------------------------------------
1 | /* Cycript - Optimizing JavaScript Compiler/Runtime
2 | * Copyright (C) 2009-2010 Jay Freeman (saurik)
3 | */
4 |
5 | /* GNU Lesser General Public License, Version 3 {{{ */
6 | /*
7 | * Cycript is free software: you can redistribute it and/or modify it under
8 | * the terms of the GNU Lesser General Public License as published by the
9 | * Free Software Foundation, either version 3 of the License, or (at your
10 | * option) any later version.
11 | *
12 | * Cycript is distributed in the hope that it will be useful, but WITHOUT
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 | * License for more details.
16 | *
17 | * You should have received a copy of the GNU Lesser General Public License
18 | * along with Cycript. If not, see .
19 | **/
20 | /* }}} */
21 |
22 | #ifndef SIG_TYPES_H
23 | #define SIG_TYPES_H
24 |
25 | #include "Standard.hpp"
26 |
27 | namespace sig {
28 |
29 | enum Primitive {
30 | typename_P = '#',
31 | union_P = '(',
32 | string_P = '*',
33 | selector_P = ':',
34 | block_P = '?',
35 | object_P = 'W',
36 | boolean_P = 'B',
37 | uchar_P = 'C',
38 | uint_P = 'I',
39 | ulong_P = 'L',
40 | ulonglong_P = 'Q',
41 | ushort_P = 'S',
42 | array_P = '[',
43 | pointer_P = '^',
44 | bit_P = 'b',
45 | char_P = 'c',
46 | double_P = 'd',
47 | float_P = 'f',
48 | int_P = 'i',
49 | long_P = 'l',
50 | longlong_P = 'q',
51 | short_P = 's',
52 | void_P = 'v',
53 | struct_P = '{'
54 | };
55 |
56 | struct Element {
57 | char *name;
58 | struct Type *type;
59 | size_t offset;
60 | };
61 |
62 | struct Signature {
63 | struct Element *elements;
64 | size_t count;
65 | };
66 |
67 | #define JOC_TYPE_INOUT (1 << 0)
68 | #define JOC_TYPE_IN (1 << 1)
69 | #define JOC_TYPE_BYCOPY (1 << 2)
70 | #define JOC_TYPE_OUT (1 << 3)
71 | #define JOC_TYPE_BYREF (1 << 4)
72 | #define JOC_TYPE_CONST (1 << 5)
73 | #define JOC_TYPE_ONEWAY (1 << 6)
74 |
75 | struct Type {
76 | enum Primitive primitive;
77 | char *name;
78 | uint8_t flags;
79 |
80 | union {
81 | struct {
82 | struct Type *type;
83 | size_t size;
84 | } data;
85 |
86 | struct Signature signature;
87 | } data;
88 | };
89 |
90 | struct Type *joc_parse_type(char **name, char eos, bool variable, bool signature);
91 | void joc_parse_signature(struct Signature *signature, char **name, char eos, bool variable);
92 |
93 | _finline bool IsAggregate(Primitive primitive) {
94 | return primitive == struct_P || primitive == union_P;
95 | }
96 |
97 | }
98 |
99 | #endif/*SIG_TYPES_H*/
100 |
--------------------------------------------------------------------------------
/todo.txt:
--------------------------------------------------------------------------------
1 | lol
2 | unicode identifier support (native and \u)
3 | support unions (right now 0-1 fields parsed as struct)
4 | look into what String is, and whether to bridge it
5 | think about bridging NSNumber with Number prototype
6 | some JS callbacks don't use exception pointers at all...
7 | a newline needs to not be allowed after a unary *
8 | finish implementing default xml namespace statement
9 | encode newlines in history for later replay (psql uses ^A)
10 |
11 | consider replacing regex literals with constructors
12 | https://bugzilla.mozilla.org/show_bug.cgi?id=98409
13 | numerification needs to use specific precision values
14 | https://bugzilla.mozilla.org/show_bug.cgi?id=5856
15 | consider a mode where unicode string content is saved
16 | https://bugzilla.mozilla.org/show_bug.cgi?id=274152
17 |
18 | NSDictionaries that have NSNumber keys don't getProperty
19 | errors in another process aren't displayed; to fix this, parse errors should get converted to exceptions and thrown
20 | CYPoolTry/Catch now carefully save the exception after it /no longer needs the exception/... uhh... wtf?
21 | throw CYJSError should probably be replaced with CYThrow() across the board
22 | figure out what to do about global context refs: I really really want to retain the bastards
23 | the concept of NULL pooling is entirely incorrect and sad... bad... evil... need to work on this... really
24 | NSArray's .toString() and .toLocaleString() fail hard, as Array.prototype.to*String are Array-specific
25 | applyOnMainThread, when done at console, loops the cyonifier
26 | special work needs to be done to correctly handle the "arguments" symbol: Declare("arguments", ...Special)
27 | at the Program level I seem to be eating away all of the var statements
28 | function pointers are ?; note that blocks are currently block_P = '?'
29 | I should probably attempt to use the auto_ flag somehow to not do contexts_ push when compiling
30 | Object_callAsFunction_toCYON should be implemented
31 |
32 | [NSString stringWithString:""] crashes, on linux, not on mac
33 | GS #defines should be _finline
34 |
35 | replace procmod g+s with gdb's macosx_get_task_for_pid_rights
36 | non-local return prologue is not being Replace()d: multipass compiler!
37 | interpretation of documentation comments should be compiler-only and off by default
38 | don't ever generate $ CYWith, in particular for CYLet... use CYFunctionExpression
39 |
--------------------------------------------------------------------------------
/trampoline.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | shopt -s extglob
4 |
5 | hpp=$1
6 | object=$2
7 | name=$3
8 | sed=$4
9 | otool=$5
10 | lipo=$6
11 | nm=$7
12 | shift 7
13 |
14 | #shift 1
15 | #set /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/gcc-4.2 -I/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.1.3.sdk/usr/include "$@"
16 |
17 | "$@"
18 |
19 | detailed=$(lipo -detailed_info "${object}")
20 |
21 | {
22 |
23 | echo '#include "Trampoline.hpp"'
24 |
25 | for arch in $(echo "${detailed}" | "${sed}" -e '/^architecture / { s/^architecture //; p; }; d;'); do
26 | offset=$(echo "${detailed}" | "${sed}" -e '
27 | /^architecture / { x; s/.*/0/; x; };
28 | /^architecture '${arch}'$/ { x; s/.*/1/; x; };
29 | x; /^1$/ { x; /^ *offset / { s/^ *offset //; p; }; x; }; x;
30 | d;
31 | ')
32 |
33 | file=($("${otool}" -arch "${arch}" -l "${object}" | "${sed}" -e '
34 | x; /^1$/ { x;
35 | /^ *fileoff / { s/^.* //; p; };
36 | /^ *filesize / { s/^.* //; p; };
37 | x; }; x;
38 |
39 | /^ *cmd LC_SEGMENT/ { x; s/.*/1/; x; };
40 |
41 | d;
42 | '))
43 |
44 | fileoff=${file[0]}
45 | filesize=${file[1]}
46 |
47 | echo
48 | echo "static const char ${name}_${arch}_data_[] = {"
49 |
50 | od -v -t x1 -t c -j "$((offset + fileoff))" -N "${filesize}" "${object}" | "${sed}" -e '
51 | /^[0-7]/ ! {
52 | s@^ @// @;
53 | s/\(....\)/ \1/g;
54 | s@^ // @//@;
55 | s/ *$/,/;
56 | };
57 |
58 | /^[0-7]/ {
59 | s/^[^ ]*//;
60 | s/ */ /g;
61 | s/^ *//;
62 | s/ $//;
63 | s/ /,/g;
64 | s/\([^,][^,]\)/0x\1/g;
65 | s/$/,/;
66 | /^,$/ ! { s/^/ /g; p; }; d;
67 | };
68 | '
69 |
70 | echo "};"
71 |
72 | echo
73 | entry=$("${nm}" -arch "${arch}" "${object}" | "${sed}" -e '/ _Start$/ { s/ .*//; p; }; d;')
74 | entry=${entry##*(0)}
75 | echo "static size_t ${name}_${arch}_entry_ = 0x${entry:=0};"
76 |
77 | echo
78 | echo "/*"
79 | "${otool}" -vVt -arch "${arch}" "${object}"
80 | echo "*/"
81 |
82 | echo
83 | echo "static Trampoline ${name}_${arch}_ = {"
84 | echo " ${name}_${arch}_data_,"
85 | echo " sizeof(${name}_${arch}_data_),"
86 | echo " ${name}_${arch}_entry_,"
87 | echo "};"
88 | done
89 |
90 | } >"${hpp}"
91 |
92 | #rm -f "${object}"
93 |
--------------------------------------------------------------------------------
/website/index.html:
--------------------------------------------------------------------------------
1 | Cycript
2 |
3 |
4 | Cycript: Objective-JavaScript
5 |
6 | What is Cycript?
7 |
8 | A programming language designed to blend the barrier between Objective-C and JavaScript. This project has similar goals to JSCocoa, but a very different set of starting technologies and a different guiding philosophy. In particular, Cycript has started life with a full-blown JavaScript parser/serializer, allowing it to have interesting hybrid syntax without constraints (such as those imposed on JSCocoa by JSLint).
9 |
10 | Is it done?
11 |
12 | Well, it works ;P. It is still "in flux": core language features are changing every few hours. However, it has already changed the workflow of the "elite" iPhone developers that write most of the extensions you see in Cydia: having a language that changes doesn't matter when you are mostly using it at the immediate console. I'm hoping, however, that I manage tolock it into something that feels "correct" in the very near future.
13 |
14 | How do you pronounce "Cycript"?
15 |
16 | I pronounce it "sscript" (with a geminate, aka long, 's'). I doubt anyone else will pronounce it like this, but I have my hopes.
17 |
18 | Where do I get it?
19 |
20 | Right now you can find releases of it at: http://www.cycript.org/debs/. This package depends on MobileSubstrate and libffi (both of which are in Cydia).
21 |
22 | So, how do I use it?!
23 |
24 | Although you can write full applications in Cycript, the fastest way to get playing with it is via the immediate console: just type "cycript".
25 |
26 |
iPhone:~$ cycript
27 | cy#
28 |
29 | Code typed at this prompt will be executed as it is able to be parsed: the immediate console is trying to eagerly parse lines of code as they come in (and thereby is not subject to automatic-semicolon insertion, for those JavaScript experts). Parse errors will be noted to the output in a hopefully useful fashion.
30 |
31 | cy# function a() {
32 | cy> a + q r
33 | | .........^
34 | | syntax error, unexpected Identifier, expecting ; or "\n"
35 | cy#
36 |
37 | It should be noted that it is possible that you will manage to break my JavaScript serializer. In these cases, parse errors may be thrown by the underlying JavaScript engine rather than Cycript. To debug these issues you can use the special console command ?debug.
38 |
39 | cy# ?debug
40 | debug == true
41 | cy# var a = ((0 + (1)) * (2 * 3)) + m['a']('\'')
42 | var a=(0+1)*(2*3)+m.a("'");
43 | ...
44 |
45 | In addition to standard JavaScript, you an also access anything in the Objective-C runtime. Attempts have been made, sparingly, to bridge syntax when possible between the two environments. In particular, you may notice interesting properties of arrays, dictonaries, strings, and numbers. Care has been taken to minimize the damage to the object model.
46 |
47 | cy# var a = [NSMutableArray arrayWithCapacity:4]
48 | cy# a instanceof Array
49 | true
50 | cy# [a class]
51 | "NSCFArray"
52 | cy# [a addObject:"hello"]; a
53 | ["hello"]
54 | cy# a[1] = 4; a.push(10); a
55 | ["hello",4,10]
56 | cy# a.splice(1, 1, 6, 7); a
57 | ["hello",6,7,10]
58 | cy# b = [1, 2]; [b replaceObjectAtIndex:0 withObject:5]; b
59 | [5,2]
60 |
61 | Memory management is mostly automatic, but instead of using the usual -[alloc] message you will need to use JavaScript's "new" operator, which returns a special "uninitialized" handle that can be used to send a single message (probably a form of init) before it "expires" and reverts to nil.
62 |
63 | cy# var a = new NSMutableDictionary
64 | cy# a
65 | "*** -[NSCFDictionary count]: method sent to an uninitialized mutable dictionary object"
66 | cy# var b = [a init]; b
67 | {}
68 | cy# a
69 | nil
70 | cy# var q = [new NSString init]; q
71 | ""
72 |
73 | One note in particular is made about selectors. Not only do they act as in Objective-C, including being typed using @selector notation, but they also have Function.prototype in their prototype-chain, allowing you to use them in interesting functional ways ala JavaScript. You can also generate one from a string using new Selector().
74 |
75 | cy# var sel = @selector(initWithFrame:)
76 | cy# sel
77 | @selector(initWithFrame:)
78 | cy# sel.call(new UIView, [UIHardware fullScreenApplicationContentRect])
79 | ">"
80 | cy# new Selector("initWithFrame:")
81 | @selector(initWithFrame:)
82 |
83 | As one would expect from JavaScript, objects have a property called constructor that references their class. You can also add methods along the prototype chain to instances. Eventually, all objects go through Instance, where you can put functions that should be available for all Objective-C classes.
84 |
85 | cy# Instance.prototype.getMethod = function (sel) { return class_getInstanceMethod(this, sel); }
86 | {}
87 | cy# NSObject.getMethod(@selector(init))
88 | 0x801354
89 | cy# NSObject.prototype.getMethod = function (sel) { return "ark"; }
90 | {}
91 | cy# NSObject.getMethod(@selector(init))
92 | "ark"
93 |
94 | Given that sending messages is actually a different namespace than function resolution, it is important to separate out the analog of a "prototype" in the world of Objective-C from that in JavaScript. Therefore, a field called "messages" (may change) is also added to Class objects. These messages can even be traded around and reassigned, with the results fully mapping back to the Objective-C runtime.
95 |
96 | cy# var view = [new UIView init]
97 | cy# view.constructor
98 | "UIView"
99 | cy# view.constructor.messages['description']
100 | 0x309d84f5
101 | cy# [view description]
102 | ">"
103 | cy# view.constructor.messages['description'] = function () { return "not!"; }
104 | {}
105 | cy# [view description]
106 | "not!"
107 |
108 | Structures are also supported (although unions are currently on the todo list and bitfields are still DOA): they are bridged back/forth as much as possible. You can specify them using either array syntax or in the form of dictionaries.
109 |
110 | cy# var rect = [UIHardware fullScreenApplicationContentRect]
111 | cy# rect
112 | {origin:{x:0,y:20},size:{width:320,height:460}}
113 | cy# rect.origin = [2, 3]
114 | [2,3]
115 | cy# rect.size = {width: 0, height: 1}
116 | {width:0,height:1}
117 | cy# rect
118 | {origin:{x:2,y:3},size:{width:0,height:1}}
119 |
120 | Access, allocation, and casting of pointers is possible through the usage of the Pointer and Type classes. Pointers can be indirected using the * and -> operators, as in C.
121 |
122 | cy# var count = new new Type("I")
123 | cy# var methods = class_copyMethodList(UIApplication, count)
124 | cy# *count
125 | 305
126 | cy# *new Pointer(count, "d")
127 | 7.304555902977629e-304
128 | cy# free(count)
129 | cy# methods
130 | 0x843800
131 | cy# methods[304]
132 | 0x825248
133 | cy# method_getName(methods[304])
134 | @selector(init)
135 |
136 | Objective-C @properties (some of which are auto-detected, as Apple doesn't always compile them into the resulting binaries) can be accessed using . notation. Currently, auto-detected @properties are usable, but aren't enumerable. This namespace is strictly separated from that of instance variables, which you can access by indirecting the object using * or ->.
137 |
138 | cy# var view = [new UIView init]
139 | cy# ps = []; for (var p in view) ps.push(p); ps
140 | ["skipsSubviewEnumeration","gestureRecognizers","gesturesEnabled","capturesDescendantTouches","deliversTouchesForGesturesToSuperview","userInteractionEnabled","layer","tag"]
141 | cy# vs = []; for (var v in *view) vs.push(v); vs
142 | ["isa","_layer","_tapInfo","_gestureInfo","_gestureRecognizers","_charge","_tag","_viewFlags"]
143 | cy# view.layer
144 | ""
145 | cy# view->_layer
146 | ""
147 | cy# (*view)._layer
148 | ""
149 |
150 | Fully-fledged Objective-C classes can also be declared using @class, which blurs the line between Objective-C's @interface and @implementation. Right now, declaring instance variables are not supported, but will be in a future version: for now you must provide an empty variable block.
151 |
152 | cy# @class TestClass : NSObject {
153 | cy> }
154 | cy> - description {
155 | cy> return "test";
156 | cy> }
157 | cy> @end
158 | cy# [new TestClass init]
159 | "test"
160 |
161 | The @class syntax can also be used to extend existing classes in a manner similar to categories. Note that type signatures, however, are not yet supported, so you end up heavily restricted in what you can add via this mechanism. In this case, one can also use a parenthesized expression as the class name.
162 |
163 | cy# @class NSObject
164 | cy> - description { return "replaced"; }
165 | cy> @end
166 | cy# var o = [new NSObject init]
167 | cy# o
168 | "replaced"
169 | cy# @class ([o class]) - description { return "again"; } @end
170 | cy# o
171 | "again"
172 |
173 | Cycript is also capable of accessing normal C functions and variables. Knowledge of the type signatures of various functions are provided in the bridge definition file, which is currently a plist stored at /usr/lib/libcycript.plist.
174 |
175 | cy# malloc
176 | 0x31d48389
177 | cy# var p = malloc(4)
178 | cy# p
179 | 0x22e0a0
180 | cy# free(p)
181 | cy#
182 |
183 | Cycript attempts to do its best to serialize information to the console about objects. In particular, CoreFoundaton objects bridged to Objective-C are detected and printed using CFCopyDescription.
184 |
185 | cy# UIGetScreenImage()
186 | ""
187 | cy# ABAddressBookCreate()
188 | ""
189 |
190 | How do I write an application with it?
191 |
192 | This isn't quite "ready for primetime", but you can download the example HelloCycript.app from http://www.cycript.org/examples/ and put it in /Applicatons.
193 |
194 | What else can it do?
195 |
196 | Probably the awesomest thing you can do with Cycript is to hook into an existing process using the -p argument to the console interpreter. As an example, let's hook our way into SpringBoard and start spelunking.
197 |
198 | iPhone:~$ ps ax | grep Spring
199 | 18110 ?? Us 0:03.03 /System/Library/CoreServices/SpringBoard.app/SpringBoard
200 | 18115 s006 S+ 0:00.02 grep --color=auto --exclude=.svn Spring
201 | iPhone:~$ cycript -p 18110
202 | cy# UIApp
203 | ""
204 | cy# UIApp->_uiController.window
205 | ">"
206 | cy# UIApp->_uiController.window.subviews
207 | [">","> enabled: yes, context array: (\n)","> enabled: yes, context array: (\n)"]
208 | cy# UIApp->_uiController.window.subviews[0].subviews
209 | [">",">"]
210 | cy# UIApp->_uiController.window.subviews[0].subviews[0].image.size
211 | {width:320,height:480}
212 | cy# UIApp->_uiController.window.subviews[0].subviews[1].subviews
213 | [">",">"]
214 | cy# UIApp->_uiController.window.subviews[0].subviews[1].subviews[0].subviews
215 | [">",">"]
216 | cy# var pages = UIApp->_uiController.window.subviews[0].subviews[1].subviews[0].subviews[0]
217 | cy# pages.currentPage
218 | 1
219 | cy# pages.numberOfPages
220 | 15
221 |
222 |
223 |
--------------------------------------------------------------------------------