[vm/bytecode] Support source positions when executing bytecode

This includes both interpreted and compiled bytecode.

Change-Id: I6309ec4f0687f68caf6260cc57dacf94f027177d
Reviewed-on: https://dart-review.googlesource.com/c/84481
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Auto-Submit: Alexander Markov <alexmarkov@google.com>
Reviewed-by: RĂ©gis Crelier <regis@google.com>
diff --git a/runtime/platform/utils.h b/runtime/platform/utils.h
index 2917008..56c49bb 100644
--- a/runtime/platform/utils.h
+++ b/runtime/platform/utils.h
@@ -337,6 +337,26 @@
     return bit << n;
   }
 
+  // Decode integer in SLEB128 format from |data| and update |byte_index|.
+  static intptr_t DecodeSLEB128(const uint8_t* data,
+                                const intptr_t data_length,
+                                intptr_t* byte_index) {
+    ASSERT(*byte_index < data_length);
+    uword shift = 0;
+    intptr_t value = 0;
+    uint8_t part = 0;
+    do {
+      part = data[(*byte_index)++];
+      value |= static_cast<intptr_t>(part & 0x7f) << shift;
+      shift += 7;
+    } while ((part & 0x80) != 0);
+
+    if ((shift < (sizeof(value) * 8)) && ((part & 0x40) != 0)) {
+      value |= static_cast<intptr_t>(kUwordMax << shift);
+    }
+    return value;
+  }
+
   static char* StrError(int err, char* buffer, size_t bufsize);
 
   // Not all platforms support strndup.
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 68fc310..1cd8950 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -1756,6 +1756,7 @@
       for (RawObject** p = from; p <= to; p++) {
         s->WriteRef(*p);
       }
+      s->Write<int32_t>(bytecode->ptr()->source_positions_binary_offset_);
     }
   }
 
@@ -1791,6 +1792,7 @@
       for (RawObject** p = from; p <= to; p++) {
         *p = d->ReadRef();
       }
+      bytecode->ptr()->source_positions_binary_offset_ = d->Read<int32_t>();
     }
   }
 };
diff --git a/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc b/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc
index 5692014..ecf4f80 100644
--- a/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc
+++ b/runtime/vm/compiler/frontend/bytecode_flow_graph_builder.cc
@@ -5,6 +5,7 @@
 #include "vm/compiler/frontend/bytecode_flow_graph_builder.h"
 
 #include "vm/compiler/backend/il_printer.h"
+#include "vm/compiler/frontend/bytecode_reader.h"
 #include "vm/compiler/frontend/prologue_builder.h"
 #include "vm/compiler/jit/compiler.h"
 #include "vm/object_store.h"
@@ -1473,6 +1474,9 @@
 
   CollectControlFlow(descriptors, handlers, graph_entry);
 
+  kernel::BytecodeSourcePositionsIterator source_pos_iter(Z, bytecode);
+  bool update_position = source_pos_iter.MoveNext();
+
   code_ = Fragment(normal_entry);
 
   for (pc_ = 0; pc_ < bytecode_length_; ++pc_) {
@@ -1495,6 +1499,12 @@
       ASSERT(!code_.is_closed());
     }
 
+    while (update_position &&
+           pc_ >= source_pos_iter.BytecodeInstructionIndex()) {
+      position_ = source_pos_iter.TokenPos();
+      update_position = source_pos_iter.MoveNext();
+    }
+
     BuildInstruction(KernelBytecode::DecodeOpcode(bytecode_instr_));
 
     if (code_.is_closed()) {
diff --git a/runtime/vm/compiler/frontend/bytecode_reader.cc b/runtime/vm/compiler/frontend/bytecode_reader.cc
index d2fdfee..b5f2acc 100644
--- a/runtime/vm/compiler/frontend/bytecode_reader.cc
+++ b/runtime/vm/compiler/frontend/bytecode_reader.cc
@@ -103,6 +103,8 @@
   const Bytecode& bytecode =
       Bytecode::Handle(helper_->zone_, ReadBytecode(pool));
   function.AttachBytecode(bytecode);
+  ASSERT(bytecode.GetBinary(helper_->zone_) ==
+         helper_->reader_.typed_data()->raw());
 
   ReadExceptionsTable(bytecode, has_exceptions_table);
 
@@ -148,8 +150,9 @@
       // Read closure bytecode and attach to closure function.
       closure_bytecode = ReadBytecode(pool);
       closure.AttachBytecode(closure_bytecode);
+      ASSERT(bytecode.GetBinary(helper_->zone_) ==
+             helper_->reader_.typed_data()->raw());
 
-      // Read closure exceptions table.
       ReadExceptionsTable(closure_bytecode, has_exceptions_table);
 
       ReadSourcePositions(closure_bytecode, has_source_positions);
@@ -736,8 +739,9 @@
     return;
   }
 
