| Index: src/d8.cc
|
| ===================================================================
|
| --- src/d8.cc (revision 8618)
|
| +++ src/d8.cc (working copy)
|
| @@ -1,4 +1,4 @@
|
| -// Copyright 2009 the V8 project authors. All rights reserved.
|
| +// Copyright 2011 the V8 project authors. All rights reserved.
|
| // Redistribution and use in source and binary forms, with or without
|
| // modification, are permitted provided that the following conditions are
|
| // met:
|
| @@ -26,8 +26,11 @@
|
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
| +#ifdef COMPRESS_STARTUP_DATA_BZ2
|
| +#include <bzlib.h>
|
| +#endif
|
| +#include <errno.h>
|
| #include <stdlib.h>
|
| -#include <errno.h>
|
|
|
| #include "v8.h"
|
|
|
| @@ -38,6 +41,9 @@
|
| #include "natives.h"
|
| #include "platform.h"
|
|
|
| +#if !defined(_WIN32) && !defined(_WIN64)
|
| +#include <unistd.h> // NOLINT
|
| +#endif
|
|
|
| namespace v8 {
|
|
|
| @@ -94,6 +100,8 @@
|
| 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) {
|
| @@ -116,6 +124,7 @@
|
| 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);
|
| @@ -218,6 +227,103 @@
|
| }
|
|
|
|
|
| +Handle<Value> Shell::CreateExternalArray(const Arguments& args,
|
| + ExternalArrayType type,
|
| + size_t element_size) {
|
| + ASSERT(element_size == 1 || element_size == 2 || element_size == 4 ||
|
| + element_size == 8);
|
| + if (args.Length() != 1) {
|
| + return ThrowException(
|
| + String::New("Array constructor needs one parameter."));
|
| + }
|
| + size_t length = 0;
|
| + if (args[0]->IsUint32()) {
|
| + length = args[0]->Uint32Value();
|
| + } else if (args[0]->IsNumber()) {
|
| + double raw_length = args[0]->NumberValue();
|
| + if (raw_length < 0) {
|
| + return ThrowException(String::New("Array length must not be negative."));
|
| + }
|
| + if (raw_length > i::ExternalArray::kMaxLength) {
|
| + return ThrowException(
|
| + String::New("Array length exceeds maximum length."));
|
| + }
|
| + length = static_cast<size_t>(raw_length);
|
| + } else {
|
| + return ThrowException(String::New("Array length must be a number."));
|
| + }
|
| + if (length > static_cast<size_t>(i::ExternalArray::kMaxLength)) {
|
| + return ThrowException(String::New("Array length exceeds maximum length."));
|
| + }
|
| + void* data = calloc(length, element_size);
|
| + if (data == NULL) {
|
| + return ThrowException(String::New("Memory allocation failed."));
|
| + }
|
| + Handle<Object> array = Object::New();
|
| + Persistent<Object> persistent_array = Persistent<Object>::New(array);
|
| + persistent_array.MakeWeak(data, ExternalArrayWeakCallback);
|
| + persistent_array.MarkIndependent();
|
| + array->SetIndexedPropertiesToExternalArrayData(data, type, length);
|
| + array->Set(String::New("length"), Int32::New(length), ReadOnly);
|
| + array->Set(String::New("BYTES_PER_ELEMENT"), Int32::New(element_size));
|
| + return array;
|
| +}
|
| +
|
| +
|
| +void Shell::ExternalArrayWeakCallback(Persistent<Value> object, void* data) {
|
| + free(data);
|
| + object.Dispose();
|
| +}
|
| +
|
| +
|
| +Handle<Value> Shell::Int8Array(const Arguments& args) {
|
| + return CreateExternalArray(args, v8::kExternalByteArray, sizeof(int8_t));
|
| +}
|
| +
|
| +
|
| +Handle<Value> Shell::Uint8Array(const Arguments& args) {
|
| + return CreateExternalArray(args, kExternalUnsignedByteArray, sizeof(uint8_t));
|
| +}
|
| +
|
| +
|
| +Handle<Value> Shell::Int16Array(const Arguments& args) {
|
| + return CreateExternalArray(args, kExternalShortArray, sizeof(int16_t));
|
| +}
|
| +
|
| +
|
| +Handle<Value> Shell::Uint16Array(const Arguments& args) {
|
| + return CreateExternalArray(args, kExternalUnsignedShortArray,
|
| + sizeof(uint16_t));
|
| +}
|
| +
|
| +
|
| +Handle<Value> Shell::Int32Array(const Arguments& args) {
|
| + return CreateExternalArray(args, kExternalIntArray, sizeof(int32_t));
|
| +}
|
| +
|
| +
|
| +Handle<Value> Shell::Uint32Array(const Arguments& args) {
|
| + return CreateExternalArray(args, kExternalUnsignedIntArray, sizeof(uint32_t));
|
| +}
|
| +
|
| +
|
| +Handle<Value> Shell::Float32Array(const Arguments& args) {
|
| + return CreateExternalArray(args, kExternalFloatArray,
|
| + sizeof(float)); // NOLINT
|
| +}
|
| +
|
| +
|
| +Handle<Value> Shell::Float64Array(const Arguments& args) {
|
| + return CreateExternalArray(args, kExternalDoubleArray,
|
| + sizeof(double)); // NOLINT
|
| +}
|
| +
|
| +
|
| +Handle<Value> Shell::PixelArray(const Arguments& args) {
|
| + return CreateExternalArray(args, kExternalPixelArray, sizeof(uint8_t));
|
| +}
|
| +
|
| +
|
| Handle<Value> Shell::Yield(const Arguments& args) {
|
| v8::Unlocker unlocker;
|
| return Undefined();
|
| @@ -408,20 +514,85 @@
|
| counter->AddSample(sample);
|
| }
|
|
|
| +void Shell::InstallUtilityScript() {
|
| + Locker lock;
|
| + HandleScope scope;
|
| + // If we use the utility context, we have to set the security tokens so that
|
| + // utility, evaluation and debug context can all access each other.
|
| + utility_context_->SetSecurityToken(Undefined());
|
| + evaluation_context_->SetSecurityToken(Undefined());
|
| + Context::Scope utility_scope(utility_context_);
|
|
|
| -void Shell::Initialize() {
|
| - Shell::counter_map_ = new CounterMap();
|
| - // Set up counters
|
| - if (i::StrLength(i::FLAG_map_counters) != 0)
|
| - MapCounters(i::FLAG_map_counters);
|
| - if (i::FLAG_dump_counters) {
|
| - V8::SetCounterFunction(LookupCounter);
|
| - V8::SetCreateHistogramFunction(CreateHistogram);
|
| - V8::SetAddHistogramSampleFunction(AddHistogramSample);
|
| +#ifdef ENABLE_DEBUGGER_SUPPORT
|
| + // Install the debugger object in the utility scope
|
| + i::Debug* debug = i::Isolate::Current()->debug();
|
| + debug->Load();
|
| + i::Handle<i::JSObject> js_debug
|
| + = i::Handle<i::JSObject>(debug->debug_context()->global());
|
| + utility_context_->Global()->Set(String::New("$debug"),
|
| + Utils::ToLocal(js_debug));
|
| + debug->debug_context()->set_security_token(HEAP->undefined_value());
|
| +#endif
|
| +
|
| + // Run the d8 shell utility script in the utility context
|
| + int source_index = i::NativesCollection<i::D8>::GetIndex("d8");
|
| + i::Vector<const char> shell_source =
|
| + i::NativesCollection<i::D8>::GetRawScriptSource(source_index);
|
| + i::Vector<const char> shell_source_name =
|
| + i::NativesCollection<i::D8>::GetScriptName(source_index);
|
| + Handle<String> source = String::New(shell_source.start(),
|
| + shell_source.length());
|
| + Handle<String> name = String::New(shell_source_name.start(),
|
| + 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);
|
| + i::Handle<i::Script> script_object = compiled_script->IsJSFunction()
|
| + ? i::Handle<i::Script>(i::Script::cast(
|
| + i::JSFunction::cast(*compiled_script)->shared()->script()))
|
| + : 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.
|
| + if (i::FLAG_debugger && !i::FLAG_debugger_agent) {
|
| + v8::Debug::SetDebugEventListener(HandleDebugEvent);
|
| }
|
| +#endif
|
| +}
|
|
|
| - // Initialize the global objects
|
| - HandleScope scope;
|
| +
|
| +#ifdef COMPRESS_STARTUP_DATA_BZ2
|
| +class BZip2Decompressor : public v8::StartupDataDecompressor {
|
| + public:
|
| + virtual ~BZip2Decompressor() { }
|
| +
|
| + protected:
|
| + virtual int DecompressData(char* raw_data,
|
| + int* raw_data_size,
|
| + const char* compressed_data,
|
| + int compressed_data_size) {
|
| + ASSERT_EQ(v8::StartupData::kBZip2,
|
| + v8::V8::GetCompressedStartupDataAlgorithm());
|
| + unsigned int decompressed_size = *raw_data_size;
|
| + int result =
|
| + BZ2_bzBuffToBuffDecompress(raw_data,
|
| + &decompressed_size,
|
| + const_cast<char*>(compressed_data),
|
| + compressed_data_size,
|
| + 0, 1);
|
| + if (result == BZ_OK) {
|
| + *raw_data_size = decompressed_size;
|
| + }
|
| + return result;
|
| + }
|
| +};
|
| +#endif
|
| +
|
| +Handle<ObjectTemplate> Shell::CreateGlobalTemplate() {
|
| Handle<ObjectTemplate> global_template = ObjectTemplate::New();
|
| global_template->Set(String::New("print"), FunctionTemplate::New(Print));
|
| global_template->Set(String::New("write"), FunctionTemplate::New(Write));
|
| @@ -432,6 +603,26 @@
|
| global_template->Set(String::New("quit"), FunctionTemplate::New(Quit));
|
| global_template->Set(String::New("version"), FunctionTemplate::New(Version));
|
|
|
| + // Bind the handlers for external arrays.
|
| + global_template->Set(String::New("Int8Array"),
|
| + FunctionTemplate::New(Int8Array));
|
| + global_template->Set(String::New("Uint8Array"),
|
| + FunctionTemplate::New(Uint8Array));
|
| + global_template->Set(String::New("Int16Array"),
|
| + FunctionTemplate::New(Int16Array));
|
| + global_template->Set(String::New("Uint16Array"),
|
| + FunctionTemplate::New(Uint16Array));
|
| + global_template->Set(String::New("Int32Array"),
|
| + FunctionTemplate::New(Int32Array));
|
| + global_template->Set(String::New("Uint32Array"),
|
| + FunctionTemplate::New(Uint32Array));
|
| + global_template->Set(String::New("Float32Array"),
|
| + FunctionTemplate::New(Float32Array));
|
| + global_template->Set(String::New("Float64Array"),
|
| + FunctionTemplate::New(Float64Array));
|
| + global_template->Set(String::New("PixelArray"),
|
| + FunctionTemplate::New(PixelArray));
|
| +
|
| #ifdef LIVE_OBJECT_LIST
|
| global_template->Set(String::New("lol_is_enabled"), Boolean::New(true));
|
| #else
|
| @@ -442,94 +633,85 @@
|
| AddOSMethods(os_templ);
|
| global_template->Set(String::New("os"), os_templ);
|
|
|
| - utility_context_ = Context::New(NULL, global_template);
|
| - utility_context_->SetSecurityToken(Undefined());
|
| - Context::Scope utility_scope(utility_context_);
|
| + return global_template;
|
| +}
|
|
|
| - i::JSArguments js_args = i::FLAG_js_arguments;
|
| - i::Handle<i::FixedArray> arguments_array =
|
| - FACTORY->NewFixedArray(js_args.argc());
|
| - for (int j = 0; j < js_args.argc(); j++) {
|
| - i::Handle<i::String> arg =
|
| - FACTORY->NewStringFromUtf8(i::CStrVector(js_args[j]));
|
| - arguments_array->set(j, *arg);
|
| +
|
| +void Shell::Initialize() {
|
| +#ifdef COMPRESS_STARTUP_DATA_BZ2
|
| + BZip2Decompressor startup_data_decompressor;
|
| + int bz2_result = startup_data_decompressor.Decompress();
|
| + if (bz2_result != BZ_OK) {
|
| + fprintf(stderr, "bzip error code: %d\n", bz2_result);
|
| + exit(1);
|
| }
|
| - i::Handle<i::JSArray> arguments_jsarray =
|
| - FACTORY->NewJSArrayWithElements(arguments_array);
|
| - global_template->Set(String::New("arguments"),
|
| - Utils::ToLocal(arguments_jsarray));
|
| -
|
| -#ifdef ENABLE_DEBUGGER_SUPPORT
|
| - // Install the debugger object in the utility scope
|
| - i::Debug* debug = i::Isolate::Current()->debug();
|
| - debug->Load();
|
| - i::Handle<i::JSObject> js_debug
|
| - = i::Handle<i::JSObject>(debug->debug_context()->global());
|
| - utility_context_->Global()->Set(String::New("$debug"),
|
| - Utils::ToLocal(js_debug));
|
| #endif
|
|
|
| - // Run the d8 shell utility script in the utility context
|
| - int source_index = i::NativesCollection<i::D8>::GetIndex("d8");
|
| - i::Vector<const char> shell_source
|
| - = i::NativesCollection<i::D8>::GetScriptSource(source_index);
|
| - i::Vector<const char> shell_source_name
|
| - = i::NativesCollection<i::D8>::GetScriptName(source_index);
|
| - Handle<String> source = String::New(shell_source.start(),
|
| - shell_source.length());
|
| - Handle<String> name = String::New(shell_source_name.start(),
|
| - shell_source_name.length());
|
| - Handle<Script> script = Script::Compile(source, name);
|
| - script->Run();
|
| + Shell::counter_map_ = new CounterMap();
|
| + // Set up counters
|
| + if (i::StrLength(i::FLAG_map_counters) != 0)
|
| + MapCounters(i::FLAG_map_counters);
|
| + if (i::FLAG_dump_counters) {
|
| + V8::SetCounterFunction(LookupCounter);
|
| + V8::SetCreateHistogramFunction(CreateHistogram);
|
| + V8::SetAddHistogramSampleFunction(AddHistogramSample);
|
| + }
|
|
|
| - // 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);
|
| - i::Handle<i::Script> script_object = compiled_script->IsJSFunction()
|
| - ? i::Handle<i::Script>(i::Script::cast(
|
| - i::JSFunction::cast(*compiled_script)->shared()->script()))
|
| - : i::Handle<i::Script>(i::Script::cast(
|
| - i::SharedFunctionInfo::cast(*compiled_script)->script()));
|
| - script_object->set_type(i::Smi::FromInt(i::Script::TYPE_NATIVE));
|
| + if (options.test_shell) return;
|
|
|
| - // Create the evaluation context
|
| - evaluation_context_ = Context::New(NULL, global_template);
|
| - evaluation_context_->SetSecurityToken(Undefined());
|
| + Locker lock;
|
| + HandleScope scope;
|
| + Handle<ObjectTemplate> global_template = CreateGlobalTemplate();
|
| + utility_context_ = Context::New(NULL, global_template);
|
|
|
| #ifdef ENABLE_DEBUGGER_SUPPORT
|
| - // Set the security token of the debug context to allow access.
|
| - debug->debug_context()->set_security_token(HEAP->undefined_value());
|
| -
|
| // Start the debugger agent if requested.
|
| if (i::FLAG_debugger_agent) {
|
| v8::Debug::EnableAgent("d8 shell", i::FLAG_debugger_port, true);
|
| }
|
| +#endif
|
| +}
|
|
|
| - // Start the in-process debugger if requested.
|
| - if (i::FLAG_debugger && !i::FLAG_debugger_agent) {
|
| - v8::Debug::SetDebugEventListener(HandleDebugEvent);
|
| +
|
| +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
|
| + Handle<ObjectTemplate> global_template = CreateGlobalTemplate();
|
| + 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 =
|
| + FACTORY->NewFixedArray(js_args.argc());
|
| + for (int j = 0; j < js_args.argc(); j++) {
|
| + i::Handle<i::String> arg =
|
| + FACTORY->NewStringFromUtf8(i::CStrVector(js_args[j]));
|
| + arguments_array->set(j, *arg);
|
| }
|
| -#endif
|
| + i::Handle<i::JSArray> arguments_jsarray =
|
| + FACTORY->NewJSArrayWithElements(arguments_array);
|
| + 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_;
|
| @@ -537,7 +719,8 @@
|
|
|
|
|
| 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;
|
|
|
| @@ -595,6 +778,7 @@
|
| if (i::FLAG_debugger) {
|
| printf("JavaScript debugger enabled\n");
|
| }
|
| +
|
| editor->Open();
|
| while (true) {
|
| Locker locker;
|
| @@ -614,8 +798,8 @@
|
|
|
| class ShellThread : public i::Thread {
|
| public:
|
| - ShellThread(i::Isolate* isolate, int no, i::Vector<const char> files)
|
| - : Thread(isolate, "d8:ShellThread"),
|
| + ShellThread(int no, i::Vector<const char> files)
|
| + : Thread("d8:ShellThread"),
|
| no_(no), files_(files) { }
|
| virtual void Run();
|
| private:
|
| @@ -625,25 +809,6 @@
|
|
|
|
|
| void ShellThread::Run() {
|
| - // Prepare the context for this thread.
|
| - Locker locker;
|
| - HandleScope scope;
|
| - Handle<ObjectTemplate> global_template = ObjectTemplate::New();
|
| - global_template->Set(String::New("print"),
|
| - FunctionTemplate::New(Shell::Print));
|
| - global_template->Set(String::New("write"),
|
| - FunctionTemplate::New(Shell::Write));
|
| - global_template->Set(String::New("read"),
|
| - FunctionTemplate::New(Shell::Read));
|
| - global_template->Set(String::New("readline"),
|
| - FunctionTemplate::New(Shell::ReadLine));
|
| - global_template->Set(String::New("load"),
|
| - FunctionTemplate::New(Shell::Load));
|
| - global_template->Set(String::New("yield"),
|
| - FunctionTemplate::New(Shell::Yield));
|
| - global_template->Set(String::New("version"),
|
| - FunctionTemplate::New(Shell::Version));
|
| -
|
| char* ptr = const_cast<char*>(files_.start());
|
| while ((ptr != NULL) && (*ptr != '\0')) {
|
| // For each newline-separated line.
|
| @@ -655,8 +820,10 @@
|
| continue;
|
| }
|
|
|
| - Persistent<Context> thread_context = Context::New(NULL, global_template);
|
| - thread_context->SetSecurityToken(Undefined());
|
| + // 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')) {
|
| @@ -683,116 +850,316 @@
|
| }
|
|
|
|
|
| -int Shell::Main(int argc, char* argv[]) {
|
| - i::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
|
| - if (i::FLAG_help) {
|
| - return 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);
|
| +}
|
| +
|
| +
|
| +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;
|
| + }
|
| + }
|
| }
|
| - Initialize();
|
| - bool run_shell = (argc == 1);
|
| +}
|
|
|
| - // 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;
|
| +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();
|
| +}
|
| +
|
| +
|
| +void SourceGroup::WaitForThread() {
|
| + if (thread_ == NULL) return;
|
| + if (Shell::options.last_run) {
|
| + thread_->Join();
|
| + thread_ = NULL;
|
| + } else {
|
| + done_semaphore_->Wait();
|
| + }
|
| +}
|
| +
|
| +
|
| +bool Shell::SetOptions(int argc, char* argv[]) {
|
| + for (int i = 0; i < argc; i++) {
|
| + if (strcmp(argv[i], "--stress-opt") == 0) {
|
| + options.stress_opt = true;
|
| + argv[i] = NULL;
|
| + } else if (strcmp(argv[i], "--stress-deopt") == 0) {
|
| + 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.
|
| + options.stress_opt = false;
|
| + options.stress_deopt = false;
|
| + } else if (strcmp(argv[i], "--shell") == 0) {
|
| + options.interactive_shell = true;
|
| + argv[i] = NULL;
|
| + } else if (strcmp(argv[i], "--test") == 0) {
|
| + 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);
|
| +
|
| + // 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);
|
|
|
| {
|
| - // Acquire the V8 lock once initialization has finished. Since the thread
|
| - // below may spawn new threads accessing V8 holding the V8 lock here is
|
| - // mandatory.
|
| - Locker locker;
|
| - Context::Scope context_scope(evaluation_context_);
|
| - for (int i = 1; i < argc; i++) {
|
| - char* str = argv[i];
|
| - if (strcmp(str, "--shell") == 0) {
|
| - run_shell = true;
|
| - } else 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 + 1]);
|
| - if (!ExecuteString(source, file_name, false, true)) {
|
| - OnExit();
|
| - return 1;
|
| - }
|
| - i++;
|
| - } 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(i::Isolate::Current(),
|
| - threads.length(),
|
| - i::Vector<const char>(files, size));
|
| + 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);
|
| - } 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);
|
| - if (source.IsEmpty()) {
|
| - printf("Error reading '%s'\n", str);
|
| - return 1;
|
| - }
|
| - if (!ExecuteString(source, file_name, false, true)) {
|
| - OnExit();
|
| - return 1;
|
| - }
|
| }
|
| +
|
| + 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 (threads.length() > 0 && use_preemption) {
|
| - Locker::StartPreemption(preemption_interval);
|
| + 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 (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);
|
| + Testing::PrepareStressRun(i);
|
| + options.last_run = (i == stress_runs - 1);
|
| + result = RunMain(argc, argv);
|
| + }
|
| + printf("======== Full Deoptimization =======\n");
|
| + Testing::DeoptimizeAll();
|
| + } else {
|
| + result = RunMain(argc, argv);
|
| + }
|
| +
|
| #ifdef ENABLE_DEBUGGER_SUPPORT
|
| - // Run the remote debugger if requested.
|
| - if (i::FLAG_remote_debugger) {
|
| - RunRemoteDebugger(i::FLAG_debugger_port);
|
| - return 0;
|
| - }
|
| + // Run remote debugger if requested, but never on --test
|
| + if (i::FLAG_remote_debugger && !options.test_shell) {
|
| + InstallUtilityScript();
|
| + RunRemoteDebugger(i::FLAG_debugger_port);
|
| + return 0;
|
| + }
|
| #endif
|
| - }
|
| - if (run_shell)
|
| +
|
| + // Run interactive shell if explicitly requested or if no script has been
|
| + // executed, but never on --test
|
| +
|
| + if (( options.interactive_shell
|
| + || !options.script_executed )
|
| + && !options.test_shell ) {
|
| + InstallUtilityScript();
|
| RunShell();
|
| - for (int i = 0; i < threads.length(); i++) {
|
| - i::Thread* thread = threads[i];
|
| - thread->Join();
|
| - delete thread;
|
| }
|
| - OnExit();
|
| - return 0;
|
| +
|
| + V8::Dispose();
|
| +
|
| + return result;
|
| }
|
|
|
| -
|
| } // namespace v8
|
|
|
|
|
|
|