// 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 "src/objects/debug-objects.h"
|
#include "src/debug/debug-evaluate.h"
|
#include "src/objects/debug-objects-inl.h"
|
|
namespace v8 {
|
namespace internal {
|
|
bool DebugInfo::IsEmpty() const {
|
return flags() == kNone && debugger_hints() == 0;
|
}
|
|
bool DebugInfo::HasBreakInfo() const { return (flags() & kHasBreakInfo) != 0; }
|
|
DebugInfo::ExecutionMode DebugInfo::DebugExecutionMode() const {
|
return (flags() & kDebugExecutionMode) != 0 ? kSideEffects : kBreakpoints;
|
}
|
|
void DebugInfo::SetDebugExecutionMode(ExecutionMode value) {
|
set_flags(value == kSideEffects ? (flags() | kDebugExecutionMode)
|
: (flags() & ~kDebugExecutionMode));
|
}
|
|
void DebugInfo::ClearBreakInfo(Isolate* isolate) {
|
if (HasInstrumentedBytecodeArray()) {
|
// Reset function's bytecode array field to point to the original bytecode
|
// array.
|
shared()->SetDebugBytecodeArray(OriginalBytecodeArray());
|
set_original_bytecode_array(ReadOnlyRoots(isolate).undefined_value());
|
}
|
set_break_points(ReadOnlyRoots(isolate).empty_fixed_array());
|
|
int new_flags = flags();
|
new_flags &= ~kHasBreakInfo & ~kPreparedForDebugExecution;
|
new_flags &= ~kBreakAtEntry & ~kCanBreakAtEntry;
|
new_flags &= ~kDebugExecutionMode;
|
set_flags(new_flags);
|
}
|
|
void DebugInfo::SetBreakAtEntry() {
|
DCHECK(CanBreakAtEntry());
|
set_flags(flags() | kBreakAtEntry);
|
}
|
|
void DebugInfo::ClearBreakAtEntry() {
|
DCHECK(CanBreakAtEntry());
|
set_flags(flags() & ~kBreakAtEntry);
|
}
|
|
bool DebugInfo::BreakAtEntry() const { return (flags() & kBreakAtEntry) != 0; }
|
|
bool DebugInfo::CanBreakAtEntry() const {
|
return (flags() & kCanBreakAtEntry) != 0;
|
}
|
|
// Check if there is a break point at this source position.
|
bool DebugInfo::HasBreakPoint(Isolate* isolate, int source_position) {
|
DCHECK(HasBreakInfo());
|
// Get the break point info object for this code offset.
|
Object* break_point_info = GetBreakPointInfo(isolate, source_position);
|
|
// If there is no break point info object or no break points in the break
|
// point info object there is no break point at this code offset.
|
if (break_point_info->IsUndefined(isolate)) return false;
|
return BreakPointInfo::cast(break_point_info)->GetBreakPointCount(isolate) >
|
0;
|
}
|
|
// Get the break point info object for this source position.
|
Object* DebugInfo::GetBreakPointInfo(Isolate* isolate, int source_position) {
|
DCHECK(HasBreakInfo());
|
for (int i = 0; i < break_points()->length(); i++) {
|
if (!break_points()->get(i)->IsUndefined(isolate)) {
|
BreakPointInfo* break_point_info =
|
BreakPointInfo::cast(break_points()->get(i));
|
if (break_point_info->source_position() == source_position) {
|
return break_point_info;
|
}
|
}
|
}
|
return ReadOnlyRoots(isolate).undefined_value();
|
}
|
|
bool DebugInfo::ClearBreakPoint(Isolate* isolate, Handle<DebugInfo> debug_info,
|
Handle<BreakPoint> break_point) {
|
DCHECK(debug_info->HasBreakInfo());
|
for (int i = 0; i < debug_info->break_points()->length(); i++) {
|
if (debug_info->break_points()->get(i)->IsUndefined(isolate)) continue;
|
Handle<BreakPointInfo> break_point_info = Handle<BreakPointInfo>(
|
BreakPointInfo::cast(debug_info->break_points()->get(i)), isolate);
|
if (BreakPointInfo::HasBreakPoint(isolate, break_point_info, break_point)) {
|
BreakPointInfo::ClearBreakPoint(isolate, break_point_info, break_point);
|
return true;
|
}
|
}
|
return false;
|
}
|
|
void DebugInfo::SetBreakPoint(Isolate* isolate, Handle<DebugInfo> debug_info,
|
int source_position,
|
Handle<BreakPoint> break_point) {
|
DCHECK(debug_info->HasBreakInfo());
|
Handle<Object> break_point_info(
|
debug_info->GetBreakPointInfo(isolate, source_position), isolate);
|
if (!break_point_info->IsUndefined(isolate)) {
|
BreakPointInfo::SetBreakPoint(
|
isolate, Handle<BreakPointInfo>::cast(break_point_info), break_point);
|
return;
|
}
|
|
// Adding a new break point for a code offset which did not have any
|
// break points before. Try to find a free slot.
|
static const int kNoBreakPointInfo = -1;
|
int index = kNoBreakPointInfo;
|
for (int i = 0; i < debug_info->break_points()->length(); i++) {
|
if (debug_info->break_points()->get(i)->IsUndefined(isolate)) {
|
index = i;
|
break;
|
}
|
}
|
if (index == kNoBreakPointInfo) {
|
// No free slot - extend break point info array.
|
Handle<FixedArray> old_break_points =
|
Handle<FixedArray>(debug_info->break_points(), isolate);
|
Handle<FixedArray> new_break_points = isolate->factory()->NewFixedArray(
|
old_break_points->length() +
|
DebugInfo::kEstimatedNofBreakPointsInFunction);
|
|
debug_info->set_break_points(*new_break_points);
|
for (int i = 0; i < old_break_points->length(); i++) {
|
new_break_points->set(i, old_break_points->get(i));
|
}
|
index = old_break_points->length();
|
}
|
DCHECK_NE(index, kNoBreakPointInfo);
|
|
// Allocate new BreakPointInfo object and set the break point.
|
Handle<BreakPointInfo> new_break_point_info =
|
isolate->factory()->NewBreakPointInfo(source_position);
|
BreakPointInfo::SetBreakPoint(isolate, new_break_point_info, break_point);
|
debug_info->break_points()->set(index, *new_break_point_info);
|
}
|
|
// Get the break point objects for a source position.
|
Handle<Object> DebugInfo::GetBreakPoints(Isolate* isolate,
|
int source_position) {
|
DCHECK(HasBreakInfo());
|
Object* break_point_info = GetBreakPointInfo(isolate, source_position);
|
if (break_point_info->IsUndefined(isolate)) {
|
return isolate->factory()->undefined_value();
|
}
|
return Handle<Object>(BreakPointInfo::cast(break_point_info)->break_points(),
|
isolate);
|
}
|
|
// Get the total number of break points.
|
int DebugInfo::GetBreakPointCount(Isolate* isolate) {
|
DCHECK(HasBreakInfo());
|
int count = 0;
|
for (int i = 0; i < break_points()->length(); i++) {
|
if (!break_points()->get(i)->IsUndefined(isolate)) {
|
BreakPointInfo* break_point_info =
|
BreakPointInfo::cast(break_points()->get(i));
|
count += break_point_info->GetBreakPointCount(isolate);
|
}
|
}
|
return count;
|
}
|
|
Handle<Object> DebugInfo::FindBreakPointInfo(Isolate* isolate,
|
Handle<DebugInfo> debug_info,
|
Handle<BreakPoint> break_point) {
|
DCHECK(debug_info->HasBreakInfo());
|
for (int i = 0; i < debug_info->break_points()->length(); i++) {
|
if (!debug_info->break_points()->get(i)->IsUndefined(isolate)) {
|
Handle<BreakPointInfo> break_point_info = Handle<BreakPointInfo>(
|
BreakPointInfo::cast(debug_info->break_points()->get(i)), isolate);
|
if (BreakPointInfo::HasBreakPoint(isolate, break_point_info,
|
break_point)) {
|
return break_point_info;
|
}
|
}
|
}
|
return isolate->factory()->undefined_value();
|
}
|
|
bool DebugInfo::HasCoverageInfo() const {
|
return (flags() & kHasCoverageInfo) != 0;
|
}
|
|
void DebugInfo::ClearCoverageInfo(Isolate* isolate) {
|
if (HasCoverageInfo()) {
|
set_coverage_info(ReadOnlyRoots(isolate).undefined_value());
|
|
int new_flags = flags() & ~kHasCoverageInfo;
|
set_flags(new_flags);
|
}
|
}
|
|
DebugInfo::SideEffectState DebugInfo::GetSideEffectState(Isolate* isolate) {
|
if (side_effect_state() == kNotComputed) {
|
SideEffectState has_no_side_effect =
|
DebugEvaluate::FunctionGetSideEffectState(isolate,
|
handle(shared(), isolate));
|
set_side_effect_state(has_no_side_effect);
|
}
|
return static_cast<SideEffectState>(side_effect_state());
|
}
|
|
namespace {
|
bool IsEqual(BreakPoint* break_point1, BreakPoint* break_point2) {
|
return break_point1->id() == break_point2->id();
|
}
|
} // namespace
|
|
// Remove the specified break point object.
|
void BreakPointInfo::ClearBreakPoint(Isolate* isolate,
|
Handle<BreakPointInfo> break_point_info,
|
Handle<BreakPoint> break_point) {
|
// If there are no break points just ignore.
|
if (break_point_info->break_points()->IsUndefined(isolate)) return;
|
// If there is a single break point clear it if it is the same.
|
if (!break_point_info->break_points()->IsFixedArray()) {
|
if (IsEqual(BreakPoint::cast(break_point_info->break_points()),
|
*break_point)) {
|
break_point_info->set_break_points(
|
ReadOnlyRoots(isolate).undefined_value());
|
}
|
return;
|
}
|
// If there are multiple break points shrink the array
|
DCHECK(break_point_info->break_points()->IsFixedArray());
|
Handle<FixedArray> old_array = Handle<FixedArray>(
|
FixedArray::cast(break_point_info->break_points()), isolate);
|
Handle<FixedArray> new_array =
|
isolate->factory()->NewFixedArray(old_array->length() - 1);
|
int found_count = 0;
|
for (int i = 0; i < old_array->length(); i++) {
|
if (IsEqual(BreakPoint::cast(old_array->get(i)), *break_point)) {
|
DCHECK_EQ(found_count, 0);
|
found_count++;
|
} else {
|
new_array->set(i - found_count, old_array->get(i));
|
}
|
}
|
// If the break point was found in the list change it.
|
if (found_count > 0) break_point_info->set_break_points(*new_array);
|
}
|
|
// Add the specified break point object.
|
void BreakPointInfo::SetBreakPoint(Isolate* isolate,
|
Handle<BreakPointInfo> break_point_info,
|
Handle<BreakPoint> break_point) {
|
// If there was no break point objects before just set it.
|
if (break_point_info->break_points()->IsUndefined(isolate)) {
|
break_point_info->set_break_points(*break_point);
|
return;
|
}
|
// If the break point object is the same as before just ignore.
|
if (break_point_info->break_points() == *break_point) return;
|
// If there was one break point object before replace with array.
|
if (!break_point_info->break_points()->IsFixedArray()) {
|
Handle<FixedArray> array = isolate->factory()->NewFixedArray(2);
|
array->set(0, break_point_info->break_points());
|
array->set(1, *break_point);
|
break_point_info->set_break_points(*array);
|
return;
|
}
|
// If there was more than one break point before extend array.
|
Handle<FixedArray> old_array = Handle<FixedArray>(
|
FixedArray::cast(break_point_info->break_points()), isolate);
|
Handle<FixedArray> new_array =
|
isolate->factory()->NewFixedArray(old_array->length() + 1);
|
for (int i = 0; i < old_array->length(); i++) {
|
// If the break point was there before just ignore.
|
if (IsEqual(BreakPoint::cast(old_array->get(i)), *break_point)) return;
|
new_array->set(i, old_array->get(i));
|
}
|
// Add the new break point.
|
new_array->set(old_array->length(), *break_point);
|
break_point_info->set_break_points(*new_array);
|
}
|
|
bool BreakPointInfo::HasBreakPoint(Isolate* isolate,
|
Handle<BreakPointInfo> break_point_info,
|
Handle<BreakPoint> break_point) {
|
// No break point.
|
if (break_point_info->break_points()->IsUndefined(isolate)) {
|
return false;
|
}
|
// Single break point.
|
if (!break_point_info->break_points()->IsFixedArray()) {
|
return IsEqual(BreakPoint::cast(break_point_info->break_points()),
|
*break_point);
|
}
|
// Multiple break points.
|
FixedArray* array = FixedArray::cast(break_point_info->break_points());
|
for (int i = 0; i < array->length(); i++) {
|
if (IsEqual(BreakPoint::cast(array->get(i)), *break_point)) {
|
return true;
|
}
|
}
|
return false;
|
}
|
|
// Get the number of break points.
|
int BreakPointInfo::GetBreakPointCount(Isolate* isolate) {
|
// No break point.
|
if (break_points()->IsUndefined(isolate)) return 0;
|
// Single break point.
|
if (!break_points()->IsFixedArray()) return 1;
|
// Multiple break points.
|
return FixedArray::cast(break_points())->length();
|
}
|
|
int CoverageInfo::SlotCount() const {
|
DCHECK_EQ(kFirstSlotIndex, length() % kSlotIndexCount);
|
return (length() - kFirstSlotIndex) / kSlotIndexCount;
|
}
|
|
int CoverageInfo::StartSourcePosition(int slot_index) const {
|
DCHECK_LT(slot_index, SlotCount());
|
const int slot_start = CoverageInfo::FirstIndexForSlot(slot_index);
|
return Smi::ToInt(get(slot_start + kSlotStartSourcePositionIndex));
|
}
|
|
int CoverageInfo::EndSourcePosition(int slot_index) const {
|
DCHECK_LT(slot_index, SlotCount());
|
const int slot_start = CoverageInfo::FirstIndexForSlot(slot_index);
|
return Smi::ToInt(get(slot_start + kSlotEndSourcePositionIndex));
|
}
|
|
int CoverageInfo::BlockCount(int slot_index) const {
|
DCHECK_LT(slot_index, SlotCount());
|
const int slot_start = CoverageInfo::FirstIndexForSlot(slot_index);
|
return Smi::ToInt(get(slot_start + kSlotBlockCountIndex));
|
}
|
|
void CoverageInfo::InitializeSlot(int slot_index, int from_pos, int to_pos) {
|
DCHECK_LT(slot_index, SlotCount());
|
const int slot_start = CoverageInfo::FirstIndexForSlot(slot_index);
|
set(slot_start + kSlotStartSourcePositionIndex, Smi::FromInt(from_pos));
|
set(slot_start + kSlotEndSourcePositionIndex, Smi::FromInt(to_pos));
|
set(slot_start + kSlotBlockCountIndex, Smi::kZero);
|
}
|
|
void CoverageInfo::IncrementBlockCount(int slot_index) {
|
DCHECK_LT(slot_index, SlotCount());
|
const int slot_start = CoverageInfo::FirstIndexForSlot(slot_index);
|
const int old_count = BlockCount(slot_index);
|
set(slot_start + kSlotBlockCountIndex, Smi::FromInt(old_count + 1));
|
}
|
|
void CoverageInfo::ResetBlockCount(int slot_index) {
|
DCHECK_LT(slot_index, SlotCount());
|
const int slot_start = CoverageInfo::FirstIndexForSlot(slot_index);
|
set(slot_start + kSlotBlockCountIndex, Smi::kZero);
|
}
|
|
void CoverageInfo::Print(std::unique_ptr<char[]> function_name) {
|
DCHECK(FLAG_trace_block_coverage);
|
DisallowHeapAllocation no_gc;
|
|
StdoutStream os;
|
os << "Coverage info (";
|
if (strlen(function_name.get()) > 0) {
|
os << function_name.get();
|
} else {
|
os << "{anonymous}";
|
}
|
os << "):" << std::endl;
|
|
for (int i = 0; i < SlotCount(); i++) {
|
os << "{" << StartSourcePosition(i) << "," << EndSourcePosition(i) << "}"
|
<< std::endl;
|
}
|
}
|
|
} // namespace internal
|
} // namespace v8
|