11cb0ef41Sopenharmony_ci// Copyright 2016 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci#include "src/codegen/source-position-table.h"
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ci#include "src/base/export-template.h"
81cb0ef41Sopenharmony_ci#include "src/base/logging.h"
91cb0ef41Sopenharmony_ci#include "src/common/assert-scope.h"
101cb0ef41Sopenharmony_ci#include "src/heap/local-factory-inl.h"
111cb0ef41Sopenharmony_ci#include "src/objects/objects-inl.h"
121cb0ef41Sopenharmony_ci#include "src/objects/objects.h"
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_cinamespace v8 {
151cb0ef41Sopenharmony_cinamespace internal {
161cb0ef41Sopenharmony_ci
171cb0ef41Sopenharmony_ci// We'll use a simple encoding scheme to record the source positions.
181cb0ef41Sopenharmony_ci// Conceptually, each position consists of:
191cb0ef41Sopenharmony_ci// - code_offset: An integer index into the BytecodeArray or code.
201cb0ef41Sopenharmony_ci// - source_position: An integer index into the source string.
211cb0ef41Sopenharmony_ci// - position type: Each position is either a statement or an expression.
221cb0ef41Sopenharmony_ci//
231cb0ef41Sopenharmony_ci// The basic idea for the encoding is to use a variable-length integer coding,
241cb0ef41Sopenharmony_ci// where each byte contains 7 bits of payload data, and 1 'more' bit that
251cb0ef41Sopenharmony_ci// determines whether additional bytes follow. Additionally:
261cb0ef41Sopenharmony_ci// - we record the difference from the previous position,
271cb0ef41Sopenharmony_ci// - we just stuff one bit for the type into the code offset,
281cb0ef41Sopenharmony_ci// - we write least-significant bits first,
291cb0ef41Sopenharmony_ci// - we use zig-zag encoding to encode both positive and negative numbers.
301cb0ef41Sopenharmony_ci
311cb0ef41Sopenharmony_cinamespace {
321cb0ef41Sopenharmony_ci
331cb0ef41Sopenharmony_ci// Each byte is encoded as MoreBit | ValueBits.
341cb0ef41Sopenharmony_ciusing MoreBit = base::BitField8<bool, 7, 1>;
351cb0ef41Sopenharmony_ciusing ValueBits = base::BitField8<unsigned, 0, 7>;
361cb0ef41Sopenharmony_ci
371cb0ef41Sopenharmony_ci// Helper: Add the offsets from 'other' to 'value'. Also set is_statement.
381cb0ef41Sopenharmony_civoid AddAndSetEntry(PositionTableEntry* value,
391cb0ef41Sopenharmony_ci                    const PositionTableEntry& other) {
401cb0ef41Sopenharmony_ci  value->code_offset += other.code_offset;
411cb0ef41Sopenharmony_ci  DCHECK_IMPLIES(value->code_offset != kFunctionEntryBytecodeOffset,
421cb0ef41Sopenharmony_ci                 value->code_offset >= 0);
431cb0ef41Sopenharmony_ci  value->source_position += other.source_position;
441cb0ef41Sopenharmony_ci  DCHECK_LE(0, value->source_position);
451cb0ef41Sopenharmony_ci  value->is_statement = other.is_statement;
461cb0ef41Sopenharmony_ci}
471cb0ef41Sopenharmony_ci
481cb0ef41Sopenharmony_ci// Helper: Subtract the offsets from 'other' from 'value'.
491cb0ef41Sopenharmony_civoid SubtractFromEntry(PositionTableEntry* value,
501cb0ef41Sopenharmony_ci                       const PositionTableEntry& other) {
511cb0ef41Sopenharmony_ci  value->code_offset -= other.code_offset;
521cb0ef41Sopenharmony_ci  value->source_position -= other.source_position;
531cb0ef41Sopenharmony_ci}
541cb0ef41Sopenharmony_ci
551cb0ef41Sopenharmony_ci// Helper: Encode an integer.
561cb0ef41Sopenharmony_citemplate <typename T>
571cb0ef41Sopenharmony_civoid EncodeInt(ZoneVector<byte>* bytes, T value) {
581cb0ef41Sopenharmony_ci  using unsigned_type = typename std::make_unsigned<T>::type;
591cb0ef41Sopenharmony_ci  // Zig-zag encoding.
601cb0ef41Sopenharmony_ci  static constexpr int kShift = sizeof(T) * kBitsPerByte - 1;
611cb0ef41Sopenharmony_ci  value = ((static_cast<unsigned_type>(value) << 1) ^ (value >> kShift));
621cb0ef41Sopenharmony_ci  DCHECK_GE(value, 0);
631cb0ef41Sopenharmony_ci  unsigned_type encoded = static_cast<unsigned_type>(value);
641cb0ef41Sopenharmony_ci  bool more;
651cb0ef41Sopenharmony_ci  do {
661cb0ef41Sopenharmony_ci    more = encoded > ValueBits::kMax;
671cb0ef41Sopenharmony_ci    byte current =
681cb0ef41Sopenharmony_ci        MoreBit::encode(more) | ValueBits::encode(encoded & ValueBits::kMask);
691cb0ef41Sopenharmony_ci    bytes->push_back(current);
701cb0ef41Sopenharmony_ci    encoded >>= ValueBits::kSize;
711cb0ef41Sopenharmony_ci  } while (more);
721cb0ef41Sopenharmony_ci}
731cb0ef41Sopenharmony_ci
741cb0ef41Sopenharmony_ci// Encode a PositionTableEntry.
751cb0ef41Sopenharmony_civoid EncodeEntry(ZoneVector<byte>* bytes, const PositionTableEntry& entry) {
761cb0ef41Sopenharmony_ci  // We only accept ascending code offsets.
771cb0ef41Sopenharmony_ci  DCHECK_LE(0, entry.code_offset);
781cb0ef41Sopenharmony_ci  // All but the first entry must be *strictly* ascending (no two entries for
791cb0ef41Sopenharmony_ci  // the same position).
801cb0ef41Sopenharmony_ci  // TODO(11496): This DCHECK fails tests.
811cb0ef41Sopenharmony_ci  // DCHECK_IMPLIES(!bytes->empty(), entry.code_offset > 0);
821cb0ef41Sopenharmony_ci  // Since code_offset is not negative, we use sign to encode is_statement.
831cb0ef41Sopenharmony_ci  EncodeInt(bytes,
841cb0ef41Sopenharmony_ci            entry.is_statement ? entry.code_offset : -entry.code_offset - 1);
851cb0ef41Sopenharmony_ci  EncodeInt(bytes, entry.source_position);
861cb0ef41Sopenharmony_ci}
871cb0ef41Sopenharmony_ci
881cb0ef41Sopenharmony_ci// Helper: Decode an integer.
891cb0ef41Sopenharmony_citemplate <typename T>
901cb0ef41Sopenharmony_ciT DecodeInt(base::Vector<const byte> bytes, int* index) {
911cb0ef41Sopenharmony_ci  byte current;
921cb0ef41Sopenharmony_ci  int shift = 0;
931cb0ef41Sopenharmony_ci  T decoded = 0;
941cb0ef41Sopenharmony_ci  bool more;
951cb0ef41Sopenharmony_ci  do {
961cb0ef41Sopenharmony_ci    current = bytes[(*index)++];
971cb0ef41Sopenharmony_ci    decoded |= static_cast<typename std::make_unsigned<T>::type>(
981cb0ef41Sopenharmony_ci                   ValueBits::decode(current))
991cb0ef41Sopenharmony_ci               << shift;
1001cb0ef41Sopenharmony_ci    more = MoreBit::decode(current);
1011cb0ef41Sopenharmony_ci    shift += ValueBits::kSize;
1021cb0ef41Sopenharmony_ci  } while (more);
1031cb0ef41Sopenharmony_ci  DCHECK_GE(decoded, 0);
1041cb0ef41Sopenharmony_ci  decoded = (decoded >> 1) ^ (-(decoded & 1));
1051cb0ef41Sopenharmony_ci  return decoded;
1061cb0ef41Sopenharmony_ci}
1071cb0ef41Sopenharmony_ci
1081cb0ef41Sopenharmony_civoid DecodeEntry(base::Vector<const byte> bytes, int* index,
1091cb0ef41Sopenharmony_ci                 PositionTableEntry* entry) {
1101cb0ef41Sopenharmony_ci  int tmp = DecodeInt<int>(bytes, index);
1111cb0ef41Sopenharmony_ci  if (tmp >= 0) {
1121cb0ef41Sopenharmony_ci    entry->is_statement = true;
1131cb0ef41Sopenharmony_ci    entry->code_offset = tmp;
1141cb0ef41Sopenharmony_ci  } else {
1151cb0ef41Sopenharmony_ci    entry->is_statement = false;
1161cb0ef41Sopenharmony_ci    entry->code_offset = -(tmp + 1);
1171cb0ef41Sopenharmony_ci  }
1181cb0ef41Sopenharmony_ci  entry->source_position = DecodeInt<int64_t>(bytes, index);
1191cb0ef41Sopenharmony_ci}
1201cb0ef41Sopenharmony_ci
1211cb0ef41Sopenharmony_cibase::Vector<const byte> VectorFromByteArray(ByteArray byte_array) {
1221cb0ef41Sopenharmony_ci  return base::Vector<const byte>(byte_array.GetDataStartAddress(),
1231cb0ef41Sopenharmony_ci                                  byte_array.length());
1241cb0ef41Sopenharmony_ci}
1251cb0ef41Sopenharmony_ci
1261cb0ef41Sopenharmony_ci#ifdef ENABLE_SLOW_DCHECKS
1271cb0ef41Sopenharmony_civoid CheckTableEquals(const ZoneVector<PositionTableEntry>& raw_entries,
1281cb0ef41Sopenharmony_ci                      SourcePositionTableIterator* encoded) {
1291cb0ef41Sopenharmony_ci  // Brute force testing: Record all positions and decode
1301cb0ef41Sopenharmony_ci  // the entire table to verify they are identical.
1311cb0ef41Sopenharmony_ci  auto raw = raw_entries.begin();
1321cb0ef41Sopenharmony_ci  for (; !encoded->done(); encoded->Advance(), raw++) {
1331cb0ef41Sopenharmony_ci    DCHECK(raw != raw_entries.end());
1341cb0ef41Sopenharmony_ci    DCHECK_EQ(encoded->code_offset(), raw->code_offset);
1351cb0ef41Sopenharmony_ci    DCHECK_EQ(encoded->source_position().raw(), raw->source_position);
1361cb0ef41Sopenharmony_ci    DCHECK_EQ(encoded->is_statement(), raw->is_statement);
1371cb0ef41Sopenharmony_ci  }
1381cb0ef41Sopenharmony_ci  DCHECK(raw == raw_entries.end());
1391cb0ef41Sopenharmony_ci}
1401cb0ef41Sopenharmony_ci#endif
1411cb0ef41Sopenharmony_ci
1421cb0ef41Sopenharmony_ci}  // namespace
1431cb0ef41Sopenharmony_ci
1441cb0ef41Sopenharmony_ciSourcePositionTableBuilder::SourcePositionTableBuilder(
1451cb0ef41Sopenharmony_ci    Zone* zone, SourcePositionTableBuilder::RecordingMode mode)
1461cb0ef41Sopenharmony_ci    : mode_(mode),
1471cb0ef41Sopenharmony_ci      bytes_(zone),
1481cb0ef41Sopenharmony_ci#ifdef ENABLE_SLOW_DCHECKS
1491cb0ef41Sopenharmony_ci      raw_entries_(zone),
1501cb0ef41Sopenharmony_ci#endif
1511cb0ef41Sopenharmony_ci      previous_() {
1521cb0ef41Sopenharmony_ci}
1531cb0ef41Sopenharmony_ci
1541cb0ef41Sopenharmony_civoid SourcePositionTableBuilder::AddPosition(size_t code_offset,
1551cb0ef41Sopenharmony_ci                                             SourcePosition source_position,
1561cb0ef41Sopenharmony_ci                                             bool is_statement) {
1571cb0ef41Sopenharmony_ci  if (Omit()) return;
1581cb0ef41Sopenharmony_ci  DCHECK(source_position.IsKnown());
1591cb0ef41Sopenharmony_ci  int offset = static_cast<int>(code_offset);
1601cb0ef41Sopenharmony_ci  AddEntry({offset, source_position.raw(), is_statement});
1611cb0ef41Sopenharmony_ci}
1621cb0ef41Sopenharmony_ci
1631cb0ef41Sopenharmony_civoid SourcePositionTableBuilder::AddEntry(const PositionTableEntry& entry) {
1641cb0ef41Sopenharmony_ci  PositionTableEntry tmp(entry);
1651cb0ef41Sopenharmony_ci  SubtractFromEntry(&tmp, previous_);
1661cb0ef41Sopenharmony_ci  EncodeEntry(&bytes_, tmp);
1671cb0ef41Sopenharmony_ci  previous_ = entry;
1681cb0ef41Sopenharmony_ci#ifdef ENABLE_SLOW_DCHECKS
1691cb0ef41Sopenharmony_ci  raw_entries_.push_back(entry);
1701cb0ef41Sopenharmony_ci#endif
1711cb0ef41Sopenharmony_ci}
1721cb0ef41Sopenharmony_ci
1731cb0ef41Sopenharmony_citemplate <typename IsolateT>
1741cb0ef41Sopenharmony_ciHandle<ByteArray> SourcePositionTableBuilder::ToSourcePositionTable(
1751cb0ef41Sopenharmony_ci    IsolateT* isolate) {
1761cb0ef41Sopenharmony_ci  if (bytes_.empty()) return isolate->factory()->empty_byte_array();
1771cb0ef41Sopenharmony_ci  DCHECK(!Omit());
1781cb0ef41Sopenharmony_ci
1791cb0ef41Sopenharmony_ci  Handle<ByteArray> table = isolate->factory()->NewByteArray(
1801cb0ef41Sopenharmony_ci      static_cast<int>(bytes_.size()), AllocationType::kOld);
1811cb0ef41Sopenharmony_ci  MemCopy(table->GetDataStartAddress(), bytes_.data(), bytes_.size());
1821cb0ef41Sopenharmony_ci
1831cb0ef41Sopenharmony_ci#ifdef ENABLE_SLOW_DCHECKS
1841cb0ef41Sopenharmony_ci  // Brute force testing: Record all positions and decode
1851cb0ef41Sopenharmony_ci  // the entire table to verify they are identical.
1861cb0ef41Sopenharmony_ci  SourcePositionTableIterator it(
1871cb0ef41Sopenharmony_ci      *table, SourcePositionTableIterator::kAll,
1881cb0ef41Sopenharmony_ci      SourcePositionTableIterator::kDontSkipFunctionEntry);
1891cb0ef41Sopenharmony_ci  CheckTableEquals(raw_entries_, &it);
1901cb0ef41Sopenharmony_ci  // No additional source positions after creating the table.
1911cb0ef41Sopenharmony_ci  mode_ = OMIT_SOURCE_POSITIONS;
1921cb0ef41Sopenharmony_ci#endif
1931cb0ef41Sopenharmony_ci  return table;
1941cb0ef41Sopenharmony_ci}
1951cb0ef41Sopenharmony_ci
1961cb0ef41Sopenharmony_citemplate EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
1971cb0ef41Sopenharmony_ci    Handle<ByteArray> SourcePositionTableBuilder::ToSourcePositionTable(
1981cb0ef41Sopenharmony_ci        Isolate* isolate);
1991cb0ef41Sopenharmony_citemplate EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE)
2001cb0ef41Sopenharmony_ci    Handle<ByteArray> SourcePositionTableBuilder::ToSourcePositionTable(
2011cb0ef41Sopenharmony_ci        LocalIsolate* isolate);
2021cb0ef41Sopenharmony_ci
2031cb0ef41Sopenharmony_cibase::OwnedVector<byte>
2041cb0ef41Sopenharmony_ciSourcePositionTableBuilder::ToSourcePositionTableVector() {
2051cb0ef41Sopenharmony_ci  if (bytes_.empty()) return base::OwnedVector<byte>();
2061cb0ef41Sopenharmony_ci  DCHECK(!Omit());
2071cb0ef41Sopenharmony_ci
2081cb0ef41Sopenharmony_ci  base::OwnedVector<byte> table = base::OwnedVector<byte>::Of(bytes_);
2091cb0ef41Sopenharmony_ci
2101cb0ef41Sopenharmony_ci#ifdef ENABLE_SLOW_DCHECKS
2111cb0ef41Sopenharmony_ci  // Brute force testing: Record all positions and decode
2121cb0ef41Sopenharmony_ci  // the entire table to verify they are identical.
2131cb0ef41Sopenharmony_ci  SourcePositionTableIterator it(
2141cb0ef41Sopenharmony_ci      table.as_vector(), SourcePositionTableIterator::kAll,
2151cb0ef41Sopenharmony_ci      SourcePositionTableIterator::kDontSkipFunctionEntry);
2161cb0ef41Sopenharmony_ci  CheckTableEquals(raw_entries_, &it);
2171cb0ef41Sopenharmony_ci  // No additional source positions after creating the table.
2181cb0ef41Sopenharmony_ci  mode_ = OMIT_SOURCE_POSITIONS;
2191cb0ef41Sopenharmony_ci#endif
2201cb0ef41Sopenharmony_ci  return table;
2211cb0ef41Sopenharmony_ci}
2221cb0ef41Sopenharmony_ci
2231cb0ef41Sopenharmony_civoid SourcePositionTableIterator::Initialize() {
2241cb0ef41Sopenharmony_ci  Advance();
2251cb0ef41Sopenharmony_ci  if (function_entry_filter_ == kSkipFunctionEntry &&
2261cb0ef41Sopenharmony_ci      current_.code_offset == kFunctionEntryBytecodeOffset && !done()) {
2271cb0ef41Sopenharmony_ci    Advance();
2281cb0ef41Sopenharmony_ci  }
2291cb0ef41Sopenharmony_ci}
2301cb0ef41Sopenharmony_ci
2311cb0ef41Sopenharmony_ciSourcePositionTableIterator::SourcePositionTableIterator(
2321cb0ef41Sopenharmony_ci    ByteArray byte_array, IterationFilter iteration_filter,
2331cb0ef41Sopenharmony_ci    FunctionEntryFilter function_entry_filter)
2341cb0ef41Sopenharmony_ci    : raw_table_(VectorFromByteArray(byte_array)),
2351cb0ef41Sopenharmony_ci      iteration_filter_(iteration_filter),
2361cb0ef41Sopenharmony_ci      function_entry_filter_(function_entry_filter) {
2371cb0ef41Sopenharmony_ci  Initialize();
2381cb0ef41Sopenharmony_ci}
2391cb0ef41Sopenharmony_ci
2401cb0ef41Sopenharmony_ciSourcePositionTableIterator::SourcePositionTableIterator(
2411cb0ef41Sopenharmony_ci    Handle<ByteArray> byte_array, IterationFilter iteration_filter,
2421cb0ef41Sopenharmony_ci    FunctionEntryFilter function_entry_filter)
2431cb0ef41Sopenharmony_ci    : table_(byte_array),
2441cb0ef41Sopenharmony_ci      iteration_filter_(iteration_filter),
2451cb0ef41Sopenharmony_ci      function_entry_filter_(function_entry_filter) {
2461cb0ef41Sopenharmony_ci  Initialize();
2471cb0ef41Sopenharmony_ci#ifdef DEBUG
2481cb0ef41Sopenharmony_ci  // We can enable allocation because we keep the table in a handle.
2491cb0ef41Sopenharmony_ci  no_gc.Release();
2501cb0ef41Sopenharmony_ci#endif  // DEBUG
2511cb0ef41Sopenharmony_ci}
2521cb0ef41Sopenharmony_ci
2531cb0ef41Sopenharmony_ciSourcePositionTableIterator::SourcePositionTableIterator(
2541cb0ef41Sopenharmony_ci    base::Vector<const byte> bytes, IterationFilter iteration_filter,
2551cb0ef41Sopenharmony_ci    FunctionEntryFilter function_entry_filter)
2561cb0ef41Sopenharmony_ci    : raw_table_(bytes),
2571cb0ef41Sopenharmony_ci      iteration_filter_(iteration_filter),
2581cb0ef41Sopenharmony_ci      function_entry_filter_(function_entry_filter) {
2591cb0ef41Sopenharmony_ci  Initialize();
2601cb0ef41Sopenharmony_ci#ifdef DEBUG
2611cb0ef41Sopenharmony_ci  // We can enable allocation because the underlying vector does not move.
2621cb0ef41Sopenharmony_ci  no_gc.Release();
2631cb0ef41Sopenharmony_ci#endif  // DEBUG
2641cb0ef41Sopenharmony_ci}
2651cb0ef41Sopenharmony_ci
2661cb0ef41Sopenharmony_civoid SourcePositionTableIterator::Advance() {
2671cb0ef41Sopenharmony_ci  base::Vector<const byte> bytes =
2681cb0ef41Sopenharmony_ci      table_.is_null() ? raw_table_ : VectorFromByteArray(*table_);
2691cb0ef41Sopenharmony_ci  DCHECK(!done());
2701cb0ef41Sopenharmony_ci  DCHECK(index_ >= 0 && index_ <= bytes.length());
2711cb0ef41Sopenharmony_ci  bool filter_satisfied = false;
2721cb0ef41Sopenharmony_ci  while (!done() && !filter_satisfied) {
2731cb0ef41Sopenharmony_ci    if (index_ >= bytes.length()) {
2741cb0ef41Sopenharmony_ci      index_ = kDone;
2751cb0ef41Sopenharmony_ci    } else {
2761cb0ef41Sopenharmony_ci      PositionTableEntry tmp;
2771cb0ef41Sopenharmony_ci      DecodeEntry(bytes, &index_, &tmp);
2781cb0ef41Sopenharmony_ci      AddAndSetEntry(&current_, tmp);
2791cb0ef41Sopenharmony_ci      SourcePosition p = source_position();
2801cb0ef41Sopenharmony_ci      filter_satisfied =
2811cb0ef41Sopenharmony_ci          (iteration_filter_ == kAll) ||
2821cb0ef41Sopenharmony_ci          (iteration_filter_ == kJavaScriptOnly && p.IsJavaScript()) ||
2831cb0ef41Sopenharmony_ci          (iteration_filter_ == kExternalOnly && p.IsExternal());
2841cb0ef41Sopenharmony_ci    }
2851cb0ef41Sopenharmony_ci  }
2861cb0ef41Sopenharmony_ci}
2871cb0ef41Sopenharmony_ci
2881cb0ef41Sopenharmony_ci}  // namespace internal
2891cb0ef41Sopenharmony_ci}  // namespace v8
290