]> git.saurik.com Git - cycript.git/blob - Application.mm
Drastic improvements to memory management, handle many more minimization corner cases...
[cycript.git] / Application.mm
1 #define _GNU_SOURCE
2
3 #include <substrate.h>
4 #include "cycript.hpp"
5
6 #include <cstdio>
7 #include <sstream>
8
9 #include <setjmp.h>
10
11 #include <readline/readline.h>
12 #include <readline/history.h>
13
14 #include <sys/mman.h>
15
16 #include <errno.h>
17 #include <unistd.h>
18
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22
23 #include "Cycript.tab.hh"
24
25 static jmp_buf ctrlc_;
26
27 void sigint(int) {
28 longjmp(ctrlc_, 1);
29 }
30
31 void Run(const char *code, FILE *fout) { _pooled
32 JSStringRef script(JSStringCreateWithUTF8CString(code));
33
34 JSContextRef context(CYGetJSContext());
35
36 JSValueRef exception(NULL);
37 JSValueRef result(JSEvaluateScript(context, script, NULL, NULL, 0, &exception));
38 JSStringRelease(script);
39
40 if (exception != NULL)
41 result = exception;
42
43 if (!JSValueIsUndefined(context, result)) {
44 CFStringRef json;
45
46 @try { json:
47 json = CYCopyJSONString(context, result);
48 } @catch (id error) {
49 CYThrow(context, error, &result);
50 goto json;
51 }
52
53 if (fout != NULL) {
54 fputs([reinterpret_cast<const NSString *>(json) UTF8String], fout);
55 fputs("\n", fout);
56 fflush(fout);
57 }
58
59 CFRelease(json);
60 }
61 }
62
63 void Console() {
64 bool bypass(false);
65 bool debug(false);
66
67 FILE *fout(stdout);
68
69 rl_bind_key('\t', rl_insert);
70
71 struct sigaction action;
72 sigemptyset(&action.sa_mask);
73 action.sa_handler = &sigint;
74 action.sa_flags = 0;
75 sigaction(SIGINT, &action, NULL);
76
77 restart: for (;;) {
78 std::string command;
79 std::vector<std::string> lines;
80
81 bool extra(false);
82 const char *prompt("cy# ");
83
84 if (setjmp(ctrlc_) != 0) {
85 fputs("\n", fout);
86 fflush(fout);
87 goto restart;
88 }
89
90 read:
91 char *line(readline(prompt));
92 if (line == NULL)
93 break;
94
95 if (!extra) {
96 extra = true;
97 if (line[0] == '\\') {
98 std::string data(line + 1);
99 if (data == "bypass") {
100 bypass = !bypass;
101 fprintf(fout, "bypass == %s\n", bypass ? "true" : "false");
102 fflush(fout);
103 } else if (data == "debug") {
104 debug = !debug;
105 fprintf(fout, "debug == %s\n", debug ? "true" : "false");
106 fflush(fout);
107 }
108 add_history(line);
109 goto restart;
110 }
111 }
112
113 lines.push_back(line);
114 command += line;
115 free(line);
116
117 std::string code;
118
119 if (bypass)
120 code = command;
121 else {
122 CYDriver driver("");
123 cy::parser parser(driver);
124
125 driver.data_ = command.c_str();
126 driver.size_ = command.size();
127
128 if (parser.parse() != 0 || !driver.errors_.empty()) {
129 for (CYDriver::Errors::const_iterator i(driver.errors_.begin()); i != driver.errors_.end(); ++i) {
130 cy::position begin(i->location_.begin);
131 if (begin.line != lines.size() || begin.column - 1 != lines.back().size()) {
132 std::cerr << i->message_ << std::endl;
133 add_history(command.c_str());
134 goto restart;
135 }
136 }
137
138 driver.errors_.clear();
139
140 command += '\n';
141 prompt = "cy> ";
142 goto read;
143 }
144
145 if (driver.source_ == NULL)
146 goto restart;
147
148 std::ostringstream str;
149 driver.source_->Show(str);
150 code = str.str();
151 }
152
153 add_history(command.c_str());
154
155 if (debug)
156 std::cout << code << std::endl;
157
158 Run(code.c_str(), fout);
159 }
160
161 fputs("\n", fout);
162 fflush(fout);
163 }
164
165 void *Map(const char *path, size_t *psize) {
166 int fd;
167 _syscall(fd = open(path, O_RDONLY));
168
169 struct stat stat;
170 _syscall(fstat(fd, &stat));
171 size_t size(stat.st_size);
172
173 *psize = size;
174
175 void *base;
176 _syscall(base = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0));
177
178 _syscall(close(fd));
179 return base;
180 }
181
182 int main(int argc, const char *argv[]) {
183 const char *script;
184
185 if (argc == 1)
186 script = NULL;
187 else {
188 CYSetArgs(argc - 1, argv + 1);
189 script = argv[1];
190 }
191
192 if (script == NULL || strcmp(script, "-") == 0)
193 Console();
194 else {
195 CYDriver driver(script);
196 cy::parser parser(driver);
197
198 size_t size;
199 char *start(reinterpret_cast<char *>(Map(script, &size)));
200 char *end(start + size);
201
202 if (size >= 2 && start[0] == '#' && start[1] == '!') {
203 start += 2;
204 while (start != end && *start++ != '\n');
205 }
206
207 driver.data_ = start;
208 driver.size_ = end - start;
209
210 if (parser.parse() != 0 || !driver.errors_.empty()) {
211 for (CYDriver::Errors::const_iterator i(driver.errors_.begin()); i != driver.errors_.end(); ++i)
212 std::cerr << i->location_.begin << ": " << i->message_ << std::endl;
213 } else if (driver.source_ != NULL) {
214 std::ostringstream str;
215 driver.source_->Show(str);
216 std::string code(str.str());
217 std::cout << code << std::endl;
218 Run(code.c_str(), stdout);
219 }
220 }
221
222 return 0;
223 }