Chromium Code Reviews| Index: src/d8.cc |
| diff --git a/src/d8.cc b/src/d8.cc |
| index 6f948c6e5c46e0ce363e7be07c75d44e5e100196..0a9b707535d4bc57b2bae15715f2ed4b00c637d7 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); |
| @@ -550,6 +555,13 @@ void Shell::InstallUtilityScript() { |
| : i::Handle<i::Script>(i::Script::cast( |
| i::SharedFunctionInfo::cast(*compiled_script)->script())); |
| script_object->set_type(i::Smi::FromInt(i::Script::TYPE_NATIVE)); |
| + |
| +#ifdef ENABLE_DEBUGGER_SUPPORT |
| + // Start the in-process debugger if requested. |
|
Yang
2011/07/08 13:15:40
set the listener *after* d8.js has been loaded fix
|
| + if (i::FLAG_debugger && !i::FLAG_debugger_agent) { |
| + v8::Debug::SetDebugEventListener(HandleDebugEvent); |
| + } |
| +#endif |
| } |
| @@ -625,7 +637,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 +657,7 @@ void Shell::Initialize(bool test_shell) { |
| V8::SetAddHistogramSampleFunction(AddHistogramSample); |
| } |
| - if (test_shell) return; |
| + if (options.test_shell) return; |
| Locker lock; |
| HandleScope scope; |
| @@ -657,26 +669,17 @@ void Shell::Initialize(bool test_shell) { |
| if (i::FLAG_debugger_agent) { |
| v8::Debug::EnableAgent("d8 shell", i::FLAG_debugger_port, true); |
| } |
| - |
| - // Start the in-process debugger if requested. |
| - if (i::FLAG_debugger && !i::FLAG_debugger_agent) { |
| - v8::Debug::SetDebugEventListener(HandleDebugEvent); |
| - } |
| #endif |
| } |
| -void Shell::RenewEvaluationContext() { |
| +Persistent<Context> Shell::CreateEvaluationContext() { |
| + // This needs to be a critical section since this is not thread-safe |
| + i::ScopedLock lock(context_mutex_); |
| // 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,28 +691,27 @@ 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)); |
| + return context; |
| } |
| void Shell::OnExit() { |
| if (i::FLAG_dump_counters) { |
| - ::printf("+----------------------------------------+-------------+\n"); |
| - ::printf("| Name | Value |\n"); |
| - ::printf("+----------------------------------------+-------------+\n"); |
| + printf("+----------------------------------------+-------------+\n"); |
| + printf("| Name | Value |\n"); |
| + printf("+----------------------------------------+-------------+\n"); |
| for (CounterMap::Iterator i(counter_map_); i.More(); i.Next()) { |
| Counter* counter = i.CurrentValue(); |
| if (counter->is_histogram()) { |
| - ::printf("| c:%-36s | %11i |\n", i.CurrentKey(), counter->count()); |
| - ::printf("| t:%-36s | %11i |\n", |
| - i.CurrentKey(), |
| - counter->sample_total()); |
| + printf("| c:%-36s | %11i |\n", i.CurrentKey(), counter->count()); |
| + printf("| t:%-36s | %11i |\n", i.CurrentKey(), counter->sample_total()); |
| } else { |
| - ::printf("| %-38s | %11i |\n", i.CurrentKey(), counter->count()); |
| + printf("| %-38s | %11i |\n", i.CurrentKey(), counter->count()); |
| } |
| } |
| - ::printf("+----------------------------------------+-------------+\n"); |
| + printf("+----------------------------------------+-------------+\n"); |
| } |
| if (counters_file_ != NULL) |
| delete counters_file_; |
| @@ -717,7 +719,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 +809,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 +820,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 +849,298 @@ 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; |
| +} |
| + |
| + |
| +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; |
| } |
| -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.stress_opt = true; |
| argv[i] = NULL; |
| } else if (strcmp(argv[i], "--stress-deopt") == 0) { |
| - FLAG_stress_deopt = true; |
| + options.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.stress_opt = false; |
| + options.stress_deopt = false; |
| } else if (strcmp(argv[i], "--shell") == 0) { |
| - FLAG_interactive_shell = true; |
| + options.interactive_shell = true; |
| argv[i] = NULL; |
| } else if (strcmp(argv[i], "--test") == 0) { |
| - FLAG_test_shell = true; |
| + options.test_shell = true; |
| + argv[i] = NULL; |
| + } else if (strcmp(argv[i], "--preemption") == 0) { |
| + options.use_preemption = true; |
| argv[i] = NULL; |
| + } else if (strcmp(argv[i], "--no-preemption") == 0) { |
| + options.use_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) { |
| + options.num_isolates++; |
| + } |
| + } |
| + |
| + // 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); |
| + } |
| + |
| + for (int i = 1; i < options.num_isolates; ++i) { |
| + options.isolate_sources[i].StartExecuteInThread(); |
| + } |
| + |
| + Locker lock; |
| + HandleScope scope; |
| + Persistent<Context> context = CreateEvaluationContext(); |
| + { |
| + Context::Scope cscope(context); |
| + options.isolate_sources[0].Execute(); |
| + } |
| + if (options.last_run) { |
| + // Keep using the same context in the interactive shell |
| + evaluation_context_ = context; |
| + } else { |
| + context.Dispose(); |
| + } |
| + // Start preemption if threads have been created and preemption is enabled. |
| + if (options.parallel_files != NULL |
| + && threads.length() > 0 |
| + && options.use_preemption) { |
| + Locker::StartPreemption(options.preemption_interval); |
| + } |
| + } |
| + |
| + for (int i = 1; i < options.num_isolates; ++i) { |
| + options.isolate_sources[i].WaitForThread(); |
| + } |
| + |
| + 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.stress_opt || options.stress_deopt) { |
| + Testing::SetStressRunType( |
| + options.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.test_shell) { |
| InstallUtilityScript(); |
| RunRemoteDebugger(i::FLAG_debugger_port); |
| return 0; |
| @@ -1003,12 +1149,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.interactive_shell |
| + || !options.script_executed ) |
| + && !options.test_shell ) { |
| InstallUtilityScript(); |
| RunShell(); |
| } |
| - v8::V8::Dispose(); |
| + V8::Dispose(); |
| return result; |
| } |