inspector-impl.cc 13.7 KB
Newer Older
1 2 3 4 5 6 7
// 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.

#include "test/inspector/inspector-impl.h"

#include "include/v8.h"
8 9

#include "src/vector.h"
10 11 12

namespace {

13
const int kInspectorClientIndex = 0;
14 15 16 17 18 19 20 21

class ChannelImpl final : public v8_inspector::V8Inspector::Channel {
 public:
  explicit ChannelImpl(InspectorClientImpl::FrontendChannel* frontend_channel)
      : frontend_channel_(frontend_channel) {}
  virtual ~ChannelImpl() = default;

 private:
22 23 24 25
  void sendResponse(
      int callId,
      std::unique_ptr<v8_inspector::StringBuffer> message) override {
    frontend_channel_->SendMessageToFrontend(message->string());
26
  }
27 28 29
  void sendNotification(
      std::unique_ptr<v8_inspector::StringBuffer> message) override {
    frontend_channel_->SendMessageToFrontend(message->string());
30 31 32 33 34 35 36
  }
  void flushProtocolNotifications() override {}

  InspectorClientImpl::FrontendChannel* frontend_channel_;
  DISALLOW_COPY_AND_ASSIGN(ChannelImpl);
};

37 38 39 40 41 42 43 44
InspectorClientImpl* InspectorClientFromContext(
    v8::Local<v8::Context> context) {
  InspectorClientImpl* inspector_client = static_cast<InspectorClientImpl*>(
      context->GetAlignedPointerFromEmbedderData(kInspectorClientIndex));
  CHECK(inspector_client);
  return inspector_client;
}

45 46 47 48 49
v8::internal::Vector<uint16_t> ToVector(v8::Local<v8::String> str) {
  v8::internal::Vector<uint16_t> buffer =
      v8::internal::Vector<uint16_t>::New(str->Length());
  str->Write(buffer.start(), 0, str->Length());
  return buffer;
50 51 52 53 54 55 56 57 58 59 60
}

void MessageHandler(v8::Local<v8::Message> message,
                    v8::Local<v8::Value> exception) {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::Local<v8::Context> context = isolate->GetEnteredContext();
  if (context.IsEmpty()) return;
  v8_inspector::V8Inspector* inspector =
      InspectorClientImpl::InspectorFromContext(context);

  v8::Local<v8::StackTrace> stack = message->GetStackTrace();
61 62
  int script_id =
      static_cast<int>(message->GetScriptOrigin().ScriptID()->Value());
63 64 65 66 67 68 69 70 71 72
  if (!stack.IsEmpty() && stack->GetFrameCount() > 0) {
    int top_script_id = stack->GetFrame(0)->GetScriptId();
    if (top_script_id == script_id) script_id = 0;
  }
  int line_number = message->GetLineNumber(context).FromMaybe(0);
  int column_number = 0;
  if (message->GetStartColumn(context).IsJust())
    column_number = message->GetStartColumn(context).FromJust() + 1;

  v8_inspector::StringView detailed_message;
73 74
  v8::internal::Vector<uint16_t> message_text_string = ToVector(message->Get());
  v8_inspector::StringView message_text(message_text_string.start(),
75
                                        message_text_string.length());
76
  v8::internal::Vector<uint16_t> url_string;
77 78
  if (message->GetScriptOrigin().ResourceName()->IsString()) {
    url_string =
79
        ToVector(message->GetScriptOrigin().ResourceName().As<v8::String>());
80
  }
81
  v8_inspector::StringView url(url_string.start(), url_string.length());
82 83 84 85 86 87

  inspector->exceptionThrown(context, message_text, exception, detailed_message,
                             url, line_number, column_number,
                             inspector->createStackTrace(stack), script_id);
}

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
v8::Local<v8::String> ToString(v8::Isolate* isolate,
                               const v8_inspector::StringView& string) {
  if (string.is8Bit())
    return v8::String::NewFromOneByte(isolate, string.characters8(),
                                      v8::NewStringType::kNormal,
                                      static_cast<int>(string.length()))
        .ToLocalChecked();
  else
    return v8::String::NewFromTwoByte(isolate, string.characters16(),
                                      v8::NewStringType::kNormal,
                                      static_cast<int>(string.length()))
        .ToLocalChecked();
}

void Print(v8::Isolate* isolate, const v8_inspector::StringView& string) {
  v8::Local<v8::String> v8_string = ToString(isolate, string);
  v8::String::Utf8Value utf8_string(v8_string);
  fwrite(*utf8_string, sizeof(**utf8_string), utf8_string.length(), stdout);
}
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
}  //  namespace

