1// Copyright 2017 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/objects/debug-objects.h"
6
7#include "src/base/platform/mutex.h"
8#include "src/debug/debug-evaluate.h"
9#include "src/handles/handles-inl.h"
10#include "src/objects/call-site-info-inl.h"
11#include "src/objects/debug-objects-inl.h"
12#include "src/utils/ostreams.h"
13
14namespace v8 {
15namespace internal {
16
17bool DebugInfo::IsEmpty() const {
18  return flags(kRelaxedLoad) == kNone && debugger_hints() == 0;
19}
20
21bool DebugInfo::HasBreakInfo() const {
22  return (flags(kRelaxedLoad) & kHasBreakInfo) != 0;
23}
24
25DebugInfo::ExecutionMode DebugInfo::DebugExecutionMode() const {
26  return (flags(kRelaxedLoad) & kDebugExecutionMode) != 0 ? kSideEffects
27                                                          : kBreakpoints;
28}
29
30void DebugInfo::SetDebugExecutionMode(ExecutionMode value) {
31  set_flags(value == kSideEffects
32                ? (flags(kRelaxedLoad) | kDebugExecutionMode)
33                : (flags(kRelaxedLoad) & ~kDebugExecutionMode),
34            kRelaxedStore);
35}
36
37void DebugInfo::ClearBreakInfo(Isolate* isolate) {
38  if (HasInstrumentedBytecodeArray()) {
39    // If the function is currently running on the stack, we need to update the
40    // bytecode pointers on the stack so they point to the original
41    // BytecodeArray before releasing that BytecodeArray from this DebugInfo.
42    // Otherwise, it could be flushed and cause problems on resume. See v8:9067.
43    {
44      RedirectActiveFunctions redirect_visitor(
45          shared(), RedirectActiveFunctions::Mode::kUseOriginalBytecode);
46      redirect_visitor.VisitThread(isolate, isolate->thread_local_top());
47      isolate->thread_manager()->IterateArchivedThreads(&redirect_visitor);
48    }
49
50    SharedFunctionInfo::UninstallDebugBytecode(shared(), isolate);
51  }
52  set_break_points(ReadOnlyRoots(isolate).empty_fixed_array());
53
54  int new_flags = flags(kRelaxedLoad);
55  new_flags &= ~kHasBreakInfo & ~kPreparedForDebugExecution;
56  new_flags &= ~kBreakAtEntry & ~kCanBreakAtEntry;
57  new_flags &= ~kDebugExecutionMode;
58  set_flags(new_flags, kRelaxedStore);
59}
60
61void DebugInfo::SetBreakAtEntry() {
62  DCHECK(CanBreakAtEntry());
63  set_flags(flags(kRelaxedLoad) | kBreakAtEntry, kRelaxedStore);
64}
65
66void DebugInfo::ClearBreakAtEntry() {
67  DCHECK(CanBreakAtEntry());
68  set_flags(flags(kRelaxedLoad) & ~kBreakAtEntry, kRelaxedStore);
69}
70
71bool DebugInfo::BreakAtEntry() const {
72  return (flags(kRelaxedLoad) & kBreakAtEntry) != 0;
73}
74
75bool DebugInfo::CanBreakAtEntry() const {
76  return (flags(kRelaxedLoad) & kCanBreakAtEntry) != 0;
77}
78
79// Check if there is a break point at this source position.
80bool DebugInfo::HasBreakPoint(Isolate* isolate, int source_position) {
81  DCHECK(HasBreakInfo());
82  // Get the break point info object for this code offset.
83  Object break_point_info = GetBreakPointInfo(isolate, source_position);
84
85  // If there is no break point info object or no break points in the break
86  // point info object there is no break point at this code offset.
87  if (break_point_info.IsUndefined(isolate)) return false;
88  return BreakPointInfo::cast(break_point_info).GetBreakPointCount(isolate) > 0;
89}
90
91// Get the break point info object for this source position.
92Object DebugInfo::GetBreakPointInfo(Isolate* isolate, int source_position) {
93  DCHECK(HasBreakInfo());
94  for (int i = 0; i < break_points().length(); i++) {
95    if (!break_points().get(i).IsUndefined(isolate)) {
96      BreakPointInfo break_point_info =
97          BreakPointInfo::cast(break_points().get(i));
98      if (break_point_info.source_position() == source_position) {
99        return break_point_info;
100      }
101    }
102  }
103  return ReadOnlyRoots(isolate).undefined_value();
104}
105
106bool DebugInfo::ClearBreakPoint(Isolate* isolate, Handle<DebugInfo> debug_info,
107                                Handle<BreakPoint> break_point) {
108  DCHECK(debug_info->HasBreakInfo());
109  for (int i = 0; i < debug_info->break_points().length(); i++) {
110    if (debug_info->break_points().get(i).IsUndefined(isolate)) continue;
111    Handle<BreakPointInfo> break_point_info = Handle<BreakPointInfo>(
112        BreakPointInfo::cast(debug_info->break_points().get(i)), isolate);
113    if (BreakPointInfo::HasBreakPoint(isolate, break_point_info, break_point)) {
114      BreakPointInfo::ClearBreakPoint(isolate, break_point_info, break_point);
115      return true;
116    }
117  }
118  return false;
119}
120
121void DebugInfo::SetBreakPoint(Isolate* isolate, Handle<DebugInfo> debug_info,
122                              int source_position,
123                              Handle<BreakPoint> break_point) {
124  DCHECK(debug_info->HasBreakInfo());
125  Handle<Object> break_point_info(
126      debug_info->GetBreakPointInfo(isolate, source_position), isolate);
127  if (!break_point_info->IsUndefined(isolate)) {
128    BreakPointInfo::SetBreakPoint(
129        isolate, Handle<BreakPointInfo>::cast(break_point_info), break_point);
130    return;
131  }
132
133  // Adding a new break point for a code offset which did not have any
134  // break points before. Try to find a free slot.
135  static const int kNoBreakPointInfo = -1;
136  int index = kNoBreakPointInfo;
137  for (int i = 0; i < debug_info->break_points().length(); i++) {
138    if (debug_info->break_points().get(i).IsUndefined(isolate)) {
139      index = i;
140      break;
141    }
142  }
143  if (index == kNoBreakPointInfo) {
144    // No free slot - extend break point info array.
145    Handle<FixedArray> old_break_points =
146        Handle<FixedArray>(debug_info->break_points(), isolate);
147    Handle<FixedArray> new_break_points = isolate->factory()->NewFixedArray(
148        old_break_points->length() +
149        DebugInfo::kEstimatedNofBreakPointsInFunction);
150
151    debug_info->set_break_points(*new_break_points);
152    for (int i = 0; i < old_break_points->length(); i++) {
153      new_break_points->set(i, old_break_points->get(i));
154    }
155    index = old_break_points->length();
156  }
157  DCHECK_NE(index, kNoBreakPointInfo);
158
159  // Allocate new BreakPointInfo object and set the break point.
160  Handle<BreakPointInfo> new_break_point_info =
161      isolate->factory()->NewBreakPointInfo(source_position);
162  BreakPointInfo::SetBreakPoint(isolate, new_break_point_info, break_point);
163  debug_info->break_points().set(index, *new_break_point_info);
164}
165
166// Get the break point objects for a source position.
167Handle<Object> DebugInfo::GetBreakPoints(Isolate* isolate,
168                                         int source_position) {
169  DCHECK(HasBreakInfo());
170  Object break_point_info = GetBreakPointInfo(isolate, source_position);
171  if (break_point_info.IsUndefined(isolate)) {
172    return isolate->factory()->undefined_value();
173  }
174  return Handle<Object>(BreakPointInfo::cast(break_point_info).break_points(),
175                        isolate);
176}
177
178// Get the total number of break points.
179int DebugInfo::GetBreakPointCount(Isolate* isolate) {
180  DCHECK(HasBreakInfo());
181  int count = 0;
182  for (int i = 0; i < break_points().length(); i++) {
183    if (!break_points().get(i).IsUndefined(isolate)) {
184      BreakPointInfo break_point_info =
185          BreakPointInfo::cast(break_points().get(i));
186      count += break_point_info.GetBreakPointCount(isolate);
187    }
188  }
189  return count;
190}
191
192Handle<Object> DebugInfo::FindBreakPointInfo(Isolate* isolate,
193                                             Handle<DebugInfo> debug_info,
194                                             Handle<BreakPoint> break_point) {
195  DCHECK(debug_info->HasBreakInfo());
196  for (int i = 0; i < debug_info->break_points().length(); i++) {
197    if (!debug_info->break_points().get(i).IsUndefined(isolate)) {
198      Handle<BreakPointInfo> break_point_info = Handle<BreakPointInfo>(
199          BreakPointInfo::cast(debug_info->break_points().get(i)), isolate);
200      if (BreakPointInfo::HasBreakPoint(isolate, break_point_info,
201                                        break_point)) {
202        return break_point_info;
203      }
204    }
205  }
206  return isolate->factory()->undefined_value();
207}
208
209bool DebugInfo::HasCoverageInfo() const {
210  return (flags(kRelaxedLoad) & kHasCoverageInfo) != 0;
211}
212
213void DebugInfo::ClearCoverageInfo(Isolate* isolate) {
214  if (HasCoverageInfo()) {
215    set_coverage_info(ReadOnlyRoots(isolate).undefined_value());
216
217    int new_flags = flags(kRelaxedLoad) & ~kHasCoverageInfo;
218    set_flags(new_flags, kRelaxedStore);
219  }
220}
221
222DebugInfo::SideEffectState DebugInfo::GetSideEffectState(Isolate* isolate) {
223  if (side_effect_state() == kNotComputed) {
224    SideEffectState has_no_side_effect =
225        DebugEvaluate::FunctionGetSideEffectState(isolate,
226                                                  handle(shared(), isolate));
227    set_side_effect_state(has_no_side_effect);
228  }
229  return static_cast<SideEffectState>(side_effect_state());
230}
231
232namespace {
233bool IsEqual(BreakPoint break_point1, BreakPoint break_point2) {
234  return break_point1.id() == break_point2.id();
235}
236}  // namespace
237
238// Remove the specified break point object.
239void BreakPointInfo::ClearBreakPoint(Isolate* isolate,
240                                     Handle<BreakPointInfo> break_point_info,
241                                     Handle<BreakPoint> break_point) {
242  // If there are no break points just ignore.
243  if (break_point_info->break_points().IsUndefined(isolate)) return;
244  // If there is a single break point clear it if it is the same.
245  if (!break_point_info->break_points().IsFixedArray()) {
246    if (IsEqual(BreakPoint::cast(break_point_info->break_points()),
247                *break_point)) {
248      break_point_info->set_break_points(
249          ReadOnlyRoots(isolate).undefined_value());
250    }
251    return;
252  }
253  // If there are multiple break points shrink the array
254  DCHECK(break_point_info->break_points().IsFixedArray());
255  Handle<FixedArray> old_array = Handle<FixedArray>(
256      FixedArray::cast(break_point_info->break_points()), isolate);
257  Handle<FixedArray> new_array =
258      isolate->factory()->NewFixedArray(old_array->length() - 1);
259  int found_count = 0;
260  for (int i = 0; i < old_array->length(); i++) {
261    if (IsEqual(BreakPoint::cast(old_array->get(i)), *break_point)) {
262      DCHECK_EQ(found_count, 0);
263      found_count++;
264    } else {
265      new_array->set(i - found_count, old_array->get(i));
266    }
267  }
268  // If the break point was found in the list change it.
269  if (found_count > 0) break_point_info->set_break_points(*new_array);
270}
271
272// Add the specified break point object.
273void BreakPointInfo::SetBreakPoint(Isolate* isolate,
274                                   Handle<BreakPointInfo> break_point_info,
275                                   Handle<BreakPoint> break_point) {
276  // If there was no break point objects before just set it.
277  if (break_point_info->break_points().IsUndefined(isolate)) {
278    break_point_info->set_break_points(*break_point);
279    return;
280  }
281  // If there was one break point object before replace with array.
282  if (!break_point_info->break_points().IsFixedArray()) {
283    if (IsEqual(BreakPoint::cast(break_point_info->break_points()),
284        *break_point)) {
285          return;
286    }
287
288    Handle<FixedArray> array = isolate->factory()->NewFixedArray(2);
289    array->set(0, break_point_info->break_points());
290    array->set(1, *break_point);
291    break_point_info->set_break_points(*array);
292    return;
293  }
294  // If there was more than one break point before extend array.
295  Handle<FixedArray> old_array = Handle<FixedArray>(
296      FixedArray::cast(break_point_info->break_points()), isolate);
297  Handle<FixedArray> new_array =
298      isolate->factory()->NewFixedArray(old_array->length() + 1);
299  for (int i = 0; i < old_array->length(); i++) {
300    // If the break point was there before just ignore.
301    if (IsEqual(BreakPoint::cast(old_array->get(i)), *break_point)) return;
302    new_array->set(i, old_array->get(i));
303  }
304  // Add the new break point.
305  new_array->set(old_array->length(), *break_point);
306  break_point_info->set_break_points(*new_array);
307}
308
309bool BreakPointInfo::HasBreakPoint(Isolate* isolate,
310                                   Handle<BreakPointInfo> break_point_info,
311                                   Handle<BreakPoint> break_point) {
312  // No break point.
313  if (break_point_info->break_points().IsUndefined(isolate)) {
314    return false;
315  }
316  // Single break point.
317  if (!break_point_info->break_points().IsFixedArray()) {
318    return IsEqual(BreakPoint::cast(break_point_info->break_points()),
319                   *break_point);
320  }
321  // Multiple break points.
322  FixedArray array = FixedArray::cast(break_point_info->break_points());
323  for (int i = 0; i < array.length(); i++) {
324    if (IsEqual(BreakPoint::cast(array.get(i)), *break_point)) {
325      return true;
326    }
327  }
328  return false;
329}
330
331MaybeHandle<BreakPoint> BreakPointInfo::GetBreakPointById(
332    Isolate* isolate, Handle<BreakPointInfo> break_point_info,
333    int breakpoint_id) {
334  // No break point.
335  if (break_point_info->break_points().IsUndefined(isolate)) {
336    return MaybeHandle<BreakPoint>();
337  }
338  // Single break point.
339  if (!break_point_info->break_points().IsFixedArray()) {
340    BreakPoint breakpoint = BreakPoint::cast(break_point_info->break_points());
341    if (breakpoint.id() == breakpoint_id) {
342      return handle(breakpoint, isolate);
343    }
344  } else {
345    // Multiple break points.
346    FixedArray array = FixedArray::cast(break_point_info->break_points());
347    for (int i = 0; i < array.length(); i++) {
348      BreakPoint breakpoint = BreakPoint::cast(array.get(i));
349      if (breakpoint.id() == breakpoint_id) {
350        return handle(breakpoint, isolate);
351      }
352    }
353  }
354  return MaybeHandle<BreakPoint>();
355}
356
357// Get the number of break points.
358int BreakPointInfo::GetBreakPointCount(Isolate* isolate) {
359  // No break point.
360  if (break_points().IsUndefined(isolate)) return 0;
361  // Single break point.
362  if (!break_points().IsFixedArray()) return 1;
363  // Multiple break points.
364  return FixedArray::cast(break_points()).length();
365}
366
367void CoverageInfo::InitializeSlot(int slot_index, int from_pos, int to_pos) {
368  set_slots_start_source_position(slot_index, from_pos);
369  set_slots_end_source_position(slot_index, to_pos);
370  ResetBlockCount(slot_index);
371  set_slots_padding(slot_index, 0);
372}
373
374void CoverageInfo::ResetBlockCount(int slot_index) {
375  set_slots_block_count(slot_index, 0);
376}
377
378void CoverageInfo::CoverageInfoPrint(std::ostream& os,
379                                     std::unique_ptr<char[]> function_name) {
380  DCHECK(FLAG_trace_block_coverage);
381  DisallowGarbageCollection no_gc;
382
383  os << "Coverage info (";
384  if (function_name == nullptr) {
385    os << "{unknown}";
386  } else if (strlen(function_name.get()) > 0) {
387    os << function_name.get();
388  } else {
389    os << "{anonymous}";
390  }
391  os << "):" << std::endl;
392
393  for (int i = 0; i < slot_count(); i++) {
394    os << "{" << slots_start_source_position(i) << ","
395       << slots_end_source_position(i) << "}" << std::endl;
396  }
397}
398
399// static
400int StackFrameInfo::GetSourcePosition(Handle<StackFrameInfo> info) {
401  if (info->shared_or_script().IsScript()) {
402    return info->bytecode_offset_or_source_position();
403  }
404  Isolate* isolate = info->GetIsolate();
405  Handle<SharedFunctionInfo> shared(
406      SharedFunctionInfo::cast(info->shared_or_script()), isolate);
407  SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate, shared);
408  int source_position = shared->abstract_code(isolate).SourcePosition(
409      info->bytecode_offset_or_source_position());
410  info->set_shared_or_script(shared->script());
411  info->set_bytecode_offset_or_source_position(source_position);
412  return source_position;
413}
414
415// static
416void ErrorStackData::EnsureStackFrameInfos(Isolate* isolate,
417                                           Handle<ErrorStackData> error_stack) {
418  if (!error_stack->limit_or_stack_frame_infos().IsSmi()) {
419    return;
420  }
421  int limit = Smi::cast(error_stack->limit_or_stack_frame_infos()).value();
422  Handle<FixedArray> call_site_infos(error_stack->call_site_infos(), isolate);
423  Handle<FixedArray> stack_frame_infos =
424      isolate->factory()->NewFixedArray(call_site_infos->length());
425  int index = 0;
426  for (int i = 0; i < call_site_infos->length(); ++i) {
427    Handle<CallSiteInfo> call_site_info(
428        CallSiteInfo::cast(call_site_infos->get(i)), isolate);
429    if (call_site_info->IsAsync()) {
430      break;
431    }
432    Handle<Script> script;
433    if (!CallSiteInfo::GetScript(isolate, call_site_info).ToHandle(&script) ||
434        !script->IsSubjectToDebugging()) {
435      continue;
436    }
437    Handle<StackFrameInfo> stack_frame_info =
438        isolate->factory()->NewStackFrameInfo(
439            script, CallSiteInfo::GetSourcePosition(call_site_info),
440            CallSiteInfo::GetFunctionDebugName(call_site_info),
441            call_site_info->IsConstructor());
442    stack_frame_infos->set(index++, *stack_frame_info);
443  }
444  stack_frame_infos =
445      FixedArray::ShrinkOrEmpty(isolate, stack_frame_infos, index);
446  if (limit < 0 && -limit < index) {
447    // Negative limit encodes cap to be applied to |stack_frame_infos|.
448    stack_frame_infos =
449        FixedArray::ShrinkOrEmpty(isolate, stack_frame_infos, -limit);
450  } else if (limit >= 0 && limit < call_site_infos->length()) {
451    // Positive limit means we need to cap the |call_site_infos|
452    // to that number before exposing them to the world.
453    call_site_infos =
454        FixedArray::ShrinkOrEmpty(isolate, call_site_infos, limit);
455    error_stack->set_call_site_infos(*call_site_infos);
456  }
457  error_stack->set_limit_or_stack_frame_infos(*stack_frame_infos);
458}
459
460// static
461MaybeHandle<JSObject> PromiseOnStack::GetPromise(
462    Handle<PromiseOnStack> promise_on_stack) {
463  HeapObject promise;
464  Isolate* isolate = promise_on_stack->GetIsolate();
465  if (promise_on_stack->promise()->GetHeapObjectIfWeak(isolate, &promise)) {
466    return handle(JSObject::cast(promise), isolate);
467  }
468  return {};
469}
470
471}  // namespace internal
472}  // namespace v8
473