// 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, int id) { auto v8_context = Utils::OpenHandle(*context); DCHECK_NO_SCRIPT_NO_EXCEPTION(v8_context->GetIsolate()); v8_context->set_debug_context_id(i::Smi::FromInt(id)); } int GetContextId(Local context) { auto v8_context = Utils::OpenHandle(*context); DCHECK_NO_SCRIPT_NO_EXCEPTION_MAYBE_TEARDOWN(v8_context->GetIsolate()); i::Object value = v8_context->debug_context_id(); return (value.IsSmi()) ? i::Smi::ToInt(value) : 0; } void SetInspector(Isolate* isolate, v8_inspector::V8Inspector* inspector) { i::Isolate* i_isolate = reinterpret_cast(isolate); if (inspector == nullptr) { DCHECK_NO_SCRIPT_NO_EXCEPTION_MAYBE_TEARDOWN(i_isolate); i_isolate->set_inspector(nullptr); } else { DCHECK_NO_SCRIPT_NO_EXCEPTION(i_isolate); i_isolate->set_inspector(inspector); } } v8_inspector::V8Inspector* GetInspector(Isolate* isolate) { i::Isolate* i_isolate = reinterpret_cast(isolate); DCHECK_NO_SCRIPT_NO_EXCEPTION_MAYBE_TEARDOWN(i_isolate); return i_isolate->inspector(); } namespace { i::Handle GetBigIntStringPresentationHandle( i::Isolate* i_isolate, i::Handle 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 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 GetBigIntStringValue(Isolate* isolate, Local bigint) { i::Isolate* i_isolate = reinterpret_cast(isolate); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); i::Handle i_bigint = Utils::OpenHandle(*bigint); i::Handle string_value = GetBigIntStringPresentationHandle(i_isolate, i_bigint); return Utils::ToLocal(string_value); } Local GetBigIntDescription(Isolate* isolate, Local bigint) { i::Isolate* i_isolate = reinterpret_cast(isolate); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); i::Handle i_bigint = Utils::OpenHandle(*bigint); i::Handle string_value = GetBigIntStringPresentationHandle(i_isolate, i_bigint); i::Handle description = i_isolate->factory() ->NewConsString( string_value, i_isolate->factory()->LookupSingleCharacterStringFromCode('n')) .ToHandleChecked(); return Utils::ToLocal(description); } Local GetDateDescription(Local date) { auto receiver = Utils::OpenHandle(*date); i::Handle jsdate = i::Handle::cast(receiver); i::Isolate* i_isolate = jsdate->GetIsolate(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); auto buffer = i::ToDateString(jsdate->value().Number(), i_isolate->date_cache(), i::ToDateStringMode::kLocalDateAndTime); return Utils::ToLocal(i_isolate->factory() ->NewStringFromUtf8(base::VectorOf(buffer)) .ToHandleChecked()); } Local GetFunctionDescription(Local function) { auto receiver = Utils::OpenHandle(*function); auto i_isolate = receiver->GetIsolate(); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); if (receiver->IsJSBoundFunction()) { return Utils::ToLocal(i::JSBoundFunction::ToString( i::Handle::cast(receiver))); } if (receiver->IsJSFunction()) { auto js_function = i::Handle::cast(receiver); #if V8_ENABLE_WEBASSEMBLY if (js_function->shared().HasWasmExportedFunctionData()) { auto i_isolate = js_function->GetIsolate(); auto func_index = js_function->shared().wasm_exported_function_data().function_index(); auto instance = i::handle( js_function->shared().wasm_exported_function_data().instance(), i_isolate); if (instance->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, func_index); i::IncrementalStringBuilder builder(i_isolate); builder.AppendCStringLiteral("function "); builder.AppendString(debug_name); builder.AppendCStringLiteral("() { [native code] }"); return Utils::ToLocal(builder.Finish().ToHandleChecked()); } } #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(isolate); DCHECK_NO_SCRIPT_NO_EXCEPTION(i_isolate); i_isolate->debug()->SetBreakOnNextFunctionCall(); } void ClearBreakOnNextFunctionCall(Isolate* isolate) { auto i_isolate = reinterpret_cast(isolate); DCHECK_NO_SCRIPT_NO_EXCEPTION(i_isolate); i_isolate->debug()->ClearBreakOnNextFunctionCall(); } MaybeLocal GetInternalProperties(Isolate* v8_isolate, Local value) { i::Isolate* isolate = reinterpret_cast(v8_isolate); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate); i::Handle val = Utils::OpenHandle(*value); i::Handle result; if (!i::Runtime::GetInternalProperties(isolate, val).ToHandle(&result)) return MaybeLocal(); return Utils::ToLocal(result); } namespace { void CollectPrivateMethodsAndAccessorsFromContext( i::Isolate* isolate, i::Handle context, i::IsStaticFlag is_static_flag, std::vector>* names_out, std::vector>* values_out) { DCHECK_NO_SCRIPT_NO_EXCEPTION(isolate); i::Handle scope_info(context->scope_info(), isolate); for (auto it : i::ScopeInfo::IterateLocalNames(scope_info)) { i::Handle name(it->name(), isolate); i::VariableMode mode = scope_info->ContextLocalMode(it->index()); i::IsStaticFlag flag = scope_info->ContextLocalIsStaticFlag(it->index()); if (!i::IsPrivateMethodOrAccessorVariableMode(mode) || flag != is_static_flag) { continue; } int context_index = scope_info->ContextHeaderLength() + it->index(); i::Handle slot_value(context->get(context_index), isolate); DCHECK_IMPLIES(mode == i::VariableMode::kPrivateMethod, slot_value->IsJSFunction()); DCHECK_IMPLIES(mode != i::VariableMode::kPrivateMethod, slot_value->IsAccessorPair()); names_out->push_back(Utils::ToLocal(name)); values_out->push_back(Utils::ToLocal(slot_value)); } } } // namespace bool GetPrivateMembers(Local context, Local object, std::vector>* names_out, std::vector>* values_out) { i::Isolate* isolate = reinterpret_cast(context->GetIsolate()); API_RCS_SCOPE(isolate, debug, GetPrivateMembers); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate); i::Handle receiver = Utils::OpenHandle(*object); i::Handle names; i::Handle values; i::PropertyFilter key_filter = static_cast(i::PropertyFilter::PRIVATE_NAMES_ONLY); i::Handle 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; for (int i = 0; i < keys->length(); ++i) { // Exclude the private brand symbols. i::Handle key(i::Symbol::cast(keys->get(i)), isolate); if (key->is_private_brand()) { i::Handle value; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, value, i::Object::GetProperty(isolate, receiver, key), false); i::Handle value_context(i::Context::cast(*value), isolate); i::Handle scope_info(value_context->scope_info(), isolate); // At least one slot contains the brand symbol so it does not count. private_entries_count += (scope_info->ContextLocalCount() - 1); } else { private_entries_count++; } } // Estimate number of static private methods/accessors for classes. bool has_static_private_methods_or_accessors = false; if (receiver->IsJSFunction()) { i::Handle func(i::JSFunction::cast(*receiver), isolate); i::Handle 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 func_context(func->context(), isolate); i::Handle scope_info(func_context->scope_info(), isolate); int local_count = scope_info->ContextLocalCount(); for (int j = 0; j < local_count; ++j) { i::VariableMode mode = scope_info->ContextLocalMode(j); i::IsStaticFlag is_static_flag = scope_info->ContextLocalIsStaticFlag(j); if (i::IsPrivateMethodOrAccessorVariableMode(mode) && is_static_flag == i::IsStaticFlag::kStatic) { private_entries_count += local_count; break; } } } } DCHECK(names_out->empty()); names_out->reserve(private_entries_count); DCHECK(values_out->empty()); values_out->reserve(private_entries_count); if (has_static_private_methods_or_accessors) { i::Handle recevier_context( i::JSFunction::cast(*receiver).context(), isolate); CollectPrivateMethodsAndAccessorsFromContext(isolate, recevier_context, i::IsStaticFlag::kStatic, names_out, values_out); } for (int i = 0; i < keys->length(); ++i) { i::Handle obj_key(keys->get(i), isolate); i::Handle key(i::Symbol::cast(*obj_key), isolate); CHECK(key->is_private_name()); i::Handle value; ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate, value, i::Object::GetProperty(isolate, receiver, key), false); if (key->is_private_brand()) { DCHECK(value->IsContext()); i::Handle value_context(i::Context::cast(*value), isolate); CollectPrivateMethodsAndAccessorsFromContext(isolate, value_context, i::IsStaticFlag::kNotStatic, names_out, values_out); } else { // Private fields i::Handle name( i::String::cast(i::Symbol::cast(*key).description()), isolate); names_out->push_back(Utils::ToLocal(name)); values_out->push_back(Utils::ToLocal(value)); } } DCHECK_EQ(names_out->size(), values_out->size()); DCHECK_LE(names_out->size(), private_entries_count); return true; } MaybeLocal GetCreationContext(Local value) { i::Handle val = Utils::OpenHandle(*value); if (val->IsJSGlobalProxy()) { return MaybeLocal(); } return value->GetCreationContext(); } void ChangeBreakOnException(Isolate* isolate, ExceptionBreakState type) { i::Isolate* i_isolate = reinterpret_cast(isolate); DCHECK_NO_SCRIPT_NO_EXCEPTION(i_isolate); i_isolate->debug()->ChangeBreakOnException(i::BreakException, type == BreakOnAnyException); i_isolate->debug()->ChangeBreakOnException(i::BreakUncaughtException, type != NoBreakOnException); } void SetBreakPointsActive(Isolate* v8_isolate, bool is_active) { i::Isolate* isolate = reinterpret_cast(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(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(action)); } bool PrepareRestartFrame(Isolate* v8_isolate, int callFrameOrdinal) { i::Isolate* isolate = reinterpret_cast(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(v8_isolate); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate); // Clear all current stepping setup. isolate->debug()->ClearStepping(); } void BreakRightNow(Isolate* v8_isolate, base::EnumSet break_reasons) { i::Isolate* isolate = reinterpret_cast(v8_isolate); ENTER_V8_BASIC(isolate); isolate->debug()->HandleDebugBreak(i::kIgnoreIfAllFramesBlackboxed, break_reasons); } void SetTerminateOnResume(Isolate* v8_isolate) { i::Isolate* isolate = reinterpret_cast(v8_isolate); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate); isolate->debug()->SetTerminateOnResume(); } bool CanBreakProgram(Isolate* v8_isolate) { i::Isolate* isolate = reinterpret_cast(v8_isolate); ENTER_V8_BASIC(isolate); return !isolate->debug()->AllFramesOnStackAreBlackboxed(); } size_t ScriptSource::Length() const { i::Handle source = Utils::OpenHandle(this); if (source->IsString()) return i::Handle::cast(source)->length(); return Size(); } size_t ScriptSource::Size() const { #if V8_ENABLE_WEBASSEMBLY MemorySpan wasm_bytecode; if (WasmBytecode().To(&wasm_bytecode)) { return wasm_bytecode.size(); } #endif // V8_ENABLE_WEBASSEMBLY i::Handle source = Utils::OpenHandle(this); if (!source->IsString()) return 0; i::Handle string = i::Handle::cast(source); return string->length() * (string->IsTwoByteRepresentation() ? 2 : 1); } MaybeLocal ScriptSource::JavaScriptCode() const { i::Handle source = Utils::OpenHandle(this); if (!source->IsString()) return MaybeLocal(); return Utils::ToLocal(i::Handle::cast(source)); } #if V8_ENABLE_WEBASSEMBLY Maybe> ScriptSource::WasmBytecode() const { i::Handle source = Utils::OpenHandle(this); if (!source->IsForeign()) return Nothing>(); base::Vector wire_bytes = i::Managed::cast(*source).raw()->wire_bytes(); return Just(MemorySpan{wire_bytes.begin(), wire_bytes.size()}); } #endif // V8_ENABLE_WEBASSEMBLY Isolate* Script::GetIsolate() const { return reinterpret_cast(Utils::OpenHandle(this)->GetIsolate()); } ScriptOriginOptions Script::OriginOptions() const { return Utils::OpenHandle(this)->origin_options(); } bool Script::WasCompiled() const { return Utils::OpenHandle(this)->compilation_state() == i::Script::COMPILATION_STATE_COMPILED; } bool Script::IsEmbedded() const { i::Handle script = Utils::OpenHandle(this); return script->context_data() == script->GetReadOnlyRoots().uninitialized_symbol(); } int Script::Id() const { return Utils::OpenHandle(this)->id(); } int Script::StartLine() const { return Utils::OpenHandle(this)->line_offset(); } int Script::StartColumn() const { return Utils::OpenHandle(this)->column_offset(); } int Script::EndLine() const { i::Handle script = Utils::OpenHandle(this); #if V8_ENABLE_WEBASSEMBLY if (script->type() == i::Script::TYPE_WASM) return 0; #endif // V8_ENABLE_WEBASSEMBLY if (!script->source().IsString()) { 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, i::Script::WITH_OFFSET); return info.line; } int Script::EndColumn() const { i::Handle script = Utils::OpenHandle(this); #if V8_ENABLE_WEBASSEMBLY if (script->type() == i::Script::TYPE_WASM) { return script->wasm_native_module()->wire_bytes().length(); } #endif // V8_ENABLE_WEBASSEMBLY if (!script->source().IsString()) { 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, i::Script::WITH_OFFSET); return info.column; } MaybeLocal Script::Name() const { i::Handle script = Utils::OpenHandle(this); i::Isolate* isolate = script->GetIsolate(); i::Handle value(script->name(), isolate); if (!value->IsString()) return MaybeLocal(); return Utils::ToLocal(i::Handle::cast(value)); } MaybeLocal Script::SourceURL() const { i::Handle script = Utils::OpenHandle(this); i::Isolate* isolate = script->GetIsolate(); i::Handle value(script->source_url(), isolate); if (!value->IsString()) return MaybeLocal(); return Utils::ToLocal(i::Handle::cast(value)); } MaybeLocal Script::SourceMappingURL() const { i::Handle script = Utils::OpenHandle(this); i::Isolate* isolate = script->GetIsolate(); i::Handle value(script->source_mapping_url(), isolate); if (!value->IsString()) return MaybeLocal(); return Utils::ToLocal(i::Handle::cast(value)); } MaybeLocal Script::GetSha256Hash() const { i::Handle script = Utils::OpenHandle(this); i::Isolate* isolate = script->GetIsolate(); i::Handle value = i::Script::GetScriptHash(isolate, script, /* forceForInspector: */ true); return Utils::ToLocal(value); } Maybe Script::ContextId() const { i::Handle script = Utils::OpenHandle(this); i::Object value = script->context_data(); if (value.IsSmi()) return Just(i::Smi::ToInt(value)); return Nothing(); } Local Script::Source() const { i::Handle script = Utils::OpenHandle(this); i::Isolate* isolate = script->GetIsolate(); #if V8_ENABLE_WEBASSEMBLY if (script->type() == i::Script::TYPE_WASM) { i::Handle wasm_native_module( script->wasm_managed_native_module(), isolate); return Utils::Convert(wasm_native_module); } #endif // V8_ENABLE_WEBASSEMBLY i::Handle source(script->source(), isolate); return Utils::Convert(source); } #if V8_ENABLE_WEBASSEMBLY bool Script::IsWasm() const { return Utils::OpenHandle(this)->type() == i::Script::TYPE_WASM; } #endif // V8_ENABLE_WEBASSEMBLY bool Script::IsModule() const { return Utils::OpenHandle(this)->origin_options().IsModule(); } namespace { int GetSmiValue(i::Handle 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* locations) const { CHECK(!start.IsEmpty()); i::Handle script = Utils::OpenHandle(this); #if V8_ENABLE_WEBASSEMBLY if (script->type() == i::Script::TYPE_WASM) { 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::max(); } else if (!GetSourceOffset(end, GetSourceOffsetMode::kClamp) .To(&end_offset)) { return false; } if (start_offset >= end_offset) return true; std::vector 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 Script::GetSourceOffset(const Location& location, GetSourceOffsetMode mode) const { i::Handle script = Utils::OpenHandle(this); #if V8_ENABLE_WEBASSEMBLY if (script->type() == i::Script::TYPE_WASM) { 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