isolate-data.cc 18.8 KB
Newer Older
1 2 3 4 5 6
// Copyright 2017 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/isolate-data.h"

7
#include "src/inspector/test-interface.h"
8
#include "src/utils/vector.h"
9
#include "test/inspector/task-runner.h"
10 11 12 13
#include "test/inspector/utils.h"

namespace v8 {
namespace internal {
14 15 16 17

namespace {

const int kIsolateDataIndex = 2;
18
const int kContextGroupIdIndex = 3;
19

20
void Print(v8::Isolate* isolate, const v8_inspector::StringView& string) {
21
  v8::Local<v8::String> v8_string = ToV8String(isolate, string);
22
  v8::String::Utf8Value utf8_string(isolate, v8_string);
23 24 25
  fwrite(*utf8_string, sizeof(**utf8_string), utf8_string.length(), stdout);
}

26 27 28 29
class Inspectable : public v8_inspector::V8InspectorSession::Inspectable {
 public:
  Inspectable(v8::Isolate* isolate, v8::Local<v8::Value> object)
      : object_(isolate, object) {}
30
  ~Inspectable() override = default;
31 32 33 34 35 36 37 38
  v8::Local<v8::Value> get(v8::Local<v8::Context> context) override {
    return object_.Get(context->GetIsolate());
  }