-  // TODO(alexmarkov): store offset of source positions into Bytecode object.
-  helper_->SkipBytes(helper_->reader_.ReadUInt());
+  intptr_t length = helper_->reader_.ReadUInt();
+  bytecode.set_source_positions_binary_offset(helper_->reader_.offset());
+  helper_->SkipBytes(length);
 }
 
 RawTypedData* BytecodeMetadataHelper::NativeEntry(const Function& function,
diff --git a/runtime/vm/compiler/frontend/bytecode_reader.h b/runtime/vm/compiler/frontend/bytecode_reader.h
index 8dbc003..079316c 100644
--- a/runtime/vm/compiler/frontend/bytecode_reader.h
+++ b/runtime/vm/compiler/frontend/bytecode_reader.h
@@ -6,6 +6,7 @@
 #define RUNTIME_VM_COMPILER_FRONTEND_BYTECODE_READER_H_
 
 #include "vm/compiler/frontend/kernel_translation_helper.h"
+#include "vm/constants_kbc.h"
 #include "vm/object.h"
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
@@ -52,6 +53,46 @@
                                         const Function& function);
 };
 
+class BytecodeSourcePositionsIterator : ValueObject {
+ public:
+  BytecodeSourcePositionsIterator(Zone* zone, const Bytecode& bytecode)
+      : reader_(ExternalTypedData::Handle(zone, bytecode.GetBinary(zone))),
+        pairs_remaining_(0),
+        cur_bci_(0),
+        cur_token_pos_(TokenPosition::kNoSource.value()) {
+    if (bytecode.HasSourcePositions()) {
+      reader_.set_offset(bytecode.source_positions_binary_offset());
+      pairs_remaining_ = reader_.ReadUInt();
+    }
+  }
+
+  bool MoveNext() {
+    if (pairs_remaining_ == 0) {
+      return false;
+    }
+    ASSERT(pairs_remaining_ > 0);
+    --pairs_remaining_;
+    cur_bci_ += reader_.ReadUInt();
+    cur_token_pos_ += reader_.ReadSLEB128();
+    return true;
+  }
+
+  intptr_t BytecodeInstructionIndex() const { return cur_bci_; }
+
+  uword PcOffset() const {
+    return KernelBytecode::BytecodePcToOffset(BytecodeInstructionIndex(),
+                                              /* is_return_address = */ true);
+  }
+
+  TokenPosition TokenPos() const { return TokenPosition(cur_token_pos_); }
+
+ private:
+  Reader reader_;
+  intptr_t pairs_remaining_;
+  intptr_t cur_bci_;
+  intptr_t cur_token_pos_;
+};
+
 }  // namespace kernel
 }  // namespace dart
 
diff --git a/runtime/vm/kernel_binary.h b/runtime/vm/kernel_binary.h
index cad0c46..3b7d847 100644
--- a/runtime/vm/kernel_binary.h
+++ b/runtime/vm/kernel_binary.h
@@ -238,6 +238,11 @@
     }
   }
 
+  intptr_t ReadSLEB128() {
+    const uint8_t* buffer = this->buffer();
+    return Utils::DecodeSLEB128(buffer, size_, &offset_);
+  }
+
   /**
    * Read and return a TokenPosition from this reader.
    */
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index a82b1f4..4d20ed2 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -12722,26 +12722,6 @@
   }
 }
 
-// Decode integer in SLEB128 format from |data| and update |byte_index|.
-static intptr_t DecodeSLEB128(const uint8_t* data,
-                              const intptr_t data_length,
-                              intptr_t* byte_index) {
-  ASSERT(*byte_index < data_length);
-  uword shift = 0;
-  intptr_t value = 0;
-  uint8_t part = 0;
-  do {
-    part = data[(*byte_index)++];
-    value |= static_cast<intptr_t>(part & 0x7f) << shift;
-    shift += 7;
-  } while ((part & 0x80) != 0);
-
-  if ((shift < (sizeof(value) * 8)) && ((part & 0x40) != 0)) {
-    value |= static_cast<intptr_t>(kUwordMax << shift);
-  }
-  return value;
-}
-
 // Encode integer in SLEB128 format.
 void PcDescriptors::EncodeInteger(GrowableArray<uint8_t>* data,
                                   intptr_t value) {
@@ -12752,7 +12732,7 @@
 intptr_t PcDescriptors::DecodeInteger(intptr_t* byte_index) const {
   NoSafepointScope no_safepoint;
   const uint8_t* data = raw_ptr()->data();
-  return DecodeSLEB128(data, Length(), byte_index);
+  return Utils::DecodeSLEB128(data, Length(), byte_index);
 }
 
 RawObjectPool* ObjectPool::New(intptr_t len) {
@@ -15361,11 +15341,43 @@
     result.set_pc_descriptors(Object::empty_descriptors());
     result.set_instructions(instructions);
     result.set_object_pool(object_pool);
+    result.set_source_positions_binary_offset(0);
   }
   return result.raw();
 }
 #endif
 
