| 1 | /* Cycript - Optimizing JavaScript Compiler/Runtime |
| 2 | * Copyright (C) 2009-2013 Jay Freeman (saurik) |
| 3 | */ |
| 4 | |
| 5 | /* GNU General Public License, Version 3 {{{ */ |
| 6 | /* |
| 7 | * Cycript is free software: you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU General Public License as published |
| 9 | * by the Free Software Foundation, either version 3 of the License, |
| 10 | * or (at your option) any later version. |
| 11 | * |
| 12 | * Cycript is distributed in the hope that it will be useful, but |
| 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | * GNU General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU General Public License |
| 18 | * along with Cycript. If not, see <http://www.gnu.org/licenses/>. |
| 19 | **/ |
| 20 | /* }}} */ |
| 21 | |
| 22 | #include "cycript.hpp" |
| 23 | #include "JavaScript.hpp" |
| 24 | |
| 25 | #include "Pooling.hpp" |
| 26 | #include "Parser.hpp" |
| 27 | |
| 28 | #include "Cycript.tab.hh" |
| 29 | |
| 30 | #include <Foundation/Foundation.h> |
| 31 | #include <pthread.h> |
| 32 | #include <unistd.h> |
| 33 | #include <sstream> |
| 34 | |
| 35 | #include <sys/types.h> |
| 36 | #include <sys/socket.h> |
| 37 | #include <netinet/in.h> |
| 38 | #include <sys/un.h> |
| 39 | |
| 40 | struct CYExecute_ { |
| 41 | CYPool &pool_; |
| 42 | const char * volatile data_; |
| 43 | }; |
| 44 | |
| 45 | // XXX: this is "tre lame" |
| 46 | @interface CYClient_ : NSObject { |
| 47 | } |
| 48 | |
| 49 | - (void) execute:(NSValue *)value; |
| 50 | |
| 51 | @end |
| 52 | |
| 53 | @implementation CYClient_ |
| 54 | |
| 55 | - (void) execute:(NSValue *)value { |
| 56 | CYExecute_ *execute(reinterpret_cast<CYExecute_ *>([value pointerValue])); |
| 57 | const char *data(execute->data_); |
| 58 | execute->data_ = NULL; |
| 59 | execute->data_ = CYExecute(execute->pool_, CYUTF8String(data)); |
| 60 | } |
| 61 | |
| 62 | @end |
| 63 | |
| 64 | struct CYClient : |
| 65 | CYData |
| 66 | { |
| 67 | int socket_; |
| 68 | pthread_t thread_; |
| 69 | |
| 70 | CYClient(int socket) : |
| 71 | socket_(socket) |
| 72 | { |
| 73 | } |
| 74 | |
| 75 | ~CYClient() { |
| 76 | _syscall(close(socket_)); |
| 77 | } |
| 78 | |
| 79 | void Handle() { |
| 80 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
| 81 | |
| 82 | CYClient_ *client = [[[CYClient_ alloc] init] autorelease]; |
| 83 | |
| 84 | bool dispatch; |
| 85 | if (CFStringRef mode = CFRunLoopCopyCurrentMode(CFRunLoopGetMain())) { |
| 86 | dispatch = true; |
| 87 | CFRelease(mode); |
| 88 | } else |
| 89 | dispatch = false; |
| 90 | |
| 91 | for (;;) { |
| 92 | uint32_t size; |
| 93 | if (!CYRecvAll(socket_, &size, sizeof(size))) |
| 94 | return; |
| 95 | |
| 96 | CYLocalPool pool; |
| 97 | char *data(new(pool) char[size + 1]); |
| 98 | if (!CYRecvAll(socket_, data, size)) |
| 99 | return; |
| 100 | data[size] = '\0'; |
| 101 | |
| 102 | CYStream stream(data, data + size); |
| 103 | CYDriver driver(stream); |
| 104 | |
| 105 | cy::parser parser(driver); |
| 106 | |
| 107 | const char *json; |
| 108 | if (parser.parse() != 0 || !driver.errors_.empty()) { |
| 109 | json = NULL; |
| 110 | size = _not(uint32_t); |
| 111 | } else { |
| 112 | NSAutoreleasePool *ar = [[NSAutoreleasePool alloc] init]; |
| 113 | |
| 114 | CYOptions options; |
| 115 | CYContext context(options); |
| 116 | driver.program_->Replace(context); |
| 117 | std::ostringstream str; |
| 118 | CYOutput out(str, options); |
| 119 | out << *driver.program_; |
| 120 | std::string code(str.str()); |
| 121 | CYExecute_ execute = {pool, code.c_str()}; |
| 122 | NSValue *value([NSValue valueWithPointer:&execute]); |
| 123 | if (dispatch) |
| 124 | [client performSelectorOnMainThread:@selector(execute:) withObject:value waitUntilDone:YES]; |
| 125 | else |
| 126 | [client execute:value]; |
| 127 | json = execute.data_; |
| 128 | size = json == NULL ? _not(uint32_t) : strlen(json); |
| 129 | |
| 130 | [ar release]; |
| 131 | } |
| 132 | |
| 133 | if (!CYSendAll(socket_, &size, sizeof(size))) |
| 134 | return; |
| 135 | if (json != NULL) |
| 136 | if (!CYSendAll(socket_, json, size)) |
| 137 | return; |
| 138 | } |
| 139 | |
| 140 | [pool release]; |
| 141 | } |
| 142 | }; |
| 143 | |
| 144 | static void *OnClient(void *data) { |
| 145 | CYClient *client(reinterpret_cast<CYClient *>(data)); |
| 146 | client->Handle(); |
| 147 | delete client; |
| 148 | return NULL; |
| 149 | } |
| 150 | |
| 151 | extern "C" void CYHandleClient(CYPool &pool, int socket) { |
| 152 | CYClient *client(new(pool) CYClient(socket)); |
| 153 | _assert(pthread_create(&client->thread_, NULL, &OnClient, client) == 0); |
| 154 | } |
| 155 | |
| 156 | extern "C" void CYHandleServer(pid_t pid) { |
| 157 | CYInitializeDynamic(); |
| 158 | |
| 159 | int socket(_syscall(::socket(PF_UNIX, SOCK_STREAM, 0))); try { |
| 160 | struct sockaddr_un address; |
| 161 | memset(&address, 0, sizeof(address)); |
| 162 | address.sun_family = AF_UNIX; |
| 163 | sprintf(address.sun_path, "/tmp/.s.cy.%u", pid); |
| 164 | |
| 165 | _syscall(connect(socket, reinterpret_cast<sockaddr *>(&address), SUN_LEN(&address))); |
| 166 | |
| 167 | // XXX: this leaks memory... really? |
| 168 | CYHandleClient(*new CYPool(), socket); |
| 169 | } catch (const CYException &error) { |
| 170 | CYPool pool; |
| 171 | fprintf(stderr, "%s\n", error.PoolCString(pool)); |
| 172 | } |
| 173 | } |