// Copyright 2016 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. #if !defined(_WIN32) && !defined(_WIN64) #include // NOLINT #endif // !defined(_WIN32) && !defined(_WIN64) #include #include "include/libplatform/libplatform.h" #include "include/v8.h" #include "src/base/platform/platform.h" #include "src/flags.h" #include "src/utils.h" #include "src/vector.h" #include "test/inspector/inspector-impl.h" #include "test/inspector/task-runner.h" namespace { std::vector task_runners; void Terminate() { for (size_t i = 0; i < task_runners.size(); ++i) { task_runners[i]->Terminate(); task_runners[i]->Join(); } std::vector empty; task_runners.swap(empty); } void Exit() { fflush(stdout); fflush(stderr); Terminate(); } class UtilsExtension : public v8::Extension { public: UtilsExtension() : v8::Extension("v8_inspector/utils", "native function print();" "native function quit();" "native function setlocale();" "native function load();") {} virtual v8::Local GetNativeFunctionTemplate( v8::Isolate* isolate, v8::Local name) { v8::Local context = isolate->GetCurrentContext(); if (name->Equals(context, v8::String::NewFromUtf8( isolate, "print", v8::NewStringType::kNormal) .ToLocalChecked()) .FromJust()) { return v8::FunctionTemplate::New(isolate, UtilsExtension::Print); } else if (name->Equals(context, v8::String::NewFromUtf8(isolate, "quit", v8::NewStringType::kNormal) .ToLocalChecked()) .FromJust()) { return v8::FunctionTemplate::New(isolate, UtilsExtension::Quit); } else if (name->Equals(context, v8::String::NewFromUtf8(isolate, "setlocale", v8::NewStringType::kNormal) .ToLocalChecked()) .FromJust()) { return v8::FunctionTemplate::New(isolate, UtilsExtension::SetLocale); } else if (name->Equals(context, v8::String::NewFromUtf8(isolate, "load", v8::NewStringType::kNormal) .ToLocalChecked()) .FromJust()) { return v8::FunctionTemplate::New(isolate, UtilsExtension::Load); } return v8::Local(); } private: static void Print(const v8::FunctionCallbackInfo& args) { for (int i = 0; i < args.Length(); i++) { v8::HandleScope handle_scope(args.GetIsolate()); if (i != 0) { printf(" "); } // Explicitly catch potential exceptions in toString(). v8::TryCatch try_catch(args.GetIsolate()); v8::Local arg = args[i]; v8::Local str_obj; if (arg->IsSymbol()) { arg = v8::Local::Cast(arg)->Name(); } if (!arg->ToString(args.GetIsolate()->GetCurrentContext()) .ToLocal(&str_obj)) { try_catch.ReThrow(); return; } v8::String::Utf8Value str(str_obj); int n = static_cast(fwrite(*str, sizeof(**str), str.length(), stdout)); if (n != str.length()) { printf("Error in fwrite\n"); Quit(args); } } printf("\n"); fflush(stdout); } static void Quit(const v8::FunctionCallbackInfo& args) { Exit(); } static void SetLocale(const v8::FunctionCallbackInfo& args) { if (args.Length() != 1 || !args[0]->IsString()) { fprintf(stderr, "Internal error: setlocale get one string argument."); Exit(); } v8::String::Utf8Value str(args[0]); setlocale(LC_NUMERIC, *str); } static void Load(const v8::FunctionCallbackInfo& args) { if (args.Length() != 1 || !args[0]->IsString()) { fprintf(stderr, "Internal error: load gets one string argument."); Exit(); } v8::String::Utf8Value str(args[0]); v8::Isolate* isolate = args.GetIsolate(); bool exists = false; std::string filename(*str, str.length()); v8::internal::Vector chars = v8::internal::ReadFile(filename.c_str(), &exists); if (!exists) { isolate->ThrowException( v8::String::NewFromUtf8(isolate, "Error loading file", v8::NewStringType::kNormal) .ToLocalChecked()); return; } ExecuteStringTask task(chars); v8::Global context(isolate, isolate->GetCurrentContext()); task.Run(isolate, context); } }; class SetTimeoutTask : public TaskRunner::Task { public: SetTimeoutTask(v8::Isolate* isolate, v8::Local function) : function_(isolate, function) {} virtual ~SetTimeoutTask() {} bool is_inspector_task() final { return false; } void Run(v8::Isolate* isolate, const v8::Global& global_context) override { v8::MicrotasksScope microtasks_scope(isolate, v8::MicrotasksScope::kRunMicrotasks); v8::HandleScope handle_scope(isolate); v8::Local context = global_context.Get(isolate); v8::Context::Scope context_scope(context); v8::Local function = function_.Get(isolate); v8::MaybeLocal result; v8_inspector::V8Inspector* inspector = InspectorClientImpl::InspectorFromContext(context); if (inspector) inspector->willExecuteScript(context, function->ScriptId()); result = function->Call(context, context->Global(), 0, nullptr); if (inspector) inspector->didExecuteScript(context); } private: v8::Global function_; }; v8::internal::Vector ToVector(v8::Local str) { v8::internal::Vector buffer = v8::internal::Vector::New(str->Length()); str->Write(buffer.start(), 0, str->Length()); return buffer; } class SetTimeoutExtension : public v8::Extension { public: SetTimeoutExtension() : v8::Extension("v8_inspector/setTimeout", "native function setTimeout();") {} virtual v8::Local GetNativeFunctionTemplate( v8::Isolate* isolate, v8::Local name) { return v8::FunctionTemplate::New(isolate, SetTimeoutExtension::SetTimeout); } private: static void SetTimeout(const v8::FunctionCallbackInfo& args) { if (args.Length() != 2 || !args[1]->IsNumber() || (!args[0]->IsFunction() && !args[0]->IsString()) || args[1].As()->Value() != 0.0) { fprintf(stderr, "Internal error: only setTimeout(function, 0) is supported."); Exit(); } v8::Local context = args.GetIsolate()->GetCurrentContext(); if (args[0]->IsFunction()) { TaskRunner::FromContext(context)->Append(new SetTimeoutTask( args.GetIsolate(), v8::Local::Cast(args[0]))); } else { TaskRunner::FromContext(context)->Append( new ExecuteStringTask(ToVector(args[0].As()))); } } }; class InspectorExtension : public v8::Extension { public: InspectorExtension() : v8::Extension("v8_inspector/inspector", "native function attachInspector();" "native function detachInspector();") {} virtual v8::Local GetNativeFunctionTemplate( v8::Isolate* isolate, v8::Local name) { v8::Local context = isolate->GetCurrentContext(); if (name->Equals(context, v8::String::NewFromUtf8(isolate, "attachInspector", v8::NewStringType::kNormal) .ToLocalChecked()) .FromJust()) { return v8::FunctionTemplate::New(isolate, InspectorExtension::Attach); } else if (name->Equals(context, v8::String::NewFromUtf8(isolate, "detachInspector", v8::NewStringType::kNormal) .ToLocalChecked()) .FromJust()) { return v8::FunctionTemplate::New(isolate, InspectorExtension::Detach); } return v8::Local(); } private: static void Attach(const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::Local context = isolate->GetCurrentContext(); v8_inspector::V8Inspector* inspector = InspectorClientImpl::InspectorFromContext(context); if (!inspector) { fprintf(stderr, "Inspector client not found - cannot attach!"); Exit(); } inspector->contextCreated( v8_inspector::V8ContextInfo(context, 1, v8_inspector::StringView())); } static void Detach(const v8::FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); v8::Local context = isolate->GetCurrentContext(); v8_inspector::V8Inspector* inspector = InspectorClientImpl::InspectorFromContext(context); if (!inspector) { fprintf(stderr, "Inspector client not found - cannot detach!"); Exit(); } inspector->contextDestroyed(context); } }; v8::Local ToString(v8::Isolate* isolate, const v8_inspector::StringView& string) { if (string.is8Bit()) return v8::String::NewFromOneByte(isolate, string.characters8(), v8::NewStringType::kNormal, static_cast(string.length())) .ToLocalChecked(); else return v8::String::NewFromTwoByte(isolate, string.characters16(), v8::NewStringType::kNormal, static_cast(string.length())) .ToLocalChecked(); } class FrontendChannelImpl : public InspectorClientImpl::FrontendChannel { public: explicit FrontendChannelImpl(TaskRunner* frontend_task_runner) : frontend_task_runner_(frontend_task_runner) {} virtual ~FrontendChannelImpl() {} void SendMessageToFrontend(const v8_inspector::StringView& message) final { v8::Isolate* isolate = v8::Isolate::GetCurrent(); v8::HandleScope scope(v8::Isolate::GetCurrent()); v8::Local prefix = v8::String::NewFromUtf8(isolate, "InspectorTest._dispatchMessage(", v8::NewStringType::kInternalized) .ToLocalChecked(); v8::Local message_string = ToString(isolate, message); v8::Local suffix = v8::String::NewFromUtf8(isolate, ")", v8::NewStringType::kInternalized) .ToLocalChecked(); v8::Local result = v8::String::Concat(prefix, message_string); result = v8::String::Concat(result, suffix); frontend_task_runner_->Append(new ExecuteStringTask(ToVector(result))); } private: TaskRunner* frontend_task_runner_; }; } // namespace int main(int argc, char* argv[]) { v8::V8::InitializeICUDefaultLocation(argv[0]); v8::Platform* platform = v8::platform::CreateDefaultPlatform(); v8::V8::InitializePlatform(platform); v8::V8::SetFlagsFromCommandLine(&argc, argv, true); v8::V8::InitializeExternalStartupData(argv[0]); v8::V8::Initialize(); SetTimeoutExtension set_timeout_extension; v8::RegisterExtension(&set_timeout_extension); InspectorExtension inspector_extension; v8::RegisterExtension(&inspector_extension); UtilsExtension utils_extension; v8::RegisterExtension(&utils_extension); SendMessageToBackendExtension send_message_to_backend_extension; v8::RegisterExtension(&send_message_to_backend_extension); v8::base::Semaphore ready_semaphore(0); const char* backend_extensions[] = {"v8_inspector/setTimeout", "v8_inspector/inspector"}; v8::ExtensionConfiguration backend_configuration( arraysize(backend_extensions), backend_extensions); TaskRunner backend_runner(&backend_configuration, false, &ready_semaphore); ready_semaphore.Wait(); SendMessageToBackendExtension::set_backend_task_runner(&backend_runner); const char* frontend_extensions[] = {"v8_inspector/utils", "v8_inspector/frontend"}; v8::ExtensionConfiguration frontend_configuration( arraysize(frontend_extensions), frontend_extensions); TaskRunner frontend_runner(&frontend_configuration, true, &ready_semaphore); ready_semaphore.Wait(); FrontendChannelImpl frontend_channel(&frontend_runner); InspectorClientImpl inspector_client(&backend_runner, &frontend_channel, &ready_semaphore); ready_semaphore.Wait(); task_runners.push_back(&frontend_runner); task_runners.push_back(&backend_runner); for (int i = 1; i < argc; ++i) { if (argv[i][0] == '-') break; bool exists = false; v8::internal::Vector chars = v8::internal::ReadFile(argv[i], &exists, true); if (!exists) { fprintf(stderr, "Internal error: script file doesn't exists: %s\n", argv[i]); Exit(); } frontend_runner.Append(new ExecuteStringTask(chars)); } frontend_runner.Join(); backend_runner.Join(); return 0; }