 private:
  v8::Global<v8::Value> object_;
};

39 40 41 42
}  //  namespace

IsolateData::IsolateData(TaskRunner* task_runner,
                         IsolateData::SetupGlobalTasks setup_global_tasks,
43 44
                         v8::StartupData* startup_data,
                         WithInspector with_inspector)
45
    : task_runner_(task_runner),
46
      setup_global_tasks_(std::move(setup_global_tasks)) {
47
  v8::Isolate::CreateParams params;
48 49 50
  array_buffer_allocator_.reset(
      v8::ArrayBuffer::Allocator::NewDefaultAllocator());
  params.array_buffer_allocator = array_buffer_allocator_.get();
51
  params.snapshot_blob = startup_data;
52
  params.only_terminate_in_safe_scope = true;
53
  isolate_.reset(v8::Isolate::New(params));
54
  isolate_->SetMicrotasksPolicy(v8::MicrotasksPolicy::kScoped);
55
  if (with_inspector) {
56
    isolate_->AddMessageListener(&IsolateData::MessageHandler);
57
    isolate_->SetPromiseRejectCallback(&IsolateData::PromiseRejectHandler);
58
    inspector_ = v8_inspector::V8Inspector::create(isolate_.get(), this);
59
  }
60
  v8::HandleScope handle_scope(isolate_.get());
61
  not_inspectable_private_.Reset(
62
      isolate_.get(),
63 64 65
      v8::Private::ForApi(
          isolate_.get(),
          v8::String::NewFromUtf8Literal(isolate_.get(), "notInspectable")));
66 67 68 69 70 71 72 73
}

IsolateData* IsolateData::FromContext(v8::Local<v8::Context> context) {
  return static_cast<IsolateData*>(
      context->GetAlignedPointerFromEmbedderData(kIsolateDataIndex));
}

int IsolateData::CreateContextGroup() {
74
  int context_group_id = ++last_context_group_id_;
75 76 77 78
  if (!CreateContext(context_group_id, v8_inspector::StringView())) {
    DCHECK(isolate_->IsExecutionTerminating());
    return -1;
  }
79 80 81
  return context_group_id;
}

82
bool IsolateData::CreateContext(int context_group_id,
83
                                v8_inspector::StringView name) {
84
  v8::HandleScope handle_scope(isolate_.get());
85
  v8::Local<v8::ObjectTemplate> global_template =
86
      v8::ObjectTemplate::New(isolate_.get());
87 88
  for (auto it = setup_global_tasks_.begin(); it != setup_global_tasks_.end();
       ++it) {
89
    (*it)->Run(isolate_.get(), global_template);
90 91
  }
  v8::Local<v8::Context> context =
92
      v8::Context::New(isolate_.get(), nullptr, global_template);
93
  if (context.IsEmpty()) return false;
94
  context->SetAlignedPointerInEmbedderData(kIsolateDataIndex, this);
95 96 97
  // Should be 2-byte aligned.
  context->SetAlignedPointerInEmbedderData(
      kContextGroupIdIndex, reinterpret_cast<void*>(context_group_id * 2));
98 99
  contexts_[context_group_id].emplace_back(isolate_.get(), context);
  if (inspector_) FireContextCreated(context, context_group_id, name);
100
  return true;
101 102
}

103 104
v8::Local<v8::Context> IsolateData::GetDefaultContext(int context_group_id) {
  return contexts_[context_group_id].begin()->Get(isolate_.get());
105 106
}

107
void IsolateData::ResetContextGroup(int context_group_id) {
108
  v8::SealHandleScope seal_handle_scope(isolate());
109 110 111
  inspector_->resetContextGroup(context_group_id);
}

112 113 114 115 116 117 118
int IsolateData::GetContextGroupId(v8::Local<v8::Context> context) {
  return static_cast<int>(
      reinterpret_cast<intptr_t>(
          context->GetAlignedPointerFromEmbedderData(kContextGroupIdIndex)) /
      2);
}

119
void IsolateData::RegisterModule(v8::Local<v8::Context> context,
120
                                 std::vector<uint16_t> name,
121 122 123 124
                                 v8::ScriptCompiler::Source* source) {
  v8::Local<v8::Module> module;
  if (!v8::ScriptCompiler::CompileModule(isolate(), source).ToLocal(&module))
    return;
125 126
  if (!module->InstantiateModule(context, &IsolateData::ModuleResolveCallback)
           .FromMaybe(false)) {
127
    return;
128
  }
129 130
  v8::Local<v8::Value> result;
  if (!module->Evaluate(context).ToLocal(&result)) return;
131
  modules_[name] = v8::Global<v8::Module>(isolate_.get(), module);
132 133
}

134
// static
135 136
v8::MaybeLocal<v8::Module> IsolateData::ModuleResolveCallback(
    v8::Local<v8::Context> context, v8::Local<v8::String> specifier,
137
    v8::Local<v8::FixedArray> import_assertions,
138
    v8::Local<v8::Module> referrer) {
139
  // TODO(v8:11189) Consider JSON modules support in the InspectorClient
140
  IsolateData* data = IsolateData::FromContext(context);
141
  std::string str = *v8::String::Utf8Value(data->isolate(), specifier);
142 143 144 145 146 147 148 149
  v8::MaybeLocal<v8::Module> maybe_module =
      data->modules_[ToVector(data->isolate(), specifier)].Get(data->isolate());
  if (maybe_module.IsEmpty()) {
    data->isolate()->ThrowError(v8::String::Concat(
        data->isolate(),
        ToV8String(data->isolate(), "Failed to resolve module: "), specifier));
  }
  return maybe_module;
150
}
151 152

int IsolateData::ConnectSession(int context_group_id,
153 154
                                const v8_inspector::StringView& state,
                                v8_inspector::V8Inspector::Channel* channel) {
155
  v8::SealHandleScope seal_handle_scope(isolate());
156
  int session_id = ++last_session_id_;
157
  sessions_[session_id] = inspector_->connect(context_group_id, channel, state);
158 159 160 161
  context_group_by_session_[sessions_[session_id].get()] = context_group_id;
  return session_id;
}

162
std::vector<uint8_t> IsolateData::DisconnectSession(int session_id) {
163
  v8::SealHandleScope seal_handle_scope(isolate());
164 165 166
  auto it = sessions_.find(session_id);
  CHECK(it != sessions_.end());
  context_group_by_session_.erase(it->second.get());
167
  std::vector<uint8_t> result = it->second->state();
168 169 170 171 172 173
  sessions_.erase(it);
  return result;
}

void IsolateData::SendMessage(int session_id,
                              const v8_inspector::StringView& message) {
174
  v8::SealHandleScope seal_handle_scope(isolate());
175 176 177 178 179 180 181
  auto it = sessions_.find(session_id);
  if (it != sessions_.end()) it->second->dispatchProtocolMessage(message);
}

void IsolateData::BreakProgram(int context_group_id,
                               const v8_inspector::StringView& reason,
                               const v8_inspector::StringView& details) {
182
  v8::SealHandleScope seal_handle_scope(isolate());
183 184 185 186 187 188 189 190 191
  for (int session_id : GetSessionIds(context_group_id)) {
    auto it = sessions_.find(session_id);
    if (it != sessions_.end()) it->second->breakProgram(reason, details);
  }
}

void IsolateData::SchedulePauseOnNextStatement(
    int context_group_id, const v8_inspector::StringView& reason,
    const v8_inspector::StringView& details) {
192
  v8::SealHandleScope seal_handle_scope(isolate());
193 194 195 196 197 198 199 200
  for (int session_id : GetSessionIds(context_group_id)) {
    auto it = sessions_.find(session_id);
    if (it != sessions_.end())
      it->second->schedulePauseOnNextStatement(reason, details);
  }
}

void IsolateData::CancelPauseOnNextStatement(int context_group_id) {
201
  v8::SealHandleScope seal_handle_scope(isolate());
202 203 204 205 206 207
  for (int session_id : GetSessionIds(context_group_id)) {
    auto it = sessions_.find(session_id);
    if (it != sessions_.end()) it->second->cancelPauseOnNextStatement();
  }
}

208 209
void IsolateData::AsyncTaskScheduled(const v8_inspector::StringView& name,
                                     void* task, bool recurring) {
210
  v8::SealHandleScope seal_handle_scope(isolate());
211 212 213 214
  inspector_->asyncTaskScheduled(name, task, recurring);
}

void IsolateData::AsyncTaskStarted(void* task) {
215
  v8::SealHandleScope seal_handle_scope(isolate());
216 217 218 219
  inspector_->asyncTaskStarted(task);
}

void IsolateData::AsyncTaskFinished(void* task) {
220
  v8::SealHandleScope seal_handle_scope(isolate());
221 222 223
  inspector_->asyncTaskFinished(task);
}

224 225
v8_inspector::V8StackTraceId IsolateData::StoreCurrentStackTrace(
    const v8_inspector::StringView& description) {
226
  v8::SealHandleScope seal_handle_scope(isolate());
227 228 229 230 231
  return inspector_->storeCurrentStackTrace(description);
}

void IsolateData::ExternalAsyncTaskStarted(
    const v8_inspector::V8StackTraceId& parent) {
232
  v8::SealHandleScope seal_handle_scope(isolate());
233 234 235 236 237
  inspector_->externalAsyncTaskStarted(parent);
}

void IsolateData::ExternalAsyncTaskFinished(
    const v8_inspector::V8StackTraceId& parent) {
238
  v8::SealHandleScope seal_handle_scope(isolate());
239 240 241
  inspector_->externalAsyncTaskFinished(parent);
}

242 243
void IsolateData::AddInspectedObject(int session_id,
                                     v8::Local<v8::Value> object) {
244
  v8::SealHandleScope seal_handle_scope(isolate());
245 246
  auto it = sessions_.find(session_id);
  if (it == sessions_.end()) return;
247 248
  std::unique_ptr<Inspectable> inspectable(
      new Inspectable(isolate_.get(), object));
249 250 251
  it->second->addInspectedObject(std::move(inspectable));
}

252
void IsolateData::SetMaxAsyncTaskStacksForTest(int limit) {
253
  v8::SealHandleScope seal_handle_scope(isolate());
254 255 256 257
  v8_inspector::SetMaxAsyncTaskStacksForTest(inspector_.get(), limit);
}

void IsolateData::DumpAsyncTaskStacksStateForTest() {
258
  v8::SealHandleScope seal_handle_scope(isolate());
259 260 261
  v8_inspector::DumpAsyncTaskStacksStateForTest(inspector_.get());
}

262
// static
263 264
int IsolateData::HandleMessage(v8::Local<v8::Message> message,
                               v8::Local<v8::Value> exception) {
265
  v8::Isolate* isolate = message->GetIsolate();
266
  v8::Local<v8::Context> context = isolate->GetEnteredOrMicrotaskContext();
267
  if (context.IsEmpty()) return 0;
268 269 270 271
  v8_inspector::V8Inspector* inspector =
      IsolateData::FromContext(context)->inspector_.get();

  v8::Local<v8::StackTrace> stack = message->GetStackTrace();
272
  int script_id = message->GetScriptOrigin().ScriptId();
273
  if (!stack.IsEmpty() && stack->GetFrameCount() > 0) {
274
    int top_script_id = stack->GetFrame(isolate, 0)->GetScriptId();
275 276 277 278 279 280 281 282
    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;
283 284 285 286
  std::vector<uint16_t> message_text_string = ToVector(isolate, message->Get());
  v8_inspector::StringView message_text(message_text_string.data(),
                                        message_text_string.size());
  std::vector<uint16_t> url_string;
287
  if (message->GetScriptOrigin().ResourceName()->IsString()) {
288
    url_string = ToVector(
289
        isolate, message->GetScriptOrigin().ResourceName().As<v8::String>());
290
  }
291
  v8_inspector::StringView url(url_string.data(), url_string.size());
292

293
  v8::SealHandleScope seal_handle_scope(isolate);
294 295 296 297 298 299 300 301 302 303 304 305 306
  return inspector->exceptionThrown(
      context, message_text, exception, detailed_message, url, line_number,
      column_number, inspector->createStackTrace(stack), script_id);
}

// static
void IsolateData::MessageHandler(v8::Local<v8::Message> message,
                                 v8::Local<v8::Value> exception) {
  HandleMessage(message, exception);
}

// static
void IsolateData::PromiseRejectHandler(v8::PromiseRejectMessage data) {
307
  v8::Isolate* isolate = data.GetPromise()->GetIsolate();
308
  v8::Local<v8::Context> context = isolate->GetEnteredOrMicrotaskContext();
309 310 311
  if (context.IsEmpty()) return;
  v8::Local<v8::Promise> promise = data.GetPromise();
  v8::Local<v8::Private> id_private = v8::Private::ForApi(
312
      isolate, v8::String::NewFromUtf8Literal(isolate, "id"));
313 314 315 316 317 318 319

  if (data.GetEvent() == v8::kPromiseHandlerAddedAfterReject) {
    v8::Local<v8::Value> id;
    if (!promise->GetPrivate(context, id_private).ToLocal(&id)) return;
    if (!id->IsInt32()) return;
    v8_inspector::V8Inspector* inspector =
        IsolateData::FromContext(context)->inspector_.get();
320
    v8::SealHandleScope seal_handle_scope(isolate);
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
    const char* reason_str = "Handler added to rejected promise";
    inspector->exceptionRevoked(
        context, id.As<v8::Int32>()->Value(),
        v8_inspector::StringView(reinterpret_cast<const uint8_t*>(reason_str),
                                 strlen(reason_str)));
    return;
  }

  v8::Local<v8::Value> exception = data.GetValue();
  int exception_id = HandleMessage(
      v8::Exception::CreateMessage(isolate, exception), exception);
  if (exception_id) {
    promise
        ->SetPrivate(isolate->GetCurrentContext(), id_private,
                     v8::Int32::New(isolate, exception_id))
        .ToChecked();
  }
338 339 340
}

void IsolateData::FireContextCreated(v8::Local<v8::Context> context,
341 342 343
                                     int context_group_id,
                                     v8_inspector::StringView name) {
  v8_inspector::V8ContextInfo info(context, context_group_id, name);
344
  info.hasMemoryOnConsole = true;
345
  v8::SealHandleScope seal_handle_scope(isolate());
346 347 348 349
  inspector_->contextCreated(info);
}

void IsolateData::FireContextDestroyed(v8::Local<v8::Context> context) {
350
  v8::SealHandleScope seal_handle_scope(isolate());
351 352 353
  inspector_->contextDestroyed(context);
}

354 355 356 357 358 359 360
void IsolateData::FreeContext(v8::Local<v8::Context> context) {
  int context_group_id = GetContextGroupId(context);
  auto it = contexts_.find(context_group_id);
  if (it == contexts_.end()) return;
  contexts_.erase(it);
}

361 362 363 364 365 366 367 368 369 370
std::vector<int> IsolateData::GetSessionIds(int context_group_id) {
  std::vector<int> result;
  for (auto& it : sessions_) {
    if (context_group_by_session_[it.second.get()] == context_group_id)
      result.push_back(it.first);
  }
  return result;
}

bool IsolateData::formatAccessorsAsProperties(v8::Local<v8::Value> object) {
371
  v8::Local<v8::Context> context = isolate()->GetCurrentContext();
372
  v8::Local<v8::Private> shouldFormatAccessorsPrivate = v8::Private::ForApi(
373 374
      isolate(),
      v8::String::NewFromUtf8Literal(isolate(), "allowAccessorFormatting"));
375 376 377 378 379 380
  CHECK(object->IsObject());
  return object.As<v8::Object>()
      ->HasPrivate(context, shouldFormatAccessorsPrivate)
      .FromMaybe(false);
}

381
bool IsolateData::isInspectableHeapObject(v8::Local<v8::Object> object) {
382
  v8::Local<v8::Context> context = isolate()->GetCurrentContext();
383
  v8::MicrotasksScope microtasks_scope(
384 385
      isolate(), v8::MicrotasksScope::kDoNotRunMicrotasks);
  return !object->HasPrivate(context, not_inspectable_private_.Get(isolate()))
386 387 388
              .FromMaybe(false);
}

389 390
v8::Local<v8::Context> IsolateData::ensureDefaultContextInGroup(
    int context_group_id) {
391
  return GetDefaultContext(context_group_id);
392 393 394 395 396 397 398 399 400
}

void IsolateData::SetCurrentTimeMS(double time) {
  current_time_ = time;
  current_time_set_ = true;
}

double IsolateData::currentTimeMS() {
  if (current_time_set_) return current_time_;
401
  return V8::GetCurrentPlatform()->CurrentClockTimeMillis();
402 403 404
}

void IsolateData::SetMemoryInfo(v8::Local<v8::Value> memory_info) {
405
  memory_info_.Reset(isolate_.get(), memory_info);
406 407 408 409 410 411
}

void IsolateData::SetLogConsoleApiMessageCalls(bool log) {
  log_console_api_message_calls_ = log;
}

412 413 414 415
void IsolateData::SetLogMaxAsyncCallStackDepthChanged(bool log) {
  log_max_async_call_stack_depth_changed_ = log;
}

416 417
void IsolateData::SetAdditionalConsoleApi(v8_inspector::StringView api_script) {
  v8::HandleScope handle_scope(isolate());
418
  additional_console_api_.Reset(isolate(), ToV8String(isolate(), api_script));
419 420
}

421 422 423 424 425 426 427
v8::MaybeLocal<v8::Value> IsolateData::memoryInfo(v8::Isolate* isolate,
                                                  v8::Local<v8::Context>) {
  if (memory_info_.IsEmpty()) return v8::MaybeLocal<v8::Value>();
  return memory_info_.Get(isolate);
}

void IsolateData::runMessageLoopOnPause(int) {
428
  v8::SealHandleScope seal_handle_scope(isolate());
429 430 431
  task_runner_->RunMessageLoop(true);
}

432 433 434 435
void IsolateData::quitMessageLoopOnPause() {
  v8::SealHandleScope seal_handle_scope(isolate());
  task_runner_->QuitMessageLoop();
}
436

437 438 439 440 441 442
void IsolateData::installAdditionalCommandLineAPI(
    v8::Local<v8::Context> context, v8::Local<v8::Object> object) {
  if (additional_console_api_.IsEmpty()) return;
  CHECK(context->GetIsolate() == isolate());
  v8::HandleScope handle_scope(isolate());
  v8::Context::Scope context_scope(context);
443 444
  v8::ScriptOrigin origin(isolate(), v8::String::NewFromUtf8Literal(
                                         isolate(), "internal-console-api"));
445 446 447 448 449 450 451
  v8::ScriptCompiler::Source scriptSource(
      additional_console_api_.Get(isolate()), origin);
  v8::MaybeLocal<v8::Script> script =
      v8::ScriptCompiler::Compile(context, &scriptSource);
  CHECK(!script.ToLocalChecked()->Run(context).IsEmpty());
}

452 453 454 455 456 457 458
void IsolateData::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;
459
  Print(isolate_.get(), message);
460
  fprintf(stdout, " (");
461
  Print(isolate_.get(), url);
462
  fprintf(stdout, ":%d:%d)", lineNumber, columnNumber);
463
  Print(isolate_.get(), stack->toString()->string());
464 465
  fprintf(stdout, "\n");
}
466 467 468 469 470

void IsolateData::maxAsyncCallStackDepthChanged(int depth) {
  if (!log_max_async_call_stack_depth_changed_) return;
  fprintf(stdout, "maxAsyncCallStackDepthChanged: %d\n", depth);
}
471 472

void IsolateData::SetResourceNamePrefix(v8::Local<v8::String> prefix) {
473
  resource_name_prefix_.Reset(isolate(), prefix);
474 475 476 477 478 479
}

namespace {
class StringBufferImpl : public v8_inspector::StringBuffer {
 public:
  StringBufferImpl(v8::Isolate* isolate, v8::Local<v8::String> string)
480
      : data_(ToVector(isolate, string)) {}
481 482

  v8_inspector::StringView string() const override {
483
    return v8_inspector::StringView(data_.data(), data_.size());
484
  }
485 486

 private:
487
  std::vector<uint16_t> data_;
488 489 490 491 492 493
};
}  // anonymous namespace

std::unique_ptr<v8_inspector::StringBuffer> IsolateData::resourceNameToUrl(
    const v8_inspector::StringView& resourceName) {
  if (resource_name_prefix_.IsEmpty()) return nullptr;
494
  v8::HandleScope handle_scope(isolate());
495
  v8::Local<v8::String> name = ToV8String(isolate(), resourceName);
496 497
  v8::Local<v8::String> prefix = resource_name_prefix_.Get(isolate());
  v8::Local<v8::String> url = v8::String::Concat(isolate(), prefix, name);
498
  return std::make_unique<StringBufferImpl>(isolate(), url);
499
}
500

501 502 503 504 505 506
int64_t IsolateData::generateUniqueId() {
  static int64_t last_unique_id = 0L;
  // Keep it not too random for tests.
  return ++last_unique_id;
}

507 508
}  // namespace internal
}  // namespace v8