+RawExternalTypedData* Bytecode::GetBinary(Zone* zone) const {
+  const Function& func = Function::Handle(zone, function());
+  const Script& script = Script::Handle(zone, func.script());
+  const KernelProgramInfo& info =
+      KernelProgramInfo::Handle(zone, script.kernel_program_info());
+  return info.metadata_payloads();
+}
+
+TokenPosition Bytecode::GetTokenIndexOfPC(uword pc) const {
+#if defined(DART_PRECOMPILED_RUNTIME)
+  UNREACHABLE();
+#else
+  if (!HasSourcePositions()) {
+    return TokenPosition::kNoSource;
+  }
+  uword pc_offset = pc - PayloadStart();
+  // PC could equal to bytecode size if the last instruction is Throw.
+  ASSERT(pc_offset <= static_cast<uword>(Size()));
+  kernel::BytecodeSourcePositionsIterator iter(Thread::Current()->zone(),
+                                               *this);
+  TokenPosition token_pos = TokenPosition::kNoSource;
+  while (iter.MoveNext()) {
+    if (pc_offset < iter.PcOffset()) {
+      break;
+    }
+    token_pos = iter.TokenPos();
+  }
+  return token_pos;
+#endif
+}
+
 const char* Bytecode::ToCString() const {
   return Thread::Current()->zone()->PrintToString("Bytecode(%s)",
                                                   QualifiedName());
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 6f5a157..5ac93eb 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -5341,9 +5341,18 @@
                           const ObjectPool& object_pool);
 #endif
 
-  TokenPosition GetTokenIndexOfPC(uword pc) const {
-    // TODO(alexmarkov): implement.
-    return TokenPosition::kNoSource;
+  RawExternalTypedData* GetBinary(Zone* zone) const;
+
+  TokenPosition GetTokenIndexOfPC(uword pc) const;
+
+  intptr_t source_positions_binary_offset() const {
+    return raw_ptr()->source_positions_binary_offset_;
+  }
+  void set_source_positions_binary_offset(intptr_t value) const {
+    StoreNonPointer(&raw_ptr()->source_positions_binary_offset_, value);
+  }
+  bool HasSourcePositions() const {
+    return (source_positions_binary_offset() != 0);
   }
 
   const char* Name() const;
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index ce9a94a..3b4717d 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -1488,6 +1488,8 @@
   RawPcDescriptors* pc_descriptors_;
   VISIT_TO(RawObject*, pc_descriptors_);
 
+  intptr_t source_positions_binary_offset_;
+
   friend class Function;
   friend class StackFrame;
 };
diff --git a/runtime/vm/stack_frame.cc b/runtime/vm/stack_frame.cc
index 59ed934..a43e7d6 100644
--- a/runtime/vm/stack_frame.cc
+++ b/runtime/vm/stack_frame.cc
@@ -456,8 +456,11 @@
 
 TokenPosition StackFrame::GetTokenPos() const {
   if (is_interpreted()) {
-    // TODO(alexmarkov): Support source information in bytecode.
-    return TokenPosition::kNoSource;
+    const Bytecode& bytecode = Bytecode::Handle(LookupDartBytecode());
+    if (bytecode.IsNull()) {
+      return TokenPosition::kNoSource;  // Stub frames do not have token_pos.
+    }
+    return bytecode.GetTokenIndexOfPC(pc());
   }
   const Code& code = Code::Handle(LookupDartCode());
   if (code.IsNull()) {
diff --git a/tests/language_2/language_2_kernel.status b/tests/language_2/language_2_kernel.status
index 26ee8bd..abdaa8a 100644
--- a/tests/language_2/language_2_kernel.status
+++ b/tests/language_2/language_2_kernel.status
@@ -347,11 +347,9 @@
 const_dynamic_type_literal_test/02: Pass
 map_literal3_test/01: Pass
 map_literal3_test/02: Pass
-vm/bool_check_stack_traces_test/01: RuntimeError # No support for line numbers in stacktraces
-vm/bool_check_stack_traces_test/none: RuntimeError # No support for line numbers in stacktraces
-vm/causal_async_exception_stack2_test: RuntimeError # No support for line numbers in stacktraces
-vm/causal_async_exception_stack_test: RuntimeError # No support for line numbers in stacktraces
-vm/regress_28325_test: RuntimeError # No support for line numbers in stacktraces
+vm/bool_check_stack_traces_test/02: Pass
+vm/causal_async_exception_stack2_test: RuntimeError # Please triage
+vm/causal_async_exception_stack_test: RuntimeError # Please triage
 
 [ $compiler != dartkb && $compiler != dartkp && $fasta ]
 vm/symbols_test/01: MissingCompileTimeError
diff --git a/tools/testing/dart/compiler_configuration.dart b/tools/testing/dart/compiler_configuration.dart
index 32b0de8..8fe7756 100644
--- a/tools/testing/dart/compiler_configuration.dart
+++ b/tools/testing/dart/compiler_configuration.dart
@@ -1108,6 +1108,7 @@
     if (_configuration.useKernelBytecode) {
       args.add('--gen-bytecode');
       args.add('--drop-ast');
+      args.add('--emit-bytecode-source-positions');
     }
 
     return Command.vmKernelCompilation(dillFile, true, bootstrapDependencies(),