fe598532ec
As part of J2V8 development (https://github.com/eclipsesource/J2V8), we realized that we had a subtle bug in how Isolate scope was created and it's lifetime managed, see: https://github.com/eclipsesource/J2V8/issues/313. Mentioned above bug was fixed, however, what we also noticed is that V8 API has been constantly and slowly moving to such an API, in which one has to pass Isolate explicitly to methods and/or constructors. We found two more places that might have been overlooked. This contribution adds passing of Isolate pointer explicitly to constructors of String::Utf8Value and String::Value classes. Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng;master.tryserver.v8:v8_linux_noi18n_rel_ng Change-Id: I61984285f152aba5ca922100cf3df913a9cb2cea Reviewed-on: https://chromium-review.googlesource.com/593309 Commit-Queue: Adam Klein <adamk@chromium.org> Reviewed-by: Adam Klein <adamk@chromium.org> Cr-Commit-Position: refs/heads/master@{#47656}
399 lines
15 KiB
C++
399 lines
15 KiB
C++
// 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"
|
|
|
|
#include "src/inspector/test-interface.h"
|
|
#include "test/inspector/task-runner.h"
|
|
|
|
namespace {
|
|
|
|
const int kIsolateDataIndex = 2;
|
|
const int kContextGroupIdIndex = 3;
|
|
|
|
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;
|
|
}
|
|
|
|
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(isolate, v8_string);
|
|
fwrite(*utf8_string, sizeof(**utf8_string), utf8_string.length(), stdout);
|
|
}
|
|
|
|
class Inspectable : public v8_inspector::V8InspectorSession::Inspectable {
|
|
public:
|
|
Inspectable(v8::Isolate* isolate, v8::Local<v8::Value> object)
|
|
: object_(isolate, object) {}
|
|
~Inspectable() override {}
|
|
v8::Local<v8::Value> get(v8::Local<v8::Context> context) override {
|
|
return object_.Get(context->GetIsolate());
|
|
}
|
|
|
|
private:
|
|
v8::Global<v8::Value> object_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
IsolateData::IsolateData(TaskRunner* task_runner,
|
|
IsolateData::SetupGlobalTasks setup_global_tasks,
|
|
v8::StartupData* startup_data, bool with_inspector)
|
|
: task_runner_(task_runner),
|
|
setup_global_tasks_(std::move(setup_global_tasks)) {
|
|
v8::Isolate::CreateParams params;
|
|
params.array_buffer_allocator =
|
|
v8::ArrayBuffer::Allocator::NewDefaultAllocator();
|
|
params.snapshot_blob = startup_data;
|
|
isolate_ = v8::Isolate::New(params);
|
|
isolate_->SetMicrotasksPolicy(v8::MicrotasksPolicy::kScoped);
|
|
if (with_inspector) {
|
|
isolate_->AddMessageListener(&IsolateData::MessageHandler);
|
|
isolate_->SetPromiseRejectCallback(&IsolateData::PromiseRejectHandler);
|
|
inspector_ = v8_inspector::V8Inspector::create(isolate_, this);
|
|
}
|
|
v8::HandleScope handle_scope(isolate_);
|
|
not_inspectable_private_.Reset(
|
|
isolate_, v8::Private::ForApi(isolate_, v8::String::NewFromUtf8(
|
|
isolate_, "notInspectable",
|
|
v8::NewStringType::kNormal)
|
|
.ToLocalChecked()));
|
|
}
|
|
|
|
IsolateData* IsolateData::FromContext(v8::Local<v8::Context> context) {
|
|
return static_cast<IsolateData*>(
|
|
context->GetAlignedPointerFromEmbedderData(kIsolateDataIndex));
|
|
}
|
|
|
|
int IsolateData::CreateContextGroup() {
|
|
v8::HandleScope handle_scope(isolate_);
|
|
v8::Local<v8::ObjectTemplate> global_template =
|
|
v8::ObjectTemplate::New(isolate_);
|
|
for (auto it = setup_global_tasks_.begin(); it != setup_global_tasks_.end();
|
|
++it) {
|
|
(*it)->Run(isolate_, global_template);
|
|
}
|
|
v8::Local<v8::Context> context =
|
|
v8::Context::New(isolate_, nullptr, global_template);
|
|
context->SetAlignedPointerInEmbedderData(kIsolateDataIndex, this);
|
|
int context_group_id = ++last_context_group_id_;
|
|
// Should be 2-byte aligned.
|
|
context->SetAlignedPointerInEmbedderData(
|
|
kContextGroupIdIndex, reinterpret_cast<void*>(context_group_id * 2));
|
|
contexts_[context_group_id].Reset(isolate_, context);
|
|
if (inspector_) FireContextCreated(context, context_group_id);
|
|
return context_group_id;
|
|
}
|
|
|
|
v8::Local<v8::Context> IsolateData::GetContext(int context_group_id) {
|
|
return contexts_[context_group_id].Get(isolate_);
|
|
}
|
|
|
|
int IsolateData::GetContextGroupId(v8::Local<v8::Context> context) {
|
|
return static_cast<int>(
|
|
reinterpret_cast<intptr_t>(
|
|
context->GetAlignedPointerFromEmbedderData(kContextGroupIdIndex)) /
|
|
2);
|
|
}
|
|
|
|
void IsolateData::RegisterModule(v8::Local<v8::Context> context,
|
|
v8::internal::Vector<uint16_t> name,
|
|
v8::ScriptCompiler::Source* source) {
|
|
v8::Local<v8::Module> module;
|
|
if (!v8::ScriptCompiler::CompileModule(isolate(), source).ToLocal(&module))
|
|
return;
|
|
if (!module->InstantiateModule(context, &IsolateData::ModuleResolveCallback)
|
|
.FromMaybe(false)) {
|
|
return;
|
|
}
|
|
v8::Local<v8::Value> result;
|
|
if (!module->Evaluate(context).ToLocal(&result)) return;
|
|
modules_[name] = v8::Global<v8::Module>(isolate_, module);
|
|
}
|
|
|
|
// static
|
|
v8::MaybeLocal<v8::Module> IsolateData::ModuleResolveCallback(
|
|
v8::Local<v8::Context> context, v8::Local<v8::String> specifier,
|
|
v8::Local<v8::Module> referrer) {
|
|
IsolateData* data = IsolateData::FromContext(context);
|
|
std::string str = *v8::String::Utf8Value(data->isolate_, specifier);
|
|
return data->modules_[ToVector(specifier)].Get(data->isolate_);
|
|
}
|
|
|
|
int IsolateData::ConnectSession(int context_group_id,
|
|
const v8_inspector::StringView& state,
|
|
v8_inspector::V8Inspector::Channel* channel) {
|
|
int session_id = ++last_session_id_;
|
|
sessions_[session_id] = inspector_->connect(context_group_id, channel, state);
|
|
context_group_by_session_[sessions_[session_id].get()] = context_group_id;
|
|
return session_id;
|
|
}
|
|
|
|
std::unique_ptr<v8_inspector::StringBuffer> IsolateData::DisconnectSession(
|
|
int session_id) {
|
|
auto it = sessions_.find(session_id);
|
|
CHECK(it != sessions_.end());
|
|
context_group_by_session_.erase(it->second.get());
|
|
std::unique_ptr<v8_inspector::StringBuffer> result = it->second->stateJSON();
|
|
sessions_.erase(it);
|
|
return result;
|
|
}
|
|
|
|
void IsolateData::SendMessage(int session_id,
|
|
const v8_inspector::StringView& message) {
|
|
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) {
|
|
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) {
|
|
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) {
|
|
for (int session_id : GetSessionIds(context_group_id)) {
|
|
auto it = sessions_.find(session_id);
|
|
if (it != sessions_.end()) it->second->cancelPauseOnNextStatement();
|
|
}
|
|
}
|
|
|
|
void IsolateData::AsyncTaskScheduled(const v8_inspector::StringView& name,
|
|
void* task, bool recurring) {
|
|
inspector_->asyncTaskScheduled(name, task, recurring);
|
|
}
|
|
|
|
void IsolateData::AsyncTaskStarted(void* task) {
|
|
inspector_->asyncTaskStarted(task);
|
|
}
|
|
|
|
void IsolateData::AsyncTaskFinished(void* task) {
|
|
inspector_->asyncTaskFinished(task);
|
|
}
|
|
|
|
void IsolateData::AddInspectedObject(int session_id,
|
|
v8::Local<v8::Value> object) {
|
|
auto it = sessions_.find(session_id);
|
|
if (it == sessions_.end()) return;
|
|
std::unique_ptr<Inspectable> inspectable(new Inspectable(isolate_, object));
|
|
it->second->addInspectedObject(std::move(inspectable));
|
|
}
|
|
|
|
void IsolateData::SetMaxAsyncTaskStacksForTest(int limit) {
|
|
v8_inspector::SetMaxAsyncTaskStacksForTest(inspector_.get(), limit);
|
|
}
|
|
|
|
void IsolateData::DumpAsyncTaskStacksStateForTest() {
|
|
v8_inspector::DumpAsyncTaskStacksStateForTest(inspector_.get());
|
|
}
|
|
|
|
// static
|
|
int IsolateData::HandleMessage(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 0;
|
|
v8_inspector::V8Inspector* inspector =
|
|
IsolateData::FromContext(context)->inspector_.get();
|
|
|
|
v8::Local<v8::StackTrace> stack = message->GetStackTrace();
|
|
int script_id =
|
|
static_cast<int>(message->GetScriptOrigin().ScriptID()->Value());
|
|
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;
|
|
v8::internal::Vector<uint16_t> message_text_string = ToVector(message->Get());
|
|
v8_inspector::StringView message_text(message_text_string.start(),
|
|
message_text_string.length());
|
|
v8::internal::Vector<uint16_t> url_string;
|
|
if (message->GetScriptOrigin().ResourceName()->IsString()) {
|
|
url_string =
|
|
ToVector(message->GetScriptOrigin().ResourceName().As<v8::String>());
|
|
}
|
|
v8_inspector::StringView url(url_string.start(), url_string.length());
|
|
|
|
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) {
|
|
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
|
v8::Local<v8::Context> context = isolate->GetEnteredContext();
|
|
if (context.IsEmpty()) return;
|
|
v8::Local<v8::Promise> promise = data.GetPromise();
|
|
v8::Local<v8::Private> id_private = v8::Private::ForApi(
|
|
isolate,
|
|
v8::String::NewFromUtf8(isolate, "id", v8::NewStringType::kNormal)
|
|
.ToLocalChecked());
|
|
|
|
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();
|
|
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();
|
|
}
|
|
}
|
|
|
|
void IsolateData::FireContextCreated(v8::Local<v8::Context> context,
|
|
int context_group_id) {
|
|
v8_inspector::V8ContextInfo info(context, context_group_id,
|
|
v8_inspector::StringView());
|
|
info.hasMemoryOnConsole = true;
|
|
inspector_->contextCreated(info);
|
|
}
|
|
|
|
void IsolateData::FireContextDestroyed(v8::Local<v8::Context> context) {
|
|
inspector_->contextDestroyed(context);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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) {
|
|
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);
|
|
}
|
|
|
|
bool IsolateData::isInspectableHeapObject(v8::Local<v8::Object> object) {
|
|
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
v8::MicrotasksScope microtasks_scope(
|
|
isolate, v8::MicrotasksScope::kDoNotRunMicrotasks);
|
|
return !object->HasPrivate(context, not_inspectable_private_.Get(isolate))
|
|
.FromMaybe(false);
|
|
}
|
|
|
|
v8::Local<v8::Context> IsolateData::ensureDefaultContextInGroup(
|
|
int context_group_id) {
|
|
return GetContext(context_group_id);
|
|
}
|
|
|
|
void IsolateData::SetCurrentTimeMS(double time) {
|
|
current_time_ = time;
|
|
current_time_set_ = true;
|
|
}
|
|
|
|
double IsolateData::currentTimeMS() {
|
|
if (current_time_set_) return current_time_;
|
|
return v8::base::OS::TimeCurrentMillis();
|
|
}
|
|
|
|
void IsolateData::SetMemoryInfo(v8::Local<v8::Value> memory_info) {
|
|
memory_info_.Reset(isolate_, memory_info);
|
|
}
|
|
|
|
void IsolateData::SetLogConsoleApiMessageCalls(bool log) {
|
|
log_console_api_message_calls_ = log;
|
|
}
|
|
|
|
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) {
|
|
task_runner_->RunMessageLoop(true);
|
|
}
|
|
|
|
void IsolateData::quitMessageLoopOnPause() { task_runner_->QuitMessageLoop(); }
|
|
|
|
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;
|
|
Print(isolate_, message);
|
|
fprintf(stdout, " (");
|
|
Print(isolate_, url);
|
|
fprintf(stdout, ":%d:%d)", lineNumber, columnNumber);
|
|
Print(isolate_, stack->toString()->string());
|
|
fprintf(stdout, "\n");
|
|
}
|