// Copyright 2012 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include #include #include #include #include #include #ifdef ENABLE_VTUNE_JIT_INTERFACE #include "src/third_party/vtune/v8-vtune.h" #endif #include "src/d8.h" #include "src/ostreams.h" #include "include/libplatform/libplatform.h" #include "include/libplatform/v8-tracing.h" #include "src/api.h" #include "src/base/cpu.h" #include "src/base/debug/stack_trace.h" #include "src/base/logging.h" #include "src/base/platform/platform.h" #include "src/base/sys-info.h" #include "src/basic-block-profiler.h" #include "src/interpreter/interpreter.h" #include "src/snapshot/natives.h" #include "src/utils.h" #include "src/v8.h" #if !defined(_WIN32) && !defined(_WIN64) #include // NOLINT #else #include // NOLINT #if defined(_MSC_VER) #include // NOLINT #endif // defined(_MSC_VER) #endif // !defined(_WIN32) && !defined(_WIN64) #ifndef DCHECK #define DCHECK(condition) assert(condition) #endif #ifndef CHECK #define CHECK(condition) assert(condition) #endif namespace v8 { namespace { const int MB = 1024 * 1024; const int kMaxWorkers = 50; class ShellArrayBufferAllocator : public v8::ArrayBuffer::Allocator { public: virtual void* Allocate(size_t length) { void* data = AllocateUninitialized(length); return data == NULL ? data : memset(data, 0, length); } virtual void* AllocateUninitialized(size_t length) { return malloc(length); } virtual void Free(void* data, size_t) { free(data); } }; class MockArrayBufferAllocator : public v8::ArrayBuffer::Allocator { public: void* Allocate(size_t length) override { size_t actual_length = length > 10 * MB ? 1 : length; void* data = AllocateUninitialized(actual_length); return data == NULL ? data : memset(data, 0, actual_length); } void* AllocateUninitialized(size_t length) override { return length > 10 * MB ? malloc(1) : malloc(length); } void Free(void* p, size_t) override { free(p); } }; // Predictable v8::Platform implementation. All background and foreground // tasks are run immediately, delayed tasks are not executed at all. class PredictablePlatform : public Platform { public: PredictablePlatform() {} void CallOnBackgroundThread(Task* task, ExpectedRuntime expected_runtime) override { task->Run(); delete task; } void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override { task->Run(); delete task; } void CallDelayedOnForegroundThread(v8::Isolate* isolate, Task* task, double delay_in_seconds) override { delete task; } void CallIdleOnForegroundThread(v8::Isolate* isolate, IdleTask* task) override { UNREACHABLE(); } bool IdleTasksEnabled(v8::Isolate* isolate) override { return false; } double MonotonicallyIncreasingTime() override { return synthetic_time_in_sec_ += 0.00001; } uint64_t AddTraceEvent(char phase, const uint8_t* categoryEnabledFlag, const char* name, const char* scope, uint64_t id, uint64_t bind_id, int numArgs, const char** argNames, const uint8_t* argTypes, const uint64_t* argValues, unsigned int flags) override { return 0; } void UpdateTraceEventDuration(const uint8_t* categoryEnabledFlag, const char* name, uint64_t handle) override {} const uint8_t* GetCategoryGroupEnabled(const char* name) override { static uint8_t no = 0; return &no; } const char* GetCategoryGroupName( const uint8_t* categoryEnabledFlag) override { static const char* dummy = "dummy"; return dummy; } private: double synthetic_time_in_sec_ = 0.0; DISALLOW_COPY_AND_ASSIGN(PredictablePlatform); }; v8::Platform* g_platform = NULL; static Local Throw(Isolate* isolate, const char* message) { return isolate->ThrowException( String::NewFromUtf8(isolate, message, NewStringType::kNormal) .ToLocalChecked()); } bool FindInObjectList(Local object, const Shell::ObjectList& list) { for (int i = 0; i < list.length(); ++i) { if (list[i]->StrictEquals(object)) { return true; } } return false; } Worker* GetWorkerFromInternalField(Isolate* isolate, Local object) { if (object->InternalFieldCount() != 1) { Throw(isolate, "this is not a Worker"); return NULL; } Worker* worker = static_cast(object->GetAlignedPointerFromInternalField(0)); if (worker == NULL) { Throw(isolate, "Worker is defunct because main thread is terminating"); return NULL; } return worker; } } // namespace namespace tracing { namespace { // String options that can be used to initialize TraceOptions. const char kRecordUntilFull[] = "record-until-full"; const char kRecordContinuously[] = "record-continuously"; const char kRecordAsMuchAsPossible[] = "record-as-much-as-possible"; const char kRecordModeParam[] = "record_mode"; const char kEnableSamplingParam[] = "enable_sampling"; const char kEnableSystraceParam[] = "enable_systrace"; const char kEnableArgumentFilterParam[] = "enable_argument_filter"; const char kIncludedCategoriesParam[] = "included_categories"; const char kExcludedCategoriesParam[] = "excluded_categories"; class TraceConfigParser { public: static void FillTraceConfig(v8::Isolate* isolate, platform::tracing::TraceConfig* trace_config, const char* json_str) { HandleScope outer_scope(isolate); Local context = Context::New(isolate); Context::Scope context_scope(context); HandleScope inner_scope(isolate); Local source = String::NewFromUtf8(isolate, json_str, NewStringType::kNormal) .ToLocalChecked(); Local result = JSON::Parse(context, source).ToLocalChecked(); Local trace_config_object = Local::Cast(result); trace_config->SetTraceRecordMode( GetTraceRecordMode(isolate, context, trace_config_object)); if (GetBoolean(isolate, context, trace_config_object, kEnableSamplingParam)) { trace_config->EnableSampling(); } if (GetBoolean(isolate, context, trace_config_object, kEnableSystraceParam)) { trace_config->EnableSystrace(); } if (GetBoolean(isolate, context, trace_config_object, kEnableArgumentFilterParam)) { trace_config->EnableArgumentFilter(); } UpdateCategoriesList(isolate, context, trace_config_object, kIncludedCategoriesParam, trace_config); UpdateCategoriesList(isolate, context, trace_config_object, kExcludedCategoriesParam, trace_config); } private: static bool GetBoolean(v8::Isolate* isolate, Local context, Local object, const char* property) { Local value = GetValue(isolate, context, object, property); if (value->IsNumber()) { Local v8_boolean = value->ToBoolean(context).ToLocalChecked(); return v8_boolean->Value(); } return false; } static int UpdateCategoriesList( v8::Isolate* isolate, Local context, Local object, const char* property, platform::tracing::TraceConfig* trace_config) { Local value = GetValue(isolate, context, object, property); if (value->IsArray()) { Local v8_array = Local::Cast(value); for (int i = 0, length = v8_array->Length(); i < length; ++i) { Local v = v8_array->Get(context, i) .ToLocalChecked() ->ToString(context) .ToLocalChecked(); String::Utf8Value str(v->ToString(context).ToLocalChecked()); if (kIncludedCategoriesParam == property) { trace_config->AddIncludedCategory(*str); } else { trace_config->AddExcludedCategory(*str); } } return v8_array->Length(); } return 0; } static platform::tracing::TraceRecordMode GetTraceRecordMode( v8::Isolate* isolate, Local context, Local object) { Local value = GetValue(isolate, context, object, kRecordModeParam); if (value->IsString()) { Local v8_string = value->ToString(context).ToLocalChecked(); String::Utf8Value str(v8_string); if (strcmp(kRecordUntilFull, *str) == 0) { return platform::tracing::TraceRecordMode::RECORD_UNTIL_FULL; } else if (strcmp(kRecordContinuously, *str) == 0) { return platform::tracing::TraceRecordMode::RECORD_CONTINUOUSLY; } else if (strcmp(kRecordAsMuchAsPossible, *str) == 0) { return platform::tracing::TraceRecordMode::RECORD_AS_MUCH_AS_POSSIBLE; } } return platform::tracing::TraceRecordMode::RECORD_UNTIL_FULL; } static Local GetValue(v8::Isolate* isolate, Local context, Local object, const char* property) { Local v8_str = String::NewFromUtf8(isolate, property, NewStringType::kNormal) .ToLocalChecked(); return object->Get(context, v8_str).ToLocalChecked(); } }; } // namespace static platform::tracing::TraceConfig* CreateTraceConfigFromJSON( v8::Isolate* isolate, const char* json_str) { platform::tracing::TraceConfig* trace_config = new platform::tracing::TraceConfig(); TraceConfigParser::FillTraceConfig(isolate, trace_config, json_str); return trace_config; } } // namespace tracing class PerIsolateData { public: explicit PerIsolateData(Isolate* isolate) : isolate_(isolate), realms_(NULL) { HandleScope scope(isolate); isolate->SetData(0, this); } ~PerIsolateData() { isolate_->SetData(0, NULL); // Not really needed, just to be sure... } inline static PerIsolateData* Get(Isolate* isolate) { return reinterpret_cast(isolate->GetData(0)); } class RealmScope { public: explicit RealmScope(PerIsolateData* data); ~RealmScope(); private: PerIsolateData* data_; }; private: friend class Shell; friend class RealmScope; Isolate* isolate_; int realm_count_; int realm_current_; int realm_switch_; Global* realms_; Global realm_shared_; int RealmIndexOrThrow(const v8::FunctionCallbackInfo& args, int arg_offset); int RealmFind(Local context); }; CounterMap* Shell::counter_map_; base::OS::MemoryMappedFile* Shell::counters_file_ = NULL; CounterCollection Shell::local_counters_; CounterCollection* Shell::counters_ = &local_counters_; base::LazyMutex Shell::context_mutex_; const base::TimeTicks Shell::kInitialTicks = base::TimeTicks::HighResolutionNow(); Global Shell::stringify_function_; base::LazyMutex Shell::workers_mutex_; bool Shell::allow_new_workers_ = true; i::List Shell::workers_; i::List Shell::externalized_shared_contents_; Global Shell::evaluation_context_; ArrayBuffer::Allocator* Shell::array_buffer_allocator; ShellOptions Shell::options; base::OnceType Shell::quit_once_ = V8_ONCE_INIT; bool CounterMap::Match(void* key1, void* key2) { const char* name1 = reinterpret_cast(key1); const char* name2 = reinterpret_cast(key2); return strcmp(name1, name2) == 0; } // Converts a V8 value to a C string. const char* Shell::ToCString(const v8::String::Utf8Value& value) { return *value ? *value : ""; } ScriptCompiler::CachedData* CompileForCachedData( Local source, Local name, ScriptCompiler::CompileOptions compile_options) { int source_length = source->Length(); uint16_t* source_buffer = new uint16_t[source_length]; source->Write(source_buffer, 0, source_length); int name_length = 0; uint16_t* name_buffer = NULL; if (name->IsString()) { Local name_string = Local::Cast(name); name_length = name_string->Length(); name_buffer = new uint16_t[name_length]; name_string->Write(name_buffer, 0, name_length); } Isolate::CreateParams create_params; create_params.array_buffer_allocator = Shell::array_buffer_allocator; Isolate* temp_isolate = Isolate::New(create_params); ScriptCompiler::CachedData* result = NULL; { Isolate::Scope isolate_scope(temp_isolate); HandleScope handle_scope(temp_isolate); Context::Scope context_scope(Context::New(temp_isolate)); Local source_copy = v8::String::NewFromTwoByte(temp_isolate, source_buffer, v8::NewStringType::kNormal, source_length).ToLocalChecked(); Local name_copy; if (name_buffer) { name_copy = v8::String::NewFromTwoByte(temp_isolate, name_buffer, v8::NewStringType::kNormal, name_length).ToLocalChecked(); } else { name_copy = v8::Undefined(temp_isolate); } ScriptCompiler::Source script_source(source_copy, ScriptOrigin(name_copy)); if (!ScriptCompiler::CompileUnboundScript(temp_isolate, &script_source, compile_options).IsEmpty() && script_source.GetCachedData()) { int length = script_source.GetCachedData()->length; uint8_t* cache = new uint8_t[length]; memcpy(cache, script_source.GetCachedData()->data, length); result = new ScriptCompiler::CachedData( cache, length, ScriptCompiler::CachedData::BufferOwned); } } temp_isolate->Dispose(); delete[] source_buffer; delete[] name_buffer; return result; } // Compile a string within the current v8 context. MaybeLocal