X-Git-Url: https://git.saurik.com/cycript.git/blobdiff_plain/58afc6aaca119c6b7b9ac5d91e3c6ee28dbd6fda..c8a2a786eb3f1cda8780b348f27e2c8240621045:/Console.cpp diff --git a/Console.cpp b/Console.cpp index 9ad12ee..57f4375 100644 --- a/Console.cpp +++ b/Console.cpp @@ -1,5 +1,5 @@ /* Cycript - Optimizing JavaScript Compiler/Runtime - * Copyright (C) 2009-2014 Jay Freeman (saurik) + * Copyright (C) 2009-2015 Jay Freeman (saurik) */ /* GNU Affero General Public License, Version 3 {{{ */ @@ -62,11 +62,14 @@ #include -#include "Display.hpp" -#include "Replace.hpp" +#ifdef __APPLE__ +#include +#endif -#include "Cycript.tab.hh" +#include "Display.hpp" #include "Driver.hpp" +#include "Highlight.hpp" +#include "Parser.hpp" static volatile enum { Working, @@ -85,7 +88,8 @@ static void sigint(int) { case Parsing: longjmp(ctrlc_, 1); case Running: - throw "*** Ctrl-C"; + CYCancel(); + return; case Sending: return; case Waiting: @@ -93,26 +97,22 @@ static void sigint(int) { } } -#if YYDEBUG static bool bison_; -#endif +static bool timing_; static bool strict_; static bool pretty_; -void Setup(CYDriver &driver, cy::parser &parser) { -#if YYDEBUG +void Setup(CYDriver &driver) { if (bison_) - parser.set_debug_level(1); -#endif + driver.debug_ = 1; if (strict_) driver.strict_ = true; } void Setup(CYOutput &out, CYDriver &driver, CYOptions &options, bool lower) { out.pretty_ = pretty_; - CYContext context(options); if (lower) - driver.program_->Replace(context); + driver.Replace(options); } static CYUTF8String Run(CYPool &pool, int client, CYUTF8String code) { @@ -211,182 +211,21 @@ int (*append_history$)(int, const char *); static std::string command_; -static CYExpression *ParseExpression(CYUTF8String code) { - std::stringstream stream; - stream << '(' << code << ')'; - CYDriver driver(stream); - - cy::parser parser(driver); - Setup(driver, parser); - - if (parser.parse() != 0 || !driver.errors_.empty()) - return NULL; - - CYOptions options; - CYContext context(options); - - CYStatement *statement(driver.program_->statements_); - _assert(statement != NULL); - _assert(statement->next_ == NULL); - - CYExpress *express(dynamic_cast(driver.program_->statements_)); - _assert(express != NULL); +static int client_; - return express->expression_; +static CYUTF8String Run(CYPool &pool, const std::string &code) { + return Run(pool, client_, code); } -static int client_; - static char **Complete(const char *word, int start, int end) { rl_attempted_completion_over = ~0; - - CYLocalPool pool; - std::string line(rl_line_buffer, start); - std::istringstream stream(command_ + line); - CYDriver driver(stream); - - driver.auto_ = true; - - cy::parser parser(driver); - Setup(driver, parser); - - if (parser.parse() != 0 || !driver.errors_.empty()) - return NULL; - - if (driver.mode_ == CYDriver::AutoNone) - return NULL; - - CYExpression *expression; - - CYOptions options; - CYContext context(options); - - std::ostringstream prefix; - - switch (driver.mode_) { - case CYDriver::AutoPrimary: - expression = $ CYThis(); - break; - - case CYDriver::AutoDirect: - expression = driver.context_; - break; - - case CYDriver::AutoIndirect: - expression = $ CYIndirect(driver.context_); - break; - - case CYDriver::AutoMessage: { - CYDriver::Context &thing(driver.contexts_.back()); - expression = $M($C1($V("object_getClass"), thing.context_), $S("messages")); - for (CYDriver::Context::Words::const_iterator part(thing.words_.begin()); part != thing.words_.end(); ++part) - prefix << (*part)->word_ << ':'; - } break; - - default: - _assert(false); - } - - std::string begin(prefix.str()); - - driver.program_ = $ CYProgram($ CYExpress($C3(ParseExpression( - " function(object, prefix, word) {\n" - " var names = [];\n" - " var before = prefix.length;\n" - " prefix += word;\n" - " var entire = prefix.length;\n" - " for (var name in object)\n" - " if (name.substring(0, entire) == prefix)\n" - " names.push(name.substr(before));\n" - " return names;\n" - " }\n" - ), expression, $S(begin.c_str()), $S(word)))); - - driver.program_->Replace(context); - - std::ostringstream str; - CYOutput out(str, options); - out << *driver.program_; - - std::string code(str.str()); - CYUTF8String json(Run(pool, client_, code)); - // XXX: if this fails we should not try to parse it - - CYExpression *result(ParseExpression(json)); - if (result == NULL) - return NULL; - - CYArray *array(dynamic_cast(result->Primitive(context))); - if (array == NULL) { - *out_ << '\n'; - Output(false, json, out_); - rl_forced_update_display(); - return NULL; - } - - // XXX: use an std::set? - typedef std::vector Completions; - Completions completions; - - std::string common; - bool rest(false); - - CYForEach (element, array->elements_) { - CYString *string(dynamic_cast(element->value_)); - _assert(string != NULL); - - std::string completion; - if (string->size_ != 0) - completion.assign(string->value_, string->size_); - else if (driver.mode_ == CYDriver::AutoMessage) - completion = "]"; - else - continue; - - completions.push_back(completion); - - if (!rest) { - common = completion; - rest = true; - } else { - size_t limit(completion.size()), size(common.size()); - if (size > limit) - common = common.substr(0, limit); - else - limit = size; - for (limit = 0; limit != size; ++limit) - if (common[limit] != completion[limit]) - break; - if (limit != size) - common = common.substr(0, limit); - } - } - - size_t count(completions.size()); - if (count == 0) - return NULL; - - size_t colon(common.find(':')); - if (colon != std::string::npos) - common = common.substr(0, colon + 1); - if (completions.size() == 1) - common += ' '; - - char **results(reinterpret_cast(malloc(sizeof(char *) * (count + 2)))); - - results[0] = strdup(common.c_str()); - size_t index(0); - for (Completions::const_iterator i(completions.begin()); i != completions.end(); ++i) - results[++index] = strdup(i->c_str()); - results[count + 1] = NULL; - - return results; + return CYComplete(word, command_ + line, &Run); } // need char *, not const char * static char name_[] = "cycript"; -static char break_[] = " \t\n\"\\'`@$><=;|&{(" ")}" ".:[]"; +static char break_[] = " \t\n\"\\'`@><=;|&{(" ")}" ".:[]"; class History { private: @@ -412,7 +251,7 @@ class History { } void operator +=(const std::string &command) { - add_history(command_.c_str()); + add_history(command.c_str()); ++histlines_; } }; @@ -526,6 +365,7 @@ static void Console(CYOptions &options) { } command_ += line; + command_ += "\n"; char *begin(line), *end(line + strlen(line)); while (char *nl = reinterpret_cast(memchr(begin, '\n', end - begin))) { @@ -543,18 +383,18 @@ static void Console(CYOptions &options) { if (bypass) code = command_; else { - CYLocalPool pool; - std::istringstream stream(command_); - CYDriver driver(stream); - cy::parser parser(driver); - Setup(driver, parser); + CYPool pool; + CYDriver driver(pool, stream); + Setup(driver); - if (parser.parse() != 0 || !driver.errors_.empty()) { + bool failed(driver.Parse()); + + if (failed || !driver.errors_.empty()) { for (CYDriver::Errors::const_iterator error(driver.errors_.begin()); error != driver.errors_.end(); ++error) { CYPosition begin(error->location_.begin); - if (begin.line != lines.size() || begin.column < lines.back().size() || error->warning_) { + if (begin.line != lines.size() + 1 || error->warning_) { CYPosition end(error->location_.end); if (begin.line != lines.size()) { @@ -574,29 +414,28 @@ static void Console(CYOptions &options) { std::cerr << " | "; std::cerr << error->message_ << std::endl; - history += command_; + history += command_.substr(0, command_.size() - 1); goto restart; } } driver.errors_.clear(); - command_ += '\n'; prompt = "cy> "; goto read; } - if (driver.program_ == NULL) + if (driver.script_ == NULL) goto restart; - std::ostringstream str; + std::stringbuf str; CYOutput out(str, options); Setup(out, driver, options, lower); - out << *driver.program_; + out << *driver.script_; code = str.str(); } - history += command_; + history += command_.substr(0, command_.size() - 1); if (debug) { std::cout << "cy= "; @@ -608,7 +447,17 @@ static void Console(CYOptions &options) { } } -void InjectLibrary(pid_t, int, const char *[]); +void InjectLibrary(pid_t, int, const char *const []); + +static uint64_t CYGetTime() { +#ifdef __APPLE__ + return mach_absolute_time(); +#else + struct timespec spec; + clock_gettime(CLOCK_MONOTONIC, &spec); + return spec.tv_sec * UINT64_C(1000000000) + spec.tv_nsec; +#endif +} int Main(int argc, char * const argv[], char const * const envp[]) { bool tty(isatty(STDIN_FILENO)); @@ -685,10 +534,10 @@ int Main(int argc, char * const argv[], char const * const envp[]) { if (false); else if (strcmp(optarg, "rename") == 0) options.verbose_ = true; -#if YYDEBUG else if (strcmp(optarg, "bison") == 0) bison_ = true; -#endif + else if (strcmp(optarg, "timing") == 0) + timing_ = true; else { fprintf(stderr, "invalid name for -g\n"); return 1; @@ -715,7 +564,8 @@ int Main(int argc, char * const argv[], char const * const envp[]) { // XXX: arg needs to be escaped in some horrendous way of doom // XXX: this is a memory leak now because I just don't care enough char *command; - asprintf(&command, "ps axc|sed -e '/^ *[0-9]/{s/^ *\\([0-9]*\\)\\( *[^ ]*\\)\\{3\\} *-*\\([^ ]*\\)/\\3 \\1/;/^%s /{s/^[^ ]* //;q;};};d'", optarg); + int writ(asprintf(&command, "ps axc|sed -e '/^ *[0-9]/{s/^ *\\([0-9]*\\)\\( *[^ ]*\\)\\{3\\} *-*\\([^ ]*\\)/\\3 \\1/;/^%s /{s/^[^ ]* //;q;};};d'", optarg)); + _assert(writ != -1); if (FILE *pids = popen(command, "r")) { char value[32]; @@ -863,7 +713,8 @@ int Main(int argc, char * const argv[], char const * const envp[]) { _syscall(chmod(address.sun_path, 0777)); _syscall(listen(server, 1)); - InjectLibrary(pid, 1, (const char *[]) {address.sun_path, NULL}); + const char *const argv[] = {address.sun_path, NULL}; + InjectLibrary(pid, 1, argv); client_ = _syscall(accept(server, NULL, NULL)); } #else @@ -901,8 +752,6 @@ int Main(int argc, char * const argv[], char const * const envp[]) { if (script == NULL && tty) Console(options); else { - CYLocalPool pool; - std::istream *stream; if (script == NULL) { stream = &std::cin; @@ -912,18 +761,58 @@ int Main(int argc, char * const argv[], char const * const envp[]) { _assert(!stream->fail()); } - CYDriver driver(*stream, script); - cy::parser parser(driver); - Setup(driver, parser); + if (timing_) { + std::stringbuf buffer; + stream->get(buffer, '\0'); + _assert(!stream->fail()); + + double average(0); + int samples(-50); + uint64_t start(CYGetTime()); + + for (;;) { + stream = new std::istringstream(buffer.str()); + + CYPool pool; + CYDriver driver(pool, *stream, script); + Setup(driver); + + uint64_t begin(CYGetTime()); + driver.Parse(); + uint64_t end(CYGetTime()); + + delete stream; + + average += (end - begin - average) / ++samples; + + uint64_t now(CYGetTime()); + if (samples == 0) + average = 0; + else if ((now - start) / 1000000000 >= 1) + std::cout << std::fixed << average << '\t' << (end - begin) << '\t' << samples << std::endl; + else continue; + + start = now; + } + + stream = new std::istringstream(buffer.str()); + std::cin.get(); + } + + CYPool pool; + CYDriver driver(pool, *stream, script); + Setup(driver); + + bool failed(driver.Parse()); - if (parser.parse() != 0 || !driver.errors_.empty()) { + if (failed || !driver.errors_.empty()) { for (CYDriver::Errors::const_iterator i(driver.errors_.begin()); i != driver.errors_.end(); ++i) std::cerr << i->location_.begin << ": " << i->message_ << std::endl; - } else if (driver.program_ != NULL) { - std::ostringstream str; + } else if (driver.script_ != NULL) { + std::stringbuf str; CYOutput out(str, options); Setup(out, driver, options, true); - out << *driver.program_; + out << *driver.script_; std::string code(str.str()); if (compile) std::cout << code;