blob: 173e2b86d3ca8e2d51a26d5e53c20745bb96cb65 [file] [log] [blame]
// Copyright 2021 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 "src/debug/debug-interface.h"
#include "include/v8-function.h"
#include "src/api/api-inl.h"
#include "src/base/utils/random-number-generator.h"
#include "src/codegen/compiler.h"
#include "src/codegen/script-details.h"
#include "src/date/date.h"
#include "src/debug/debug-coverage.h"
#include "src/debug/debug-evaluate.h"
#include "src/debug/debug-property-iterator.h"
#include "src/debug/debug-stack-trace-iterator.h"
#include "src/debug/debug.h"
#include "src/execution/vm-state-inl.h"
#include "src/heap/heap.h"
#include "src/objects/js-generator-inl.h"
#include "src/profiler/heap-profiler.h"
#include "src/strings/string-builder-inl.h"
#if V8_ENABLE_WEBASSEMBLY
#include "src/debug/debug-wasm-objects-inl.h"
#include "src/wasm/wasm-disassembler.h"
#include "src/wasm/wasm-engine.h"
#endif // V8_ENABLE_WEBASSEMBLY
// Has to be the last include (doesn't have include guards):
#include "src/api/api-macros.h"
namespace v8 {
namespace debug {
void SetContextId(Local<Context> context, int id) {
auto v8_context = Utils::OpenDirectHandle(*context);
DCHECK_NO_SCRIPT_NO_EXCEPTION(v8_context->GetIsolate());
v8_context->set_debug_context_id(i::Smi::FromInt(id));
}
int GetContextId(Local<Context> context) {
auto v8_context = Utils::OpenDirectHandle(*context);
DCHECK_NO_SCRIPT_NO_EXCEPTION(v8_context->GetIsolate());
i::Tagged<i::Object> value = v8_context->debug_context_id();
return (IsSmi(value)) ? i::Smi::ToInt(value) : 0;
}
void SetInspector(Isolate* isolate, v8_inspector::V8Inspector* inspector) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
DCHECK_NO_SCRIPT_NO_EXCEPTION(i_isolate);
if (inspector == nullptr) {
i_isolate->set_inspector(nullptr);
} else {
i_isolate->set_inspector(inspector);
}
}
v8_inspector::V8Inspector* GetInspector(Isolate* isolate) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
DCHECK_NO_SCRIPT_NO_EXCEPTION(i_isolate);
return i_isolate->inspector();
}
namespace {
i::Handle<i::String> GetBigIntStringPresentationHandle(
i::Isolate* i_isolate, i::Handle<i::BigInt> i_bigint) {
// For large BigInts computing the decimal string representation
// can take a long time, so we go with hexadecimal in that case.
int radix = (i_bigint->Words64Count() > 100 * 1000) ? 16 : 10;
i::Handle<i::String> string_value =
i::BigInt::ToString(i_isolate, i_bigint, radix, i::kDontThrow)
.ToHandleChecked();
if (radix == 16) {
if (i_bigint->IsNegative()) {
string_value =
i_isolate->factory()
->NewConsString(
i_isolate->factory()->NewStringFromAsciiChecked("-0x"),
i_isolate->factory()->NewProperSubString(
string_value, 1, string_value->length() - 1))
.ToHandleChecked();
} else {
string_value =
i_isolate->factory()
->NewConsString(
i_isolate->factory()->NewStringFromAsciiChecked("0x"),
string_value)
.ToHandleChecked();
}
}
return string_value;
}
} // namespace
Local<String> GetBigIntStringValue(Isolate* isolate, Local<BigInt> bigint) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
i::Handle<i::BigInt> i_bigint = Utils::OpenHandle(*bigint);
i::Handle<i::String> string_value =
GetBigIntStringPresentationHandle(i_isolate, i_bigint);
return Utils::ToLocal(string_value);
}
Local<String> GetBigIntDescription(Isolate* isolate, Local<BigInt> bigint) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
i::Handle<i::BigInt> i_bigint = Utils::OpenHandle(*bigint);
i::Handle<i::String> string_value =
GetBigIntStringPresentationHandle(i_isolate, i_bigint);
i::Handle<i::String> description =
i_isolate->factory()
->NewConsString(
string_value,
i_isolate->factory()->LookupSingleCharacterStringFromCode('n'))
.ToHandleChecked();
return Utils::ToLocal(description);
}
Local<String> GetDateDescription(Local<Date> date) {
auto receiver = Utils::OpenDirectHandle(*date);
auto jsdate = i::DirectHandle<i::JSDate>::cast(receiver);
i::Isolate* i_isolate = jsdate->GetIsolate();
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
auto buffer = i::ToDateString(i::Object::Number(jsdate->value()),
i_isolate->date_cache(),
i::ToDateStringMode::kLocalDateAndTime);
return Utils::ToLocal(i_isolate->factory()
->NewStringFromUtf8(base::VectorOf(buffer))
.ToHandleChecked());
}
Local<String> GetFunctionDescription(Local<Function> function) {
auto receiver = Utils::OpenHandle(*function);
auto i_isolate = receiver->GetIsolate();
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
if (IsJSBoundFunction(*receiver)) {
return Utils::ToLocal(i::JSBoundFunction::ToString(
i::Handle<i::JSBoundFunction>::cast(receiver)));
}
if (IsJSFunction(*receiver)) {
auto js_function = i::Handle<i::JSFunction>::cast(receiver);
#if V8_ENABLE_WEBASSEMBLY
if (js_function->shared()->HasWasmExportedFunctionData()) {
i::Handle<i::WasmExportedFunctionData> function_data(
js_function->shared()->wasm_exported_function_data(), i_isolate);
int func_index = function_data->function_index();
i::Handle<i::WasmTrustedInstanceData> instance_data(
function_data->instance_data(), i_isolate);
if (instance_data->module()->origin == i::wasm::kWasmOrigin) {
// For asm.js functions, we can still print the source
// code (hopefully), so don't bother with them here.
auto debug_name =
i::GetWasmFunctionDebugName(i_isolate, instance_data, func_index);
i::IncrementalStringBuilder builder(i_isolate);
builder.AppendCStringLiteral("function ");
builder.AppendString(debug_name);
builder.AppendCStringLiteral("() { [native code] }");
return Utils::ToLocal(builder.Finish().ToHandleChecked(), i_isolate);
}
}
#endif // V8_ENABLE_WEBASSEMBLY
return Utils::ToLocal(i::JSFunction::ToString(js_function));
}
return Utils::ToLocal(
receiver->GetIsolate()->factory()->function_native_code_string());
}
void SetBreakOnNextFunctionCall(Isolate* isolate) {
auto i_isolate = reinterpret_cast<i::Isolate*>(isolate);
DCHECK_NO_SCRIPT_NO_EXCEPTION(i_isolate);
i_isolate->debug()->SetBreakOnNextFunctionCall();
}
void ClearBreakOnNextFunctionCall(Isolate* isolate) {
auto i_isolate = reinterpret_cast<i::Isolate*>(isolate);
DCHECK_NO_SCRIPT_NO_EXCEPTION(i_isolate);
i_isolate->debug()->ClearBreakOnNextFunctionCall();
}
MaybeLocal<Array> GetInternalProperties(Isolate* v8_isolate,
Local<Value> value) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
i::Handle<i::Object> val = Utils::OpenHandle(*value);
i::Handle<i::JSArray> result;
if (!i::Runtime::GetInternalProperties(isolate, val).ToHandle(&result))
return MaybeLocal<Array>();
return Utils::ToLocal(result);
}
namespace {
using FlagFilter = std::function<bool(i::IsStaticFlag)>;
using VariableModeFilter = std::function<bool(i::VariableMode)>;
using ContextLocalIterator = std::function<void(
i::VariableMode, i::Handle<i::String>, i::Handle<i::Object>)>;
void ForEachContextLocal(i::Isolate* isolate, i::Handle<i::Context> context,
const VariableModeFilter& var_mode_filter,
const FlagFilter& flag_filter,
const ContextLocalIterator& context_local_it) {
DCHECK_NO_SCRIPT_NO_EXCEPTION(isolate);
i::Handle<i::ScopeInfo> scope_info(context->scope_info(), isolate);
for (auto it : i::ScopeInfo::IterateLocalNames(scope_info)) {
i::Handle<i::String> name(it->name(), isolate);
i::VariableMode mode = scope_info->ContextLocalMode(it->index());
if (!var_mode_filter(mode)) {
continue;
}
i::IsStaticFlag flag = scope_info->ContextLocalIsStaticFlag(it->index());
if (!flag_filter(flag)) {
continue;
}
int context_index = scope_info->ContextHeaderLength() + it->index();
i::Handle<i::Object> slot_value(context->get(context_index), isolate);
context_local_it(mode, name, slot_value);
}
}
} // namespace
bool GetPrivateMembers(Local<Context> context, Local<Object> object, int filter,
LocalVector<Value>* names_out,
LocalVector<Value>* values_out) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(context->GetIsolate());
API_RCS_SCOPE(isolate, debug, GetPrivateMembers);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
bool include_methods =
filter & static_cast<int>(PrivateMemberFilter::kPrivateMethods);
bool include_fields =
filter & static_cast<int>(PrivateMemberFilter::kPrivateFields);
bool include_accessors =
filter & static_cast<int>(PrivateMemberFilter::kPrivateAccessors);
bool include_methods_or_accessors = include_methods || include_accessors;
auto var_mode_filter =
include_methods
? (include_accessors ? i::IsPrivateMethodOrAccessorVariableMode
: i::IsPrivateMethodVariableMode)
: i::IsPrivateAccessorVariableMode;
auto constexpr instance_filter = [](i::IsStaticFlag flag) {
return flag == i::IsStaticFlag::kNotStatic;
};
auto constexpr static_filter = [](i::IsStaticFlag flag) {
return flag == i::IsStaticFlag::kStatic;
};
i::Handle<i::JSReceiver> receiver = Utils::OpenHandle(*object);
i::PropertyFilter key_filter =
static_cast<i::PropertyFilter>(i::PropertyFilter::PRIVATE_NAMES_ONLY);
i::Handle<i::FixedArray> keys;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, keys,
i::KeyAccumulator::GetKeys(isolate, receiver,
i::KeyCollectionMode::kOwnOnly, key_filter,
i::GetKeysConversion::kConvertToString),
false);
// Estimate number of private fields and private instance methods/accessors.
int private_entries_count = 0;
auto count_private_entry = [&](i::VariableMode mode, i::Handle<i::String>,
i::Handle<i::Object>) {
private_entries_count++;
};
for (int i = 0; i < keys->length(); ++i) {
// Exclude the private brand symbols.
i::Handle<i::Symbol> key(i::Symbol::cast(keys->get(i)), isolate);
if (key->is_private_brand()) {
if (include_methods_or_accessors) {
i::Handle<i::Object> value;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, value, i::Object::GetProperty(isolate, receiver, key),
false);
i::Handle<i::Context> value_context(i::Context::cast(*value), isolate);
ForEachContextLocal(isolate, value_context, var_mode_filter,
instance_filter, count_private_entry);
}
} else if (include_fields) {
private_entries_count++;
}
}
// Estimate number of static private methods/accessors for classes.
bool has_static_private_methods_or_accessors = false;
if (include_methods_or_accessors) {
if (IsJSFunction(*receiver)) {
i::DirectHandle<i::JSFunction> func(i::JSFunction::cast(*receiver),
isolate);
i::DirectHandle<i::SharedFunctionInfo> shared(func->shared(), isolate);
if (shared->is_class_constructor() &&
shared->has_static_private_methods_or_accessors()) {
has_static_private_methods_or_accessors = true;
i::Handle<i::Context> func_context(func->context(), isolate);
ForEachContextLocal(isolate, func_context, var_mode_filter,
static_filter, count_private_entry);
}
}
}
DCHECK(names_out->empty());
names_out->reserve(private_entries_count);
DCHECK(values_out->empty());
values_out->reserve(private_entries_count);
auto add_private_entry = [&](i::VariableMode mode, i::Handle<i::String> name,
i::Handle<i::Object> value) {
DCHECK_IMPLIES(mode == i::VariableMode::kPrivateMethod,
IsJSFunction(*value));
DCHECK_IMPLIES(mode != i::VariableMode::kPrivateMethod,
IsAccessorPair(*value));
names_out->push_back(Utils::ToLocal(name));
values_out->push_back(Utils::ToLocal(value));
};
if (has_static_private_methods_or_accessors) {
i::Handle<i::Context> receiver_context(
i::JSFunction::cast(*receiver)->context(), isolate);
ForEachContextLocal(isolate, receiver_context, var_mode_filter,
static_filter, add_private_entry);
}
for (int i = 0; i < keys->length(); ++i) {
i::DirectHandle<i::Object> obj_key(keys->get(i), isolate);
i::Handle<i::Symbol> key(i::Symbol::cast(*obj_key), isolate);
CHECK(key->is_private_name());
i::Handle<i::Object> value;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, value, i::Object::GetProperty(isolate, receiver, key), false);
if (key->is_private_brand()) {
if (include_methods_or_accessors) {
DCHECK(IsContext(*value));
i::Handle<i::Context> value_context(i::Context::cast(*value), isolate);
ForEachContextLocal(isolate, value_context, var_mode_filter,
instance_filter, add_private_entry);
}
} else if (include_fields) { // Private fields
i::DirectHandle<i::String> name(
i::String::cast(i::Symbol::cast(*key)->description()), isolate);
names_out->push_back(Utils::ToLocal(name, isolate));
values_out->push_back(Utils::ToLocal(value, isolate));
}
}
DCHECK_EQ(names_out->size(), values_out->size());
DCHECK_LE(names_out->size(), private_entries_count);
return true;
}
MaybeLocal<Context> GetCreationContext(Local<Object> value) {
if (IsJSGlobalProxy(*Utils::OpenDirectHandle(*value))) {
return MaybeLocal<Context>();
}
return value->GetCreationContext();
}
void ChangeBreakOnException(Isolate* isolate, ExceptionBreakState type) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
DCHECK_NO_SCRIPT_NO_EXCEPTION(i_isolate);
i_isolate->debug()->ChangeBreakOnException(
i::BreakCaughtException,
type == BreakOnCaughtException || type == BreakOnAnyException);
i_isolate->debug()->ChangeBreakOnException(
i::BreakUncaughtException,
type == BreakOnUncaughtException || type == BreakOnAnyException);
}
void SetBreakPointsActive(Isolate* v8_isolate, bool is_active) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
isolate->debug()->set_break_points_active(is_active);
}
void PrepareStep(Isolate* v8_isolate, StepAction action) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8_BASIC(isolate);
CHECK(isolate->debug()->CheckExecutionState());
// Clear all current stepping setup.
isolate->debug()->ClearStepping();
// Prepare step.
isolate->debug()->PrepareStep(static_cast<i::StepAction>(action));
}
bool PrepareRestartFrame(Isolate* v8_isolate, int callFrameOrdinal) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8_BASIC(isolate);
CHECK(isolate->debug()->CheckExecutionState());
i::DebugStackTraceIterator it(isolate, callFrameOrdinal);
if (it.Done() || !it.CanBeRestarted()) return false;
// Clear all current stepping setup.
isolate->debug()->ClearStepping();
it.PrepareRestart();
return true;
}
void ClearStepping(Isolate* v8_isolate) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
// Clear all current stepping setup.
isolate->debug()->ClearStepping();
}
void BreakRightNow(Isolate* v8_isolate,
base::EnumSet<debug::BreakReason> break_reasons) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8_BASIC(isolate);
isolate->debug()->HandleDebugBreak(i::kIgnoreIfAllFramesBlackboxed,
break_reasons);
}
void SetTerminateOnResume(Isolate* v8_isolate) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
isolate->debug()->SetTerminateOnResume();
}
bool CanBreakProgram(Isolate* v8_isolate) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8_BASIC(isolate);
return !isolate->debug()->AllFramesOnStackAreBlackboxed();
}
size_t ScriptSource::Length() const {
auto source = Utils::OpenDirectHandle(this);
if (IsString(*source)) {
return i::DirectHandle<i::String>::cast(source)->length();
}
return Size();
}
size_t ScriptSource::Size() const {
#if V8_ENABLE_WEBASSEMBLY
MemorySpan<const uint8_t> wasm_bytecode;
if (WasmBytecode().To(&wasm_bytecode)) {
return wasm_bytecode.size();
}
#endif // V8_ENABLE_WEBASSEMBLY
auto source = Utils::OpenDirectHandle(this);
if (!IsString(*source)) return 0;
auto string = i::DirectHandle<i::String>::cast(source);
return string->length() * (string->IsTwoByteRepresentation() ? 2 : 1);
}
MaybeLocal<String> ScriptSource::JavaScriptCode() const {
i::Handle<i::HeapObject> source = Utils::OpenHandle(this);
if (!IsString(*source)) return MaybeLocal<String>();
return Utils::ToLocal(i::Handle<i::String>::cast(source));
}
#if V8_ENABLE_WEBASSEMBLY
Maybe<MemorySpan<const uint8_t>> ScriptSource::WasmBytecode() const {
auto source = Utils::OpenDirectHandle(this);
if (!IsForeign(*source)) return Nothing<MemorySpan<const uint8_t>>();
base::Vector<const uint8_t> wire_bytes =
i::Managed<i::wasm::NativeModule>::cast(*source)->raw()->wire_bytes();
return Just(MemorySpan<const uint8_t>{wire_bytes.begin(), wire_bytes.size()});
}
#endif // V8_ENABLE_WEBASSEMBLY
Isolate* Script::GetIsolate() const {
return reinterpret_cast<Isolate*>(
Utils::OpenDirectHandle(this)->GetIsolate());
}
ScriptOriginOptions Script::OriginOptions() const {
return Utils::OpenDirectHandle(this)->origin_options();
}
bool Script::WasCompiled() const {
return Utils::OpenDirectHandle(this)->compilation_state() ==
i::Script::CompilationState::kCompiled;
}
bool Script::IsEmbedded() const {
auto script = Utils::OpenDirectHandle(this);
return script->context_data() ==
script->GetReadOnlyRoots().uninitialized_symbol();
}
int Script::Id() const { return Utils::OpenDirectHandle(this)->id(); }
int Script::StartLine() const {
return Utils::OpenDirectHandle(this)->line_offset();
}
int Script::StartColumn() const {
return Utils::OpenDirectHandle(this)->column_offset();
}
int Script::EndLine() const {
i::Handle<i::Script> script = Utils::OpenHandle(this);
#if V8_ENABLE_WEBASSEMBLY
if (script->type() == i::Script::Type::kWasm) return 0;
#endif // V8_ENABLE_WEBASSEMBLY
if (!IsString(script->source())) {
return script->line_offset();
}
i::Isolate* isolate = script->GetIsolate();
i::HandleScope scope(isolate);
i::Script::PositionInfo info;
i::Script::GetPositionInfo(
script, i::String::cast(script->source())->length(), &info);
return info.line;
}
int Script::EndColumn() const {
i::Handle<i::Script> script = Utils::OpenHandle(this);
#if V8_ENABLE_WEBASSEMBLY
if (script->type() == i::Script::Type::kWasm) {
return script->wasm_native_module()->wire_bytes().length();
}
#endif // V8_ENABLE_WEBASSEMBLY
if (!IsString(script->source())) {
return script->column_offset();
}
i::Isolate* isolate = script->GetIsolate();
i::HandleScope scope(isolate);
i::Script::PositionInfo info;
i::Script::GetPositionInfo(
script, i::String::cast(script->source())->length(), &info);
return info.column;
}
MaybeLocal<String> Script::Name() const {
auto script = Utils::OpenDirectHandle(this);
i::Isolate* isolate = script->GetIsolate();
i::DirectHandle<i::Object> value(script->name(), isolate);
if (!IsString(*value)) return MaybeLocal<String>();
return Utils::ToLocal(i::DirectHandle<i::String>::cast(value), isolate);
}
MaybeLocal<String> Script::SourceURL() const {
auto script = Utils::OpenDirectHandle(this);
i::Isolate* isolate = script->GetIsolate();
i::DirectHandle<i::PrimitiveHeapObject> value(script->source_url(), isolate);
if (!IsString(*value)) return MaybeLocal<String>();
return Utils::ToLocal(i::DirectHandle<i::String>::cast(value), isolate);
}
MaybeLocal<String> Script::SourceMappingURL() const {
auto script = Utils::OpenDirectHandle(this);
i::Isolate* isolate = script->GetIsolate();
i::DirectHandle<i::Object> value(script->source_mapping_url(), isolate);
if (!IsString(*value)) return MaybeLocal<String>();
return Utils::ToLocal(i::DirectHandle<i::String>::cast(value), isolate);
}
MaybeLocal<String> Script::GetSha256Hash() const {
i::Handle<i::Script> script = Utils::OpenHandle(this);
i::Isolate* isolate = script->GetIsolate();
i::Handle<i::String> value =
i::Script::GetScriptHash(isolate, script, /* forceForInspector: */ true);
return Utils::ToLocal(value);
}
Maybe<int> Script::ContextId() const {
auto script = Utils::OpenDirectHandle(this);
i::Tagged<i::Object> value = script->context_data();
if (IsSmi(value)) return Just(i::Smi::ToInt(value));
return Nothing<int>();
}
Local<ScriptSource> Script::Source() const {
auto script = Utils::OpenDirectHandle(this);
i::Isolate* isolate = script->GetIsolate();
#if V8_ENABLE_WEBASSEMBLY
if (script->type() == i::Script::Type::kWasm) {
i::DirectHandle<i::Object> wasm_native_module(
script->wasm_managed_native_module(), isolate);
return Utils::Convert<i::Object, ScriptSource>(wasm_native_module, isolate);
}
#endif // V8_ENABLE_WEBASSEMBLY
i::DirectHandle<i::PrimitiveHeapObject> source(script->source(), isolate);
return Utils::Convert<i::PrimitiveHeapObject, ScriptSource>(source, isolate);
}
#if V8_ENABLE_WEBASSEMBLY
bool Script::IsWasm() const {
return Utils::OpenDirectHandle(this)->type() == i::Script::Type::kWasm;
}
#endif // V8_ENABLE_WEBASSEMBLY
bool Script::IsModule() const {
return Utils::OpenDirectHandle(this)->origin_options().IsModule();
}
namespace {
int GetSmiValue(i::Handle<i::FixedArray> array, int index) {
return i::Smi::ToInt(array->get(index));
}
bool CompareBreakLocation(const i::BreakLocation& loc1,
const i::BreakLocation& loc2) {
return loc1.position() < loc2.position();
}
} // namespace
bool Script::GetPossibleBreakpoints(
const Location& start, const Location& end, bool restrict_to_function,
std::vector<BreakLocation>* locations) const {
CHECK(!start.IsEmpty());
i::Handle<i::Script> script = Utils::OpenHandle(this);
#if V8_ENABLE_WEBASSEMBLY
if (script->type() == i::Script::Type::kWasm) {
i::wasm::NativeModule* native_module = script->wasm_native_module();
return i::WasmScript::GetPossibleBreakpoints(native_module, start, end,
locations);
}
#endif // V8_ENABLE_WEBASSEMBLY
i::Isolate* isolate = script->GetIsolate();
int start_offset, end_offset;
if (!GetSourceOffset(start, GetSourceOffsetMode::kClamp).To(&start_offset)) {
return false;
}
if (end.IsEmpty()) {
end_offset = std::numeric_limits<int>::max();
} else if (!GetSourceOffset(end, GetSourceOffsetMode::kClamp)
.To(&end_offset)) {
return false;
}
if (start_offset >= end_offset) return true;
std::vector<i::BreakLocation> v8_locations;
if (!isolate->debug()->GetPossibleBreakpoints(
script, start_offset, end_offset, restrict_to_function,
&v8_locations)) {
return false;
}
std::sort(v8_locations.begin(), v8_locations.end(), CompareBreakLocation);
for (const auto& v8_location : v8_locations) {
Location location = GetSourceLocation(v8_location.position());
locations->emplace_back(location.GetLineNumber(),
location.GetColumnNumber(), v8_location.type());
}
return true;
}
Maybe<int> Script::GetSourceOffset(const Location& location,
GetSourceOffsetMode mode) const {
i::Handle<i::Script> script = Utils::OpenHandle(this);
#if V8_ENABLE_WEBASSEMBLY
if (script->type() == i::Script::Type::kWasm) {
DCHECK_EQ(0, location.GetLineNumber());
return Just(location.GetColumnNumber());
}
#endif // V8_ENABLE_WEBASSEMBLY
int line = location.GetLineNumber();
int column = location.GetColumnNumber();
if (!script->HasSourceURLComment()) {
// Line/column number for inline <script>s with sourceURL annotation
// are supposed to be related to the <script> tag, otherwise they
// are relative to the parent file. Keep this in sync with the logic
// in GetSourceLocation() below.
line -= script->line_offset();
if (line == 0) column -= script->column_offset();
}
i::Script::InitLineEnds(script->GetIsolate(), script);
i::Handle<i::FixedArray> line_ends = i::Handle<i::FixedArray>::cast(
i::handle(script->line_ends(), script->GetIsolate()));
if (line < 0) {
if (mode == GetSourceOffsetMode::kClamp) {
return Just(0);
}
return Nothing<int>();
}
if (line >= line_ends->length()) {
if (mode == GetSourceOffsetMode::kClamp) {
return Just(GetSmiValue(line_ends, line_ends->length() - 1));
}
return Nothing<int>();
}
if (column < 0) {
if (mode != GetSourceOffsetMode::kClamp) {
return Nothing<int>();
}
column = 0;
}
int offset = column;
if (line > 0) {
int prev_line_end_offset = GetSmiValue(line_ends, line - 1);
offset += prev_line_end_offset + 1;
}
int line_end_offset = GetSmiValue(line_ends, line);
if (offset > line_end_offset) {
// Be permissive with columns that don't exist,
// as long as they are clearly within the range
// of the script.
if (line < line_ends->length() - 1 || mode == GetSourceOffsetMode::kClamp) {
return Just(line_end_offset);
}
return Nothing<int>();
}
return Just(offset);
}
Location Script::GetSourceLocation(int offset) const {
i::Handle<i::Script> script = Utils::OpenHandle(this);
i::Script::PositionInfo info;
i::Script::GetPositionInfo(script, offset, &info);
if (script->HasSourceURLComment()) {
// Line/column number for inline <script>s with sourceURL annotation
// are supposed to be related to the <script> tag, otherwise they
// are relative to the parent file. Keep this in sync with the logic
// in GetSourceOffset() above.
info.line -= script->line_offset();
if (info.line == 0) info.column -= script->column_offset();
}
return Location(info.line, info.column);
}
bool Script::SetScriptSource(Local<String> newSource, bool preview,
bool allow_top_frame_live_editing,
LiveEditResult* result) const {
i::Handle<i::Script> script = Utils::OpenHandle(this);
i::Isolate* isolate = script->GetIsolate();
return isolate->debug()->SetScriptSource(
script, Utils::OpenHandle(*newSource), preview,
allow_top_frame_live_editing, result);
}
bool Script::SetBreakpoint(Local<String> condition, Location* location,
BreakpointId* id) const {
i::Handle<i::Script> script = Utils::OpenHandle(this);
i::Isolate* isolate = script->GetIsolate();
int offset;
if (!GetSourceOffset(*location).To(&offset)) {
return false;
}
if (!isolate->debug()->SetBreakPointForScript(
script, Utils::OpenHandle(*condition), &offset, id)) {
return false;
}
*location = GetSourceLocation(offset);
return true;
}
bool Script::SetInstrumentationBreakpoint(BreakpointId* id) const {
i::Handle<i::Script> script = Utils::OpenHandle(this);
i::Isolate* isolate = script->GetIsolate();
#if V8_ENABLE_WEBASSEMBLY
if (script->type() == i::Script::Type::kWasm) {
isolate->debug()->SetInstrumentationBreakpointForWasmScript(script, id);
return true;
}
#endif // V8_ENABLE_WEBASSEMBLY
i::SharedFunctionInfo::ScriptIterator it(isolate, *script);
for (i::Tagged<i::SharedFunctionInfo> sfi = it.Next(); !sfi.is_null();
sfi = it.Next()) {
if (sfi->is_toplevel()) {
return isolate->debug()->SetBreakpointForFunction(
handle(sfi, isolate), isolate->factory()->empty_string(), id,
internal::Debug::kInstrumentation);
}
}
return false;
}
#if V8_ENABLE_WEBASSEMBLY
void Script::RemoveWasmBreakpoint(BreakpointId id) {
i::Handle<i::Script> script = Utils::OpenHandle(this);
i::Isolate* isolate = script->GetIsolate();
isolate->debug()->RemoveBreakpointForWasmScript(script, id);
}
#endif // V8_ENABLE_WEBASSEMBLY
void RemoveBreakpoint(Isolate* v8_isolate, BreakpointId id) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
i::HandleScope handle_scope(isolate);
isolate->debug()->RemoveBreakpoint(id);
}
Platform* GetCurrentPlatform() { return i::V8::GetCurrentPlatform(); }
void ForceGarbageCollection(Isolate* isolate, StackState embedder_stack_state) {
i::EmbedderStackStateScope stack_scope(
reinterpret_cast<i::Isolate*>(isolate)->heap(),
i::EmbedderStackStateOrigin::kImplicitThroughTask, embedder_stack_state);
isolate->LowMemoryNotification();
}
#if V8_ENABLE_WEBASSEMBLY
WasmScript* WasmScript::Cast(Script* script) {
CHECK(script->IsWasm());
return static_cast<WasmScript*>(script);
}
WasmScript::DebugSymbolsType WasmScript::GetDebugSymbolType() const {
auto script = Utils::OpenDirectHandle(this);
DCHECK_EQ(i::Script::Type::kWasm, script->type());
switch (script->wasm_native_module()->module()->debug_symbols.type) {
case i::wasm::WasmDebugSymbols::Type::None:
return WasmScript::DebugSymbolsType::None;
case i::wasm::WasmDebugSymbols::Type::EmbeddedDWARF:
return WasmScript::DebugSymbolsType::EmbeddedDWARF;
case i::wasm::WasmDebugSymbols::Type::ExternalDWARF:
return WasmScript::DebugSymbolsType::ExternalDWARF;
case i::wasm::WasmDebugSymbols::Type::SourceMap:
return WasmScript::DebugSymbolsType::SourceMap;
}
}
MemorySpan<const char> WasmScript::ExternalSymbolsURL() const {
auto script = Utils::OpenDirectHandle(this);
DCHECK_EQ(i::Script::Type::kWasm, script->type());
const i::wasm::WasmDebugSymbols& symbols =
script->wasm_native_module()->module()->debug_symbols;
if (symbols.external_url.is_empty()) return {};
internal::wasm::ModuleWireBytes wire_bytes(
script->wasm_native_module()->wire_bytes());
i::wasm::WasmName external_url =
wire_bytes.GetNameOrNull(symbols.external_url);
return {external_url.data(), external_url.size()};
}
int WasmScript::NumFunctions() const {
i::DisallowGarbageCollection no_gc;
auto script = Utils::OpenDirectHandle(this);
DCHECK_EQ(i::Script::Type::kWasm, script->type());
i::wasm::NativeModule* native_module = script->wasm_native_module();
const i::wasm::WasmModule* module = native_module->module();
DCHECK_GE(i::kMaxInt, module->functions.size());
return static_cast<int>(module->functions.size());
}
int WasmScript::NumImportedFunctions() const {
i::DisallowGarbageCollection no_gc;
auto script = Utils::OpenDirectHandle(this);
DCHECK_EQ(i::Script::Type::kWasm, script->type());
i::wasm::NativeModule* native_module = script->wasm_native_module();
const i::wasm::WasmModule* module = native_module->module();
DCHECK_GE(i::kMaxInt, module->num_imported_functions);
return static_cast<int>(module->num_imported_functions);
}
std::pair<int, int> WasmScript::GetFunctionRange(int function_index) const {
i::DisallowGarbageCollection no_gc;
auto script = Utils::OpenDirectHandle(this);
DCHECK_EQ(i::Script::Type::kWasm, script->type());
i::wasm::NativeModule* native_module = script->wasm_native_module();
const i::wasm::WasmModule* module = native_module->module();
DCHECK_LE(0, function_index);
DCHECK_GT(module->functions.size(), function_index);
const i::wasm::WasmFunction& func = module->functions[function_index];
DCHECK_GE(i::kMaxInt, func.code.offset());
DCHECK_GE(i::kMaxInt, func.code.end_offset());
return std::make_pair(static_cast<int>(func.code.offset()),
static_cast<int>(func.code.end_offset()));
}
int WasmScript::GetContainingFunction(int byte_offset) const {
i::DisallowGarbageCollection no_gc;
auto script = Utils::OpenDirectHandle(this);
DCHECK_EQ(i::Script::Type::kWasm, script->type());
i::wasm::NativeModule* native_module = script->wasm_native_module();
const i::wasm::WasmModule* module = native_module->module();
DCHECK_LE(0, byte_offset);
return i::wasm::GetContainingWasmFunction(module, byte_offset);
}
void WasmScript::Disassemble(DisassemblyCollector* collector,
std::vector<int>* function_body_offsets) {
i::DisallowGarbageCollection no_gc;
auto script = Utils::OpenDirectHandle(this);
DCHECK_EQ(i::Script::Type::kWasm, script->type());
i::wasm::NativeModule* native_module = script->wasm_native_module();
const i::wasm::WasmModule* module = native_module->module();
i::wasm::ModuleWireBytes wire_bytes(native_module->wire_bytes());
i::wasm::Disassemble(module, wire_bytes, native_module->GetNamesProvider(),
collector, function_body_offsets);
}
void Disassemble(base::Vector<const uint8_t> wire_bytes,
DisassemblyCollector* collector,
std::vector<int>* function_body_offsets) {
i::wasm::Disassemble(wire_bytes, collector, function_body_offsets);
}
uint32_t WasmScript::GetFunctionHash(int function_index) {
i::DisallowGarbageCollection no_gc;
auto script = Utils::OpenDirectHandle(this);
DCHECK_EQ(i::Script::Type::kWasm, script->type());
i::wasm::NativeModule* native_module = script->wasm_native_module();
const i::wasm::WasmModule* module = native_module->module();
DCHECK_LE(0, function_index);
DCHECK_GT(module->functions.size(), function_index);
const i::wasm::WasmFunction& func = module->functions[function_index];
i::wasm::ModuleWireBytes wire_bytes(native_module->wire_bytes());
base::Vector<const uint8_t> function_bytes =
wire_bytes.GetFunctionBytes(&func);
// TODO(herhut): Maybe also take module, name and signature into account.
return i::StringHasher::HashSequentialString(function_bytes.begin(),
function_bytes.length(), 0);
}
int WasmScript::CodeOffset() const {
auto script = Utils::OpenDirectHandle(this);
DCHECK_EQ(i::Script::Type::kWasm, script->type());
i::wasm::NativeModule* native_module = script->wasm_native_module();
const i::wasm::WasmModule* module = native_module->module();
// If the module contains at least one function, the code offset must have
// been initialized, and it cannot be zero.
DCHECK_IMPLIES(module->num_declared_functions > 0,
module->code.offset() != 0);
return module->code.offset();
}
#endif // V8_ENABLE_WEBASSEMBLY
Location::Location(int line_number, int column_number)
: line_number_(line_number),
column_number_(column_number),
is_empty_(false) {}
Location::Location()
: line_number_(Function::kLineOffsetNotFound),
column_number_(Function::kLineOffsetNotFound),
is_empty_(true) {}
int Location::GetLineNumber() const {
DCHECK(!IsEmpty());
return line_number_;
}
int Location::GetColumnNumber() const {
DCHECK(!IsEmpty());
return column_number_;
}
bool Location::IsEmpty() const { return is_empty_; }
void GetLoadedScripts(Isolate* v8_isolate,
std::vector<v8::Global<Script>>& scripts) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
{
i::DisallowGarbageCollection no_gc;
i::Script::Iterator iterator(isolate);
for (i::Tagged<i::Script> script = iterator.Next(); !script.is_null();
script = iterator.Next()) {
#if V8_ENABLE_WEBASSEMBLY
if (script->type() != i::Script::Type::kNormal &&
script->type() != i::Script::Type::kWasm) {
continue;
}
#else
if (script->type() != i::Script::Type::kNormal) continue;
#endif // V8_ENABLE_WEBASSEMBLY
if (!script->HasValidSource()) continue;
i::HandleScope handle_scope(isolate);
i::Handle<i::Script> script_handle(script, isolate);
scripts.emplace_back(v8_isolate, ToApiHandle<Script>(script_handle));
}
}
}
MaybeLocal<UnboundScript> CompileInspectorScript(Isolate* v8_isolate,
Local<String> source) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
v8::Local<v8::Context> context = Utils::ToLocal(isolate->native_context());
PREPARE_FOR_DEBUG_INTERFACE_EXECUTION_WITH_ISOLATE(isolate, context,
UnboundScript);
i::Handle<i::String> str = Utils::OpenHandle(*source);
i::Handle<i::SharedFunctionInfo> result;
{
i::AlignedCachedData* cached_data = nullptr;
ScriptCompiler::CompilationDetails compilation_details;
i::MaybeHandle<i::SharedFunctionInfo> maybe_function_info =
i::Compiler::GetSharedFunctionInfoForScriptWithCachedData(
isolate, str, i::ScriptDetails(), cached_data,
ScriptCompiler::kNoCompileOptions,
ScriptCompiler::kNoCacheBecauseInspector,
i::v8_flags.expose_inspector_scripts ? i::NOT_NATIVES_CODE
: i::INSPECTOR_CODE,
&compilation_details);
has_exception = !maybe_function_info.ToHandle(&result);
RETURN_ON_FAILED_EXECUTION(UnboundScript);
}
RETURN_ESCAPED(ToApiHandle<UnboundScript>(result));
}
#if V8_ENABLE_WEBASSEMBLY
void EnterDebuggingForIsolate(Isolate* v8_isolate) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
i::wasm::GetWasmEngine()->EnterDebuggingForIsolate(isolate);
}
void LeaveDebuggingForIsolate(Isolate* v8_isolate) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
i::wasm::GetWasmEngine()->LeaveDebuggingForIsolate(isolate);
}
#endif // V8_ENABLE_WEBASSEMBLY
void SetDebugDelegate(Isolate* v8_isolate, DebugDelegate* delegate) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
isolate->debug()->SetDebugDelegate(delegate);
}
void SetAsyncEventDelegate(Isolate* v8_isolate, AsyncEventDelegate* delegate) {
reinterpret_cast<i::Isolate*>(v8_isolate)->set_async_event_delegate(delegate);
}
void ResetBlackboxedStateCache(Isolate* v8_isolate, Local<Script> script) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
i::DisallowGarbageCollection no_gc;
i::SharedFunctionInfo::ScriptIterator iter(isolate,
*Utils::OpenDirectHandle(*script));
for (i::Tagged<i::SharedFunctionInfo> info = iter.Next(); !info.is_null();
info = iter.Next()) {
if (auto debug_info = isolate->debug()->TryGetDebugInfo(info)) {
debug_info.value()->set_computed_debug_is_blackboxed(false);
}
}
}
int EstimatedValueSize(Isolate* v8_isolate, Local<Value> value) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
auto object = Utils::OpenDirectHandle(*value);
if (IsSmi(*object)) return i::kTaggedSize;
CHECK(IsHeapObject(*object));
return i::DirectHandle<i::HeapObject>::cast(object)->Size();
}
void AccessorPair::CheckCast(Value* that) {
auto obj = Utils::OpenDirectHandle(that);
Utils::ApiCheck(i::IsAccessorPair(*obj), "v8::debug::AccessorPair::Cast",
"Value is not a v8::debug::AccessorPair");
}
#if V8_ENABLE_WEBASSEMBLY
void WasmValueObject::CheckCast(Value* that) {
auto obj = Utils::OpenDirectHandle(that);
Utils::ApiCheck(i::IsWasmValueObject(*obj),
"v8::debug::WasmValueObject::Cast",
"Value is not a v8::debug::WasmValueObject");
}
bool WasmValueObject::IsWasmValueObject(Local<Value> that) {
auto obj = Utils::OpenDirectHandle(*that);
return i::IsWasmValueObject(*obj);
}
Local<String> WasmValueObject::type() const {
auto object =
i::DirectHandle<i::WasmValueObject>::cast(Utils::OpenDirectHandle(this));
i::Isolate* isolate = object->GetIsolate();
i::DirectHandle<i::String> type(object->type(), isolate);
return Utils::ToLocal(type, isolate);
}
#endif // V8_ENABLE_WEBASSEMBLY
Local<Function> GetBuiltin(Isolate* v8_isolate, Builtin requested_builtin) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
i::HandleScope handle_scope(isolate);
CHECK_EQ(requested_builtin, kStringToLowerCase);
i::Builtin builtin = i::Builtin::kStringPrototypeToLocaleLowerCase;
i::Factory* factory = isolate->factory();
i::Handle<i::String> name = isolate->factory()->empty_string();
i::Handle<i::NativeContext> context(isolate->native_context());
i::Handle<i::SharedFunctionInfo> info =
factory->NewSharedFunctionInfoForBuiltin(name, builtin);
info->set_language_mode(i::LanguageMode::kStrict);
i::Handle<i::JSFunction> fun =
i::Factory::JSFunctionBuilder{isolate, info, context}
.set_map(isolate->strict_function_without_prototype_map())
.Build();
fun->shared()->set_internal_formal_parameter_count(i::JSParameterCount(0));
fun->shared()->set_length(0);
return Utils::ToLocal(handle_scope.CloseAndEscape(fun));
}
void SetConsoleDelegate(Isolate* v8_isolate, ConsoleDelegate* delegate) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
DCHECK_NO_SCRIPT_NO_EXCEPTION(isolate);
if (delegate == nullptr) {
isolate->set_console_delegate(nullptr);
} else {
isolate->set_console_delegate(delegate);
}
}
ConsoleCallArguments::ConsoleCallArguments(
const v8::FunctionCallbackInfo<v8::Value>& info)
: isolate_(info.GetIsolate()),
values_(info.values_),
length_(info.length_) {}
ConsoleCallArguments::ConsoleCallArguments(
internal::Isolate* isolate, const internal::BuiltinArguments& args)
: isolate_(reinterpret_cast<v8::Isolate*>(isolate)),
values_(args.length() > 1 ? args.address_of_first_argument() : nullptr),
length_(args.length() - 1) {}
v8::Local<v8::Message> CreateMessageFromException(
Isolate* v8_isolate, v8::Local<v8::Value> v8_error) {
i::Handle<i::Object> obj = Utils::OpenHandle(*v8_error);
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate);
i::HandleScope scope(isolate);
return Utils::MessageToLocal(
scope.CloseAndEscape(isolate->CreateMessageFromException(obj)));
}
MaybeLocal<Script> GeneratorObject::Script() {
auto obj = Utils::OpenDirectHandle(this);
i::Tagged<i::Object> maybe_script = obj->function()->shared()->script();
if (!IsScript(maybe_script)) return {};
i::Isolate* isolate = obj->GetIsolate();
i::DirectHandle<i::Script> script(i::Script::cast(maybe_script), isolate);
return ToApiHandle<v8::debug::Script>(script, isolate);
}
Local<Function> GeneratorObject::Function() {
auto obj = Utils::OpenDirectHandle(this);
return Utils::ToLocal(handle(obj->function(), obj->GetIsolate()));
}
Location GeneratorObject::SuspendedLocation() {
auto obj = Utils::OpenDirectHandle(this);
CHECK(obj->is_suspended());
i::Tagged<i::Object> maybe_script = obj->function()->shared()->script();
if (!IsScript(maybe_script)) return Location();
i::Isolate* isolate = obj->GetIsolate();
i::Handle<i::Script> script(i::Script::cast(maybe_script), isolate);
i::Script::PositionInfo info;
i::SharedFunctionInfo::EnsureSourcePositionsAvailable(
isolate, i::handle(obj->function()->shared(), isolate));
i::Script::GetPositionInfo(script, obj->source_position(), &info);
return Location(info.line, info.column);
}
bool GeneratorObject::IsSuspended() {
return Utils::OpenDirectHandle(this)->is_suspended();
}
v8::Local<GeneratorObject> GeneratorObject::Cast(v8::Local<v8::Value> value) {
CHECK(value->IsGeneratorObject());
return ToApiHandle<GeneratorObject>(Utils::OpenHandle(*value));
}
MaybeLocal<Value> CallFunctionOn(Local<Context> context,
Local<Function> function, Local<Value> recv,
int argc, Global<Value> argv[],
bool throw_on_side_effect) {
auto isolate = reinterpret_cast<i::Isolate*>(context->GetIsolate());
PREPARE_FOR_DEBUG_INTERFACE_EXECUTION_WITH_ISOLATE(isolate, context, Value);
auto self = Utils::OpenHandle(*function);
auto recv_obj = Utils::OpenHandle(*recv);
static_assert(sizeof(v8::Global<v8::Value>) == sizeof(i::Handle<i::Object>));
auto args = reinterpret_cast<i::Handle<i::Object>*>(argv);
// Disable breaks in side-effect free mode.
i::DisableBreak disable_break_scope(isolate->debug(), throw_on_side_effect);
if (throw_on_side_effect) {
isolate->debug()->StartSideEffectCheckMode();
}
Local<Value> result;
has_exception = !ToLocal<Value>(
i::Execution::Call(isolate, self, recv_obj, argc, args), &result);
if (throw_on_side_effect) {
isolate->debug()->StopSideEffectCheckMode();
}
RETURN_ON_FAILED_EXECUTION(Value);
RETURN_ESCAPED(result);
}
MaybeLocal<v8::Value> EvaluateGlobal(v8::Isolate* isolate,
v8::Local<v8::String> source,
EvaluateGlobalMode mode, bool repl) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
v8::Local<v8::Context> context = Utils::ToLocal(i_isolate->native_context());
PREPARE_FOR_DEBUG_INTERFACE_EXECUTION_WITH_ISOLATE(i_isolate, context, Value);
i::REPLMode repl_mode = repl ? i::REPLMode::kYes : i::REPLMode::kNo;
Local<Value> result;
has_exception = !ToLocal<Value>(
i::DebugEvaluate::Global(i_isolate, Utils::OpenHandle(*source), mode,
repl_mode),
&result);
RETURN_ON_FAILED_EXECUTION(Value);
RETURN_ESCAPED(result);
}
void GlobalLexicalScopeNames(v8::Local<v8::Context> v8_context,
std::vector<v8::Global<v8::String>>* names) {
auto context = Utils::OpenDirectHandle(*v8_context);
i::Isolate* isolate = context->GetIsolate();
i::DirectHandle<i::ScriptContextTable> table(
context->native_context()->script_context_table(), isolate);
for (int i = 0; i < table->length(kAcquireLoad); i++) {
i::DirectHandle<i::Context> script_context(table->get(i), isolate);
DCHECK(script_context->IsScriptContext());
i::Handle<i::ScopeInfo> scope_info(script_context->scope_info(), isolate);
for (auto it : i::ScopeInfo::IterateLocalNames(scope_info)) {
if (i::ScopeInfo::VariableIsSynthetic(it->name())) continue;
names->emplace_back(reinterpret_cast<Isolate*>(isolate),
Utils::ToLocal(handle(it->name(), isolate)));
}
}
}
void SetReturnValue(v8::Isolate* v8_isolate, v8::Local<v8::Value> value) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
isolate->debug()->set_return_value(*Utils::OpenDirectHandle(*value));
}
int64_t GetNextRandomInt64(v8::Isolate* v8_isolate) {
return reinterpret_cast<i::Isolate*>(v8_isolate)
->random_number_generator()
->NextInt64();
}
int GetDebuggingId(v8::Local<v8::Function> function) {
i::Handle<i::JSReceiver> callable = v8::Utils::OpenHandle(*function);
if (!IsJSFunction(*callable)) return i::DebugInfo::kNoDebuggingId;
i::Handle<i::JSFunction> func = i::Handle<i::JSFunction>::cast(callable);
int id = func->GetIsolate()->debug()->GetFunctionDebuggingId(func);
DCHECK_NE(i::DebugInfo::kNoDebuggingId, id);
return id;
}
bool SetFunctionBreakpoint(v8::Local<v8::Function> function,
v8::Local<v8::String> condition, BreakpointId* id) {
auto callable = Utils::OpenDirectHandle(*function);
if (!IsJSFunction(*callable)) return false;
auto jsfunction = i::DirectHandle<i::JSFunction>::cast(callable);
i::Isolate* isolate = jsfunction->GetIsolate();
i::Handle<i::String> condition_string =
condition.IsEmpty() ? isolate->factory()->empty_string()
: Utils::OpenHandle(*condition);
return isolate->debug()->SetBreakpointForFunction(
handle(jsfunction->shared(), isolate), condition_string, id);
}
PostponeInterruptsScope::PostponeInterruptsScope(v8::Isolate* isolate)
: scope_(
new i::PostponeInterruptsScope(reinterpret_cast<i::Isolate*>(isolate),
i::StackGuard::API_INTERRUPT)) {}
PostponeInterruptsScope::~PostponeInterruptsScope() = default;
DisableBreakScope::DisableBreakScope(v8::Isolate* isolate)
: scope_(std::make_unique<i::DisableBreak>(
reinterpret_cast<i::Isolate*>(isolate)->debug())) {}
DisableBreakScope::~DisableBreakScope() = default;
int Coverage::BlockData::StartOffset() const { return block_->start; }
int Coverage::BlockData::EndOffset() const { return block_->end; }
uint32_t Coverage::BlockData::Count() const { return block_->count; }
int Coverage::FunctionData::StartOffset() const { return function_->start; }
int Coverage::FunctionData::EndOffset() const { return function_->end; }
uint32_t Coverage::FunctionData::Count() const { return function_->count; }
MaybeLocal<String> Coverage::FunctionData::Name() const {
return ToApiHandle<String>(function_->name);
}
size_t Coverage::FunctionData::BlockCount() const {
return function_->blocks.size();
}
bool Coverage::FunctionData::HasBlockCoverage() const {
return function_->has_block_coverage;
}
Coverage::BlockData Coverage::FunctionData::GetBlockData(size_t i) const {
return BlockData(&function_->blocks.at(i), coverage_);
}
Local<Script> Coverage::ScriptData::GetScript() const {
return ToApiHandle<Script>(script_->script);
}
size_t Coverage::ScriptData::FunctionCount() const {
return script_->functions.size();
}
Coverage::FunctionData Coverage::ScriptData::GetFunctionData(size_t i) const {
return FunctionData(&script_->functions.at(i), coverage_);
}
Coverage::ScriptData::ScriptData(size_t index,
std::shared_ptr<i::Coverage> coverage)
: script_(&coverage->at(index)), coverage_(std::move(coverage)) {}
size_t Coverage::ScriptCount() const { return coverage_->size(); }
Coverage::ScriptData Coverage::GetScriptData(size_t i) const {
return ScriptData(i, coverage_);
}
Coverage Coverage::CollectPrecise(Isolate* isolate) {
return Coverage(
i::Coverage::CollectPrecise(reinterpret_cast<i::Isolate*>(isolate)));
}
Coverage Coverage::CollectBestEffort(Isolate* isolate) {
return Coverage(
i::Coverage::CollectBestEffort(reinterpret_cast<i::Isolate*>(isolate)));
}
void Coverage::SelectMode(Isolate* isolate, CoverageMode mode) {
i::Coverage::SelectMode(reinterpret_cast<i::Isolate*>(isolate), mode);
}
MaybeLocal<v8::Value> EphemeronTable::Get(v8::Isolate* isolate,
v8::Local<v8::Value> key) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
auto self = i::DirectHandle<i::EphemeronHashTable>::cast(
Utils::OpenDirectHandle(this));
i::Handle<i::Object> internal_key = Utils::OpenHandle(*key);
DCHECK(IsJSReceiver(*internal_key));
i::DirectHandle<i::Object> value(self->Lookup(internal_key), i_isolate);
if (IsTheHole(*value)) return {};
return Utils::ToLocal(value, i_isolate);
}
Local<EphemeronTable> EphemeronTable::Set(v8::Isolate* isolate,
v8::Local<v8::Value> key,
v8::Local<v8::Value> value) {
auto self = i::Handle<i::EphemeronHashTable>::cast(Utils::OpenHandle(this));
i::Handle<i::Object> internal_key = Utils::OpenHandle(*key);
i::Handle<i::Object> internal_value = Utils::OpenHandle(*value);
DCHECK(IsJSReceiver(*internal_key));
i::Handle<i::EphemeronHashTable> result(
i::EphemeronHashTable::Put(self, internal_key, internal_value));
return ToApiHandle<EphemeronTable>(result);
}
Local<EphemeronTable> EphemeronTable::New(v8::Isolate* isolate) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
i::Handle<i::EphemeronHashTable> table =
i::EphemeronHashTable::New(i_isolate, 0);
return ToApiHandle<EphemeronTable>(table);
}
EphemeronTable* EphemeronTable::Cast(v8::Value* value) {
return static_cast<EphemeronTable*>(value);
}
Local<Value> AccessorPair::getter() {
auto accessors = Utils::OpenDirectHandle(this);
i::Isolate* isolate = accessors->GetIsolate();
i::DirectHandle<i::Object> getter(accessors->getter(), isolate);
return Utils::ToLocal(getter, isolate);
}
Local<Value> AccessorPair::setter() {
auto accessors = Utils::OpenDirectHandle(this);
i::Isolate* isolate = accessors->GetIsolate();
i::DirectHandle<i::Object> setter(accessors->setter(), isolate);
return Utils::ToLocal(setter, isolate);
}
bool AccessorPair::IsAccessorPair(Local<Value> that) {
return i::IsAccessorPair(*Utils::OpenDirectHandle(*that));
}
MaybeLocal<Message> GetMessageFromPromise(Local<Promise> p) {
i::Handle<i::JSPromise> promise = Utils::OpenHandle(*p);
i::Isolate* isolate = promise->GetIsolate();
i::Handle<i::Symbol> key = isolate->factory()->promise_debug_message_symbol();
i::Handle<i::Object> maybeMessage =
i::JSReceiver::GetDataProperty(isolate, promise, key);
if (!IsJSMessageObject(*maybeMessage, isolate)) return MaybeLocal<Message>();
return ToApiHandle<Message>(
i::Handle<i::JSMessageObject>::cast(maybeMessage));
}
void RecordAsyncStackTaggingCreateTaskCall(v8::Isolate* v8_isolate) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
isolate->CountUsage(v8::Isolate::kAsyncStackTaggingCreateTaskCall);
}
void NotifyDebuggerPausedEventSent(v8::Isolate* v8_isolate) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
isolate->debug()->NotifyDebuggerPausedEventSent();
}
std::unique_ptr<PropertyIterator> PropertyIterator::Create(
Local<Context> context, Local<Object> object, bool skip_indices) {
internal::Isolate* isolate =
reinterpret_cast<i::Isolate*>(context->GetIsolate());
if (isolate->is_execution_terminating()) {
return nullptr;
}
CallDepthScope<false> call_depth_scope(isolate, context);
return i::DebugPropertyIterator::Create(isolate, Utils::OpenHandle(*object),
skip_indices);
}
} // namespace debug
namespace internal {
Maybe<bool> DebugPropertyIterator::Advance() {
if (isolate_->is_execution_terminating()) {
return Nothing<bool>();
}
Local<v8::Context> context =
Utils::ToLocal(handle(isolate_->context()->native_context(), isolate_));
CallDepthScope<false> call_depth_scope(isolate_, context);
if (!AdvanceInternal()) {
DCHECK(isolate_->has_exception());
return Nothing<bool>();
}
return Just(true);
}
} // namespace internal
} // namespace v8
#include "src/api/api-macros-undef.h"