| Index: src/d8.cc
|
| diff --git a/src/d8.cc b/src/d8.cc
|
| index 6f948c6e5c46e0ce363e7be07c75d44e5e100196..4f905afbd572c444bc62c5401eb9fc20bd7522b9 100644
|
| --- a/src/d8.cc
|
| +++ b/src/d8.cc
|
| @@ -41,6 +41,9 @@
|
| #include "natives.h"
|
| #include "platform.h"
|
|
|
| +#if !defined(_WIN32) && !defined(_WIN64)
|
| +#include <unistd.h> // NOLINT
|
| +#endif
|
|
|
| namespace v8 {
|
|
|
| @@ -97,6 +100,8 @@ CounterCollection Shell::local_counters_;
|
| CounterCollection* Shell::counters_ = &local_counters_;
|
| Persistent<Context> Shell::utility_context_;
|
| Persistent<Context> Shell::evaluation_context_;
|
| +i::Mutex* Shell::context_mutex_(i::OS::CreateMutex());
|
| +ShellOptions Shell::options;
|
|
|
|
|
| bool CounterMap::Match(void* key1, void* key2) {
|
| @@ -119,6 +124,7 @@ bool Shell::ExecuteString(Handle<String> source,
|
| bool report_exceptions) {
|
| HandleScope handle_scope;
|
| TryCatch try_catch;
|
| + options.script_executed = true;
|
| if (i::FLAG_debugger) {
|
| // When debugging make exceptions appear to be uncaught.
|
| try_catch.SetVerbose(true);
|
| @@ -540,7 +546,6 @@ void Shell::InstallUtilityScript() {
|
| shell_source_name.length());
|
| Handle<Script> script = Script::Compile(source, name);
|
| script->Run();
|
| -
|
| // Mark the d8 shell script as native to avoid it showing up as normal source
|
| // in the debugger.
|
| i::Handle<i::Object> compiled_script = Utils::OpenHandle(*script);
|
| @@ -625,7 +630,7 @@ Handle<ObjectTemplate> Shell::CreateGlobalTemplate() {
|
| }
|
|
|
|
|
| -void Shell::Initialize(bool test_shell) {
|
| +void Shell::Initialize() {
|
| #ifdef COMPRESS_STARTUP_DATA_BZ2
|
| BZip2Decompressor startup_data_decompressor;
|
| int bz2_result = startup_data_decompressor.Decompress();
|
| @@ -645,7 +650,7 @@ void Shell::Initialize(bool test_shell) {
|
| V8::SetAddHistogramSampleFunction(AddHistogramSample);
|
| }
|
|
|
| - if (test_shell) return;
|
| + if (options.FLAG_test_shell) return;
|
|
|
| Locker lock;
|
| HandleScope scope;
|
| @@ -666,17 +671,13 @@ void Shell::Initialize(bool test_shell) {
|
| }
|
|
|
|
|
| -void Shell::RenewEvaluationContext() {
|
| +Persistent<Context> Shell::CreateEvaluationContext() {
|
| + // This needs to be a critical section since this is not thread-safe
|
| + context_mutex_->Lock();
|
| // Initialize the global objects
|
| - HandleScope scope;
|
| Handle<ObjectTemplate> global_template = CreateGlobalTemplate();
|
| -
|
| - // (Re-)create the evaluation context
|
| - if (!evaluation_context_.IsEmpty()) {
|
| - evaluation_context_.Dispose();
|
| - }
|
| - evaluation_context_ = Context::New(NULL, global_template);
|
| - Context::Scope utility_scope(evaluation_context_);
|
| + Persistent<Context> context = Context::New(NULL, global_template);
|
| + Context::Scope scope(context);
|
|
|
| i::JSArguments js_args = i::FLAG_js_arguments;
|
| i::Handle<i::FixedArray> arguments_array =
|
| @@ -688,8 +689,10 @@ void Shell::RenewEvaluationContext() {
|
| }
|
| i::Handle<i::JSArray> arguments_jsarray =
|
| FACTORY->NewJSArrayWithElements(arguments_array);
|
| - evaluation_context_->Global()->Set(String::New("arguments"),
|
| + context->Global()->Set(String::New("arguments"),
|
| Utils::ToLocal(arguments_jsarray));
|
| + context_mutex_->Unlock();
|
| + return context;
|
| }
|
|
|
|
|
| @@ -717,7 +720,8 @@ void Shell::OnExit() {
|
|
|
|
|
| static char* ReadChars(const char* name, int* size_out) {
|
| - v8::Unlocker unlocker; // Release the V8 lock while reading files.
|
| + // Release the V8 lock while reading files.
|
| + v8::Unlocker unlocker(Isolate::GetCurrent());
|
| FILE* file = i::OS::FOpen(name, "rb");
|
| if (file == NULL) return NULL;
|
|
|
| @@ -806,11 +810,6 @@ class ShellThread : public i::Thread {
|
|
|
|
|
| void ShellThread::Run() {
|
| - // Prepare the context for this thread.
|
| - Locker locker;
|
| - HandleScope scope;
|
| - Handle<ObjectTemplate> global_template = Shell::CreateGlobalTemplate();
|
| -
|
| char* ptr = const_cast<char*>(files_.start());
|
| while ((ptr != NULL) && (*ptr != '\0')) {
|
| // For each newline-separated line.
|
| @@ -822,7 +821,10 @@ void ShellThread::Run() {
|
| continue;
|
| }
|
|
|
| - Persistent<Context> thread_context = Context::New(NULL, global_template);
|
| + // Prepare the context for this thread.
|
| + Locker locker;
|
| + HandleScope scope;
|
| + Persistent<Context> thread_context = Shell::CreateEvaluationContext();
|
| Context::Scope context_scope(thread_context);
|
|
|
| while ((ptr != NULL) && (*ptr != '\0')) {
|
| @@ -848,153 +850,304 @@ void ShellThread::Run() {
|
| }
|
| }
|
|
|
| -int Shell::RunMain(int argc, char* argv[], bool* executed) {
|
| - // Default use preemption if threads are created.
|
| - bool use_preemption = true;
|
|
|
| - // Default to use lowest possible thread preemption interval to test as many
|
| - // edgecases as possible.
|
| - int preemption_interval = 1;
|
| +void SourceGroup::ExitShell(int exit_code) {
|
| + // Use _exit instead of exit to avoid races between isolate
|
| + // threads and static destructors.
|
| + fflush(stdout);
|
| + fflush(stderr);
|
| + _exit(exit_code);
|
| +}
|
|
|
| - i::List<i::Thread*> threads(1);
|
|
|
| - {
|
| - // Since the thread below may spawn new threads accessing V8 holding the
|
| - // V8 lock here is mandatory.
|
| - Locker locker;
|
| - RenewEvaluationContext();
|
| - Context::Scope context_scope(evaluation_context_);
|
| - for (int i = 1; i < argc; i++) {
|
| - char* str = argv[i];
|
| - if (strcmp(str, "--preemption") == 0) {
|
| - use_preemption = true;
|
| - } else if (strcmp(str, "--no-preemption") == 0) {
|
| - use_preemption = false;
|
| - } else if (strcmp(str, "--preemption-interval") == 0) {
|
| - if (i + 1 < argc) {
|
| - char* end = NULL;
|
| - preemption_interval = strtol(argv[++i], &end, 10); // NOLINT
|
| - if (preemption_interval <= 0 || *end != '\0' || errno == ERANGE) {
|
| - printf("Invalid value for --preemption-interval '%s'\n", argv[i]);
|
| - return 1;
|
| - }
|
| - } else {
|
| - printf("Missing value for --preemption-interval\n");
|
| - return 1;
|
| - }
|
| - } else if (strcmp(str, "-f") == 0) {
|
| - // Ignore any -f flags for compatibility with other stand-alone
|
| - // JavaScript engines.
|
| - continue;
|
| - } else if (strncmp(str, "--", 2) == 0) {
|
| - printf("Warning: unknown flag %s.\nTry --help for options\n", str);
|
| - } else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
|
| - // Execute argument given to -e option directly.
|
| - v8::HandleScope handle_scope;
|
| - v8::Handle<v8::String> file_name = v8::String::New("unnamed");
|
| - v8::Handle<v8::String> source = v8::String::New(argv[++i]);
|
| - (*executed) = true;
|
| - if (!ExecuteString(source, file_name, false, true)) {
|
| - OnExit();
|
| - return 1;
|
| - }
|
| - } else if (strcmp(str, "-p") == 0 && i + 1 < argc) {
|
| - int size = 0;
|
| - const char* files = ReadChars(argv[++i], &size);
|
| - if (files == NULL) return 1;
|
| - ShellThread* thread =
|
| - new ShellThread(threads.length(),
|
| - i::Vector<const char>(files, size));
|
| - thread->Start();
|
| - threads.Add(thread);
|
| - (*executed) = true;
|
| - } else {
|
| - // Use all other arguments as names of files to load and run.
|
| - HandleScope handle_scope;
|
| - Handle<String> file_name = v8::String::New(str);
|
| - Handle<String> source = ReadFile(str);
|
| - (*executed) = true;
|
| - if (source.IsEmpty()) {
|
| - printf("Error reading '%s'\n", str);
|
| - return 1;
|
| - }
|
| - if (!ExecuteString(source, file_name, false, true)) {
|
| - OnExit();
|
| - return 1;
|
| - }
|
| +void SourceGroup::Execute() {
|
| + for (int i = begin_offset_; i < end_offset_; ++i) {
|
| + const char* arg = argv_[i];
|
| + if (strcmp(arg, "-e") == 0 && i + 1 < end_offset_) {
|
| + // Execute argument given to -e option directly.
|
| + HandleScope handle_scope;
|
| + Handle<String> file_name = String::New("unnamed");
|
| + Handle<String> source = String::New(argv_[i + 1]);
|
| + if (!Shell::ExecuteString(source, file_name, false, true)) {
|
| + ExitShell(1);
|
| + return;
|
| + }
|
| + ++i;
|
| + } else if (arg[0] == '-') {
|
| + // Ignore other options. They have been parsed already.
|
| + } else {
|
| + // Use all other arguments as names of files to load and run.
|
| + HandleScope handle_scope;
|
| + Handle<String> file_name = String::New(arg);
|
| + Handle<String> source = ReadFile(arg);
|
| + if (source.IsEmpty()) {
|
| + printf("Error reading '%s'\n", arg);
|
| + ExitShell(1);
|
| + return;
|
| + }
|
| + if (!Shell::ExecuteString(source, file_name, false, true)) {
|
| + ExitShell(1);
|
| + return;
|
| }
|
| }
|
| + }
|
| +}
|
|
|
| - // Start preemption if threads have been created and preemption is enabled.
|
| - if (threads.length() > 0 && use_preemption) {
|
| - Locker::StartPreemption(preemption_interval);
|
| +
|
| +Handle<String> SourceGroup::ReadFile(const char* name) {
|
| + FILE* file = fopen(name, "rb");
|
| + if (file == NULL) return Handle<String>();
|
| +
|
| + fseek(file, 0, SEEK_END);
|
| + int size = ftell(file);
|
| + rewind(file);
|
| +
|
| + char* chars = new char[size + 1];
|
| + chars[size] = '\0';
|
| + for (int i = 0; i < size;) {
|
| + int read = fread(&chars[i], 1, size - i, file);
|
| + i += read;
|
| + }
|
| + fclose(file);
|
| + Handle<String> result = String::New(chars, size);
|
| + delete[] chars;
|
| + return result;
|
| +}
|
| +
|
| +
|
| +#if !(defined(USING_V8_SHARED) || defined(V8_SHARED))
|
| +i::Thread::Options SourceGroup::GetThreadOptions() {
|
| + i::Thread::Options options;
|
| + options.name = "IsolateThread";
|
| + // On some systems (OSX 10.6) the stack size default is 0.5Mb or less
|
| + // which is not enough to parse the big literal expressions used in tests.
|
| + // The stack size should be at least StackGuard::kLimitSize + some
|
| + // OS-specific padding for thread startup code.
|
| + options.stack_size = 2 << 20; // 2 Mb seems to be enough
|
| + return options;
|
| +}
|
| +
|
| +void SourceGroup::ExecuteInThread() {
|
| + Isolate* isolate = Isolate::New();
|
| + do {
|
| + if (next_semaphore_ != NULL) next_semaphore_->Wait();
|
| + {
|
| + Isolate::Scope iscope(isolate);
|
| + Locker lock(isolate);
|
| + HandleScope scope;
|
| + Persistent<Context> context = Shell::CreateEvaluationContext();
|
| + {
|
| + Context::Scope cscope(context);
|
| + Execute();
|
| + }
|
| + context.Dispose();
|
| }
|
| + if (done_semaphore_ != NULL) done_semaphore_->Signal();
|
| + } while (!Shell::options.last_run);
|
| + isolate->Dispose();
|
| +}
|
| +
|
| +void SourceGroup::StartExecuteInThread() {
|
| + if (thread_ == NULL) {
|
| + thread_ = new IsolateThread(this);
|
| + thread_->Start();
|
| }
|
| + next_semaphore_->Signal();
|
| +}
|
|
|
| - for (int i = 0; i < threads.length(); i++) {
|
| - i::Thread* thread = threads[i];
|
| - thread->Join();
|
| - delete thread;
|
| +
|
| +void SourceGroup::WaitForThread() {
|
| + if (thread_ == NULL) return;
|
| + if (Shell::options.last_run) {
|
| + thread_->Join();
|
| + thread_ = NULL;
|
| + } else {
|
| + done_semaphore_->Wait();
|
| }
|
| - OnExit();
|
| - return 0;
|
| }
|
| +#endif // USING_V8_SHARED
|
|
|
|
|
| -int Shell::Main(int argc, char* argv[]) {
|
| - // Figure out if we're requested to stress the optimization
|
| - // infrastructure by running tests multiple times and forcing
|
| - // optimization in the last run.
|
| - bool FLAG_stress_opt = false;
|
| - bool FLAG_stress_deopt = false;
|
| - bool FLAG_interactive_shell = false;
|
| - bool FLAG_test_shell = false;
|
| - bool script_executed = false;
|
| +bool Shell::SetOptions(int argc, char* argv[]) {
|
| + Locker lock;
|
|
|
| for (int i = 0; i < argc; i++) {
|
| if (strcmp(argv[i], "--stress-opt") == 0) {
|
| - FLAG_stress_opt = true;
|
| + options.FLAG_stress_opt = true;
|
| argv[i] = NULL;
|
| } else if (strcmp(argv[i], "--stress-deopt") == 0) {
|
| - FLAG_stress_deopt = true;
|
| + options.FLAG_stress_deopt = true;
|
| argv[i] = NULL;
|
| } else if (strcmp(argv[i], "--noalways-opt") == 0) {
|
| // No support for stressing if we can't use --always-opt.
|
| - FLAG_stress_opt = false;
|
| - FLAG_stress_deopt = false;
|
| + options.FLAG_stress_opt = false;
|
| + options.FLAG_stress_deopt = false;
|
| } else if (strcmp(argv[i], "--shell") == 0) {
|
| - FLAG_interactive_shell = true;
|
| + options.FLAG_interactive_shell = true;
|
| argv[i] = NULL;
|
| } else if (strcmp(argv[i], "--test") == 0) {
|
| - FLAG_test_shell = true;
|
| + options.FLAG_test_shell = true;
|
| + argv[i] = NULL;
|
| + } else if (strcmp(argv[i], "--preemption") == 0) {
|
| + options.FLAG_preemption = true;
|
| argv[i] = NULL;
|
| + } else if (strcmp(argv[i], "--no-preemption") == 0) {
|
| + options.FLAG_preemption = false;
|
| + argv[i] = NULL;
|
| + } else if (strcmp(argv[i], "--preemption-interval") == 0) {
|
| + if (++i < argc) {
|
| + argv[i-1] = NULL;
|
| + char* end = NULL;
|
| + options.preemption_interval = strtol(argv[i], &end, 10); // NOLINT
|
| + if (options.preemption_interval <= 0
|
| + || *end != '\0'
|
| + || errno == ERANGE) {
|
| + printf("Invalid value for --preemption-interval '%s'\n", argv[i]);
|
| + return false;
|
| + }
|
| + argv[i] = NULL;
|
| + } else {
|
| + printf("Missing value for --preemption-interval\n");
|
| + return false;
|
| + }
|
| + } else if (strcmp(argv[i], "-f") == 0) {
|
| + // Ignore any -f flags for compatibility with other stand-alone
|
| + // JavaScript engines.
|
| + continue;
|
| + } else if (strcmp(argv[i], "--isolate") == 0) {
|
| +#if !(defined(USING_V8_SHARED) || defined(V8_SHARED))
|
| + options.num_isolates++;
|
| +#else // USING_V8_SHARED
|
| + printf("Error: --isolate not supported when linked with shared "
|
| + "library\n");
|
| + ExitShell(1);
|
| +#endif // USING_V8_SHARED
|
| + }
|
| + }
|
| +
|
| + // Run parallel threads if we are not using --isolate
|
| + for (int i = 1; i < argc; i++) {
|
| + if (argv[i] == NULL) continue;
|
| + if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) {
|
| + if (options.num_isolates > 1) {
|
| + printf("-p is not compatible with --isolate\n");
|
| + return false;
|
| + }
|
| + argv[i] = NULL;
|
| + if (options.parallel_files == NULL) {
|
| + options.parallel_files = new i::List<i::Vector<const char> >();
|
| + }
|
| + int size = 0;
|
| + const char* files = ReadChars(argv[++i], &size);
|
| + if (files == NULL) {
|
| + printf("-p option incomplete\n");
|
| + return false;
|
| + }
|
| + argv[i] = NULL;
|
| + options.parallel_files->Add(i::Vector<const char>(files, size));
|
| }
|
| }
|
|
|
| v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
|
|
|
| - Initialize(FLAG_test_shell);
|
| + // set up isolated source groups
|
| + options.isolate_sources = new SourceGroup[options.num_isolates];
|
| + SourceGroup* current = options.isolate_sources;
|
| + current->Begin(argv, 1);
|
| + for (int i = 1; i < argc; i++) {
|
| + const char* str = argv[i];
|
| + if (strcmp(str, "--isolate") == 0) {
|
| + current->End(i);
|
| + current++;
|
| + current->Begin(argv, i + 1);
|
| + } else if (strncmp(argv[i], "--", 2) == 0) {
|
| + printf("Warning: unknown flag %s.\nTry --help for options\n", argv[i]);
|
| + }
|
| + }
|
| + current->End(argc);
|
| +
|
| + return true;
|
| +}
|
| +
|
| +
|
| +int Shell::RunMain(int argc, char* argv[]) {
|
| + i::List<i::Thread*> threads(1);
|
| +
|
| + {
|
| + if (options.parallel_files != NULL)
|
| + for (int i = 0; i < options.parallel_files->length(); i++) {
|
| + i::Vector<const char> files = options.parallel_files->at(i);
|
| + ShellThread* thread = new ShellThread(threads.length(), files);
|
| + thread->Start();
|
| + threads.Add(thread);
|
| + }
|
| +
|
| +#if !(defined(USING_V8_SHARED) || defined(V8_SHARED))
|
| + for (int i = 1; i < options.num_isolates; ++i) {
|
| + options.isolate_sources[i].StartExecuteInThread();
|
| + }
|
| +#endif // USING_V8_SHARED
|
| +
|
| + Locker lock;
|
| + HandleScope scope;
|
| + Persistent<Context> context = CreateEvaluationContext();
|
| + {
|
| + Context::Scope cscope(context);
|
| + options.isolate_sources[0].Execute();
|
| + }
|
| + evaluation_context_ = context;
|
| + if (!options.last_run) context.Dispose();
|
| + // Start preemption if threads have been created and preemption is enabled.
|
| + if (options.parallel_files != NULL
|
| + && threads.length() > 0
|
| + && options.FLAG_preemption) {
|
| + Locker::StartPreemption(options.preemption_interval);
|
| + }
|
| + }
|
| +
|
| +#if !(defined(USING_V8_SHARED) || defined(V8_SHARED))
|
| + for (int i = 1; i < options.num_isolates; ++i) {
|
| + options.isolate_sources[i].WaitForThread();
|
| + }
|
| +#endif // USING_V8_SHARED
|
| +
|
| + if (options.parallel_files != NULL)
|
| + for (int i = 0; i < threads.length(); i++) {
|
| + i::Thread* thread = threads[i];
|
| + thread->Join();
|
| + delete thread;
|
| + }
|
| +
|
| + OnExit();
|
| + return 0;
|
| +}
|
| +
|
| +
|
| +int Shell::Main(int argc, char* argv[]) {
|
| + if (!SetOptions(argc, argv)) return 1;
|
| + Initialize();
|
|
|
| int result = 0;
|
| - if (FLAG_stress_opt || FLAG_stress_deopt) {
|
| - v8::Testing::SetStressRunType(
|
| - FLAG_stress_opt ? v8::Testing::kStressTypeOpt
|
| - : v8::Testing::kStressTypeDeopt);
|
| - int stress_runs = v8::Testing::GetStressRuns();
|
| + if (options.FLAG_stress_opt || options.FLAG_stress_deopt) {
|
| + Testing::SetStressRunType(
|
| + options.FLAG_stress_opt ? Testing::kStressTypeOpt
|
| + : Testing::kStressTypeDeopt);
|
| + int stress_runs = Testing::GetStressRuns();
|
| for (int i = 0; i < stress_runs && result == 0; i++) {
|
| printf("============ Stress %d/%d ============\n", i + 1, stress_runs);
|
| - v8::Testing::PrepareStressRun(i);
|
| - result = RunMain(argc, argv, &script_executed);
|
| + Testing::PrepareStressRun(i);
|
| + options.last_run = (i == stress_runs - 1);
|
| + result = RunMain(argc, argv);
|
| }
|
| printf("======== Full Deoptimization =======\n");
|
| - v8::Testing::DeoptimizeAll();
|
| + Testing::DeoptimizeAll();
|
| } else {
|
| - result = RunMain(argc, argv, &script_executed);
|
| + result = RunMain(argc, argv);
|
| }
|
|
|
| #ifdef ENABLE_DEBUGGER_SUPPORT
|
| // Run remote debugger if requested, but never on --test
|
| - if (i::FLAG_remote_debugger && !FLAG_test_shell) {
|
| + if (i::FLAG_remote_debugger && !options.FLAG_test_shell) {
|
| InstallUtilityScript();
|
| RunRemoteDebugger(i::FLAG_debugger_port);
|
| return 0;
|
| @@ -1003,12 +1156,15 @@ int Shell::Main(int argc, char* argv[]) {
|
|
|
| // Run interactive shell if explicitly requested or if no script has been
|
| // executed, but never on --test
|
| - if ((FLAG_interactive_shell || !script_executed) && !FLAG_test_shell) {
|
| +
|
| + if (( options.FLAG_interactive_shell
|
| + || !options.script_executed )
|
| + && !options.FLAG_test_shell ) {
|
| InstallUtilityScript();
|
| RunShell();
|
| }
|
|
|
| - v8::V8::Dispose();
|
| + V8::Dispose();
|
|
|
| return result;
|
| }
|
|
|