class ConnectTask : public TaskRunner::Task {
 public:
  ConnectTask(InspectorClientImpl* client, v8::base::Semaphore* ready_semaphore)
      : client_(client), ready_semaphore_(ready_semaphore) {}
  virtual ~ConnectTask() = default;

  bool is_inspector_task() final { return true; }

  void Run(v8::Isolate* isolate,
           const v8::Global<v8::Context>& global_context) {
    v8::HandleScope handle_scope(isolate);
    v8::Local<v8::Context> context = global_context.Get(isolate);
    client_->connect(context);
    if (ready_semaphore_) ready_semaphore_->Signal();
  }

 private:
  InspectorClientImpl* client_;
  v8::base::Semaphore* ready_semaphore_;
};

130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
class DisconnectTask : public TaskRunner::Task {
 public:
  explicit DisconnectTask(InspectorClientImpl* client) : client_(client) {}
  virtual ~DisconnectTask() = default;

  bool is_inspector_task() final { return true; }

  void Run(v8::Isolate* isolate,
           const v8::Global<v8::Context>& global_context) {
    client_->disconnect();
  }

 private:
  InspectorClientImpl* client_;
};

146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
class CreateContextGroupTask : public TaskRunner::Task {
 public:
  CreateContextGroupTask(InspectorClientImpl* client,
                         v8::ExtensionConfiguration* extensions,
                         v8::base::Semaphore* ready_semaphore,
                         int* context_group_id)
      : client_(client),
        extensions_(extensions),
        ready_semaphore_(ready_semaphore),
        context_group_id_(context_group_id) {}
  virtual ~CreateContextGroupTask() = default;

  bool is_inspector_task() final { return true; }

  void Run(v8::Isolate* isolate,
           const v8::Global<v8::Context>& global_context) {
    *context_group_id_ = client_->createContextGroup(extensions_);
    if (ready_semaphore_) ready_semaphore_->Signal();
  }

 private:
  InspectorClientImpl* client_;
  v8::ExtensionConfiguration* extensions_;
  v8::base::Semaphore* ready_semaphore_;
  int* context_group_id_;
};

173 174 175 176 177 178 179 180 181 182 183 184 185
InspectorClientImpl::InspectorClientImpl(TaskRunner* task_runner,
                                         FrontendChannel* frontend_channel,
                                         v8::base::Semaphore* ready_semaphore)
    : isolate_(nullptr),
      task_runner_(task_runner),
      frontend_channel_(frontend_channel) {
  task_runner_->Append(new ConnectTask(this, ready_semaphore));
}

InspectorClientImpl::~InspectorClientImpl() {}

void InspectorClientImpl::connect(v8::Local<v8::Context> context) {
  isolate_ = context->GetIsolate();
186
  isolate_->AddMessageListener(MessageHandler);
187 188 189
  channel_.reset(new ChannelImpl(frontend_channel_));
  inspector_ = v8_inspector::V8Inspector::create(isolate_, this);

190 191 192 193 194 195
  if (states_.empty()) {
    int context_group_id = TaskRunner::GetContextGroupId(context);
    v8_inspector::StringView state;
    sessions_[context_group_id] =
        inspector_->connect(context_group_id, channel_.get(), state);
    context->SetAlignedPointerInEmbedderData(kInspectorClientIndex, this);
196 197 198 199
    v8_inspector::V8ContextInfo info(context, context_group_id,
                                     v8_inspector::StringView());
    info.hasMemoryOnConsole = true;
    inspector_->contextCreated(info);
200 201 202 203 204 205 206 207 208
  } else {
    for (const auto& it : states_) {
      int context_group_id = it.first;
      v8::Local<v8::Context> context =
          task_runner_->GetContext(context_group_id);
      v8_inspector::StringView state = it.second->string();
      sessions_[context_group_id] =
          inspector_->connect(context_group_id, channel_.get(), state);
      context->SetAlignedPointerInEmbedderData(kInspectorClientIndex, this);
209 210 211 212
      v8_inspector::V8ContextInfo info(context, context_group_id,
                                       v8_inspector::StringView());
      info.hasMemoryOnConsole = true;
      inspector_->contextCreated(info);
213 214 215
    }
  }
  states_.clear();
216 217
}

218 219 220 221 222 223 224
void InspectorClientImpl::scheduleReconnect(
    v8::base::Semaphore* ready_semaphore) {
  task_runner_->Append(new DisconnectTask(this));
  task_runner_->Append(new ConnectTask(this, ready_semaphore));
}

void InspectorClientImpl::disconnect() {
225 226 227 228
  for (const auto& it : sessions_) {
    states_[it.first] = it.second->stateJSON();
  }
  sessions_.clear();
229
  inspector_.reset();
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
}

void InspectorClientImpl::scheduleCreateContextGroup(
    v8::ExtensionConfiguration* extensions,
    v8::base::Semaphore* ready_semaphore, int* context_group_id) {
  task_runner_->Append(new CreateContextGroupTask(
      this, extensions, ready_semaphore, context_group_id));
}

int InspectorClientImpl::createContextGroup(
    v8::ExtensionConfiguration* extensions) {
  v8::HandleScope handle_scope(isolate_);
  v8::Local<v8::Context> context = task_runner_->NewContextGroup();
  context->SetAlignedPointerInEmbedderData(kInspectorClientIndex, this);
  int context_group_id = TaskRunner::GetContextGroupId(context);
  v8_inspector::StringView state;
  sessions_[context_group_id] =
      inspector_->connect(context_group_id, channel_.get(), state);
  inspector_->contextCreated(v8_inspector::V8ContextInfo(
      context, context_group_id, v8_inspector::StringView()));
  return context_group_id;
251 252
}

253 254 255 256 257 258 259 260 261 262 263 264 265 266
bool InspectorClientImpl::formatAccessorsAsProperties(
    v8::Local<v8::Value> object) {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::Local<v8::Context> context = isolate->GetCurrentContext();
  v8::Local<v8::Private> shouldFormatAccessorsPrivate = v8::Private::ForApi(
      isolate, v8::String::NewFromUtf8(isolate, "allowAccessorFormatting",
                                       v8::NewStringType::kNormal)
                   .ToLocalChecked());
  CHECK(object->IsObject());
  return object.As<v8::Object>()
      ->HasPrivate(context, shouldFormatAccessorsPrivate)
      .FromMaybe(false);
}

267 268
v8::Local<v8::Context> InspectorClientImpl::ensureDefaultContextInGroup(
    int context_group_id) {
269
  CHECK(isolate_);
270
  return task_runner_->GetContext(context_group_id);
271 272
}

273 274 275 276 277
void InspectorClientImpl::setCurrentTimeMSForTest(double time) {
  current_time_ = time;
  current_time_set_for_test_ = true;
}

278
double InspectorClientImpl::currentTimeMS() {
279
  if (current_time_set_for_test_) return current_time_;
280 281 282
  return v8::base::OS::TimeCurrentMillis();
}

283 284 285 286 287
void InspectorClientImpl::setMemoryInfoForTest(
    v8::Local<v8::Value> memory_info) {
  memory_info_.Reset(isolate_, memory_info);
}

288 289 290 291
void InspectorClientImpl::setLogConsoleApiMessageCalls(bool log) {
  log_console_api_message_calls_ = log;
}

292 293 294 295 296 297
v8::MaybeLocal<v8::Value> InspectorClientImpl::memoryInfo(
    v8::Isolate* isolate, v8::Local<v8::Context>) {
  if (memory_info_.IsEmpty()) return v8::MaybeLocal<v8::Value>();
  return memory_info_.Get(isolate);
}

298 299 300 301 302 303 304 305
void InspectorClientImpl::runMessageLoopOnPause(int) {
  task_runner_->RunMessageLoop(true);
}

void InspectorClientImpl::quitMessageLoopOnPause() {
  task_runner_->QuitMessageLoop();
}

306 307 308 309 310 311 312 313 314 315 316 317 318 319
void InspectorClientImpl::consoleAPIMessage(
    int contextGroupId, v8::Isolate::MessageErrorLevel level,
    const v8_inspector::StringView& message,
    const v8_inspector::StringView& url, unsigned lineNumber,
    unsigned columnNumber, v8_inspector::V8StackTrace* stack) {
  if (!log_console_api_message_calls_) return;
  Print(isolate_, message);
  fprintf(stdout, " (");
  Print(isolate_, url);
  fprintf(stdout, ":%d:%d)", lineNumber, columnNumber);
  Print(isolate_, stack->toString()->string());
  fprintf(stdout, "\n");
}

320 321 322 323 324
v8_inspector::V8Inspector* InspectorClientImpl::InspectorFromContext(
    v8::Local<v8::Context> context) {
  return InspectorClientFromContext(context)->inspector_.get();
}

325 326
v8_inspector::V8InspectorSession* InspectorClientImpl::SessionFromContext(
    v8::Local<v8::Context> context) {
327 328 329 330 331 332 333 334 335 336 337
  int context_group_id = TaskRunner::GetContextGroupId(context);
  return InspectorClientFromContext(context)->sessions_[context_group_id].get();
}

v8_inspector::V8InspectorSession* InspectorClientImpl::session(
    int context_group_id) {
  if (context_group_id) {
    return sessions_[context_group_id].get();
  } else {
    return sessions_.begin()->second.get();
  }
338 339 340 341
}

class SendMessageToBackendTask : public TaskRunner::Task {
 public:
342
  explicit SendMessageToBackendTask(
343 344
      const v8::internal::Vector<uint16_t>& message, int context_group_id)
      : message_(message), context_group_id_(context_group_id) {}
345 346 347 348 349 350 351 352 353

  bool is_inspector_task() final { return true; }

  void Run(v8::Isolate* isolate,
           const v8::Global<v8::Context>& global_context) override {
    v8_inspector::V8InspectorSession* session = nullptr;
    {
      v8::HandleScope handle_scope(isolate);
      v8::Local<v8::Context> context = global_context.Get(isolate);
354 355 356 357 358 359 360
      if (!context_group_id_) {
        session = InspectorClientImpl::SessionFromContext(context);
      } else {
        session = InspectorClientFromContext(context)
                      ->sessions_[context_group_id_]
                      .get();
      }
361 362
      CHECK(session);
    }
363
    v8_inspector::StringView message_view(message_.start(), message_.length());
364 365 366 367
    session->dispatchProtocolMessage(message_view);
  }

 private:
368
  v8::internal::Vector<uint16_t> message_;
369
  int context_group_id_;
370 371 372 373 374 375 376 377 378 379 380 381 382 383
};

TaskRunner* SendMessageToBackendExtension::backend_task_runner_ = nullptr;

v8::Local<v8::FunctionTemplate>
SendMessageToBackendExtension::GetNativeFunctionTemplate(
    v8::Isolate* isolate, v8::Local<v8::String> name) {
  return v8::FunctionTemplate::New(
      isolate, SendMessageToBackendExtension::SendMessageToBackend);
}

void SendMessageToBackendExtension::SendMessageToBackend(
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  CHECK(backend_task_runner_);
384
  CHECK(args.Length() == 2 && args[0]->IsString() && args[1]->IsInt32());
385
  v8::Local<v8::String> message = args[0].As<v8::String>();
386 387
  backend_task_runner_->Append(new SendMessageToBackendTask(
      ToVector(message), args[1].As<v8::Int32>()->Value()));
388
}