[vm/debugger] Make use of variable descriptors in bytecode debugger.

Change-Id: Ibf4c8f638065c8173c5c21c8fa9c40ee5cecb587
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106427
Commit-Queue: Régis Crelier <regis@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
diff --git a/runtime/vm/compiler/frontend/bytecode_reader.cc b/runtime/vm/compiler/frontend/bytecode_reader.cc
index c747fbd..51b1102 100644
--- a/runtime/vm/compiler/frontend/bytecode_reader.cc
+++ b/runtime/vm/compiler/frontend/bytecode_reader.cc
@@ -266,8 +266,16 @@
       if (FLAG_dump_kernel_bytecode) {
         KernelBytecodeDisassembler::Disassemble(closure);
       }
+
+#if !defined(PRODUCT)
+      thread_->isolate()->debugger()->NotifyBytecodeLoaded(closure);
+#endif
     }
   }
+
+#if !defined(PRODUCT)
+  thread_->isolate()->debugger()->NotifyBytecodeLoaded(function);
+#endif
 }
 
 static intptr_t IndexFor(Zone* zone,
@@ -2343,13 +2351,6 @@
         bytecode_reader.ReadCode(function, code_offset);
       }
     }
-
-#if !defined(PRODUCT)
-    if (function.HasBytecode()) {
-      thread->isolate()->debugger()->NotifyBytecodeLoaded(function);
-    }
-#endif
-
     return Error::null();
   } else {
     return thread->StealStickyError();
@@ -2416,6 +2417,7 @@
             function.token_pos() <= var_info.end_pos) ||
            (function.token_pos() <= var_info.begin_pos &&
             var_info.begin_pos <= function.end_token_pos()))) {
+        var_info.scope_id++;  // One level higher in the context chain.
         vars.Add(
             VarDesc{&String::Handle(zone, parent_vars.GetName(i)), var_info});
       }
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index 10412c0..9436d8d 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -652,8 +652,8 @@
 void ActivationFrame::GetVarDescriptors() {
   if (var_descriptors_.IsNull()) {
     if (IsInterpreted()) {
-      // TODO(regis): Kernel bytecode does not yet provide var descriptors.
-      var_descriptors_ = Object::empty_var_descriptors().raw();
+      var_descriptors_ = bytecode().GetLocalVarDescriptors();
+      ASSERT(!var_descriptors_.IsNull());
       return;
     }
     Code& unoptimized_code = Code::Handle(function().unoptimized_code());
@@ -684,9 +684,14 @@
   OS::PrintErr("deopt_id_ %" Px "\n", deopt_id_);
   OS::PrintErr("context_level_ %" Px "\n", context_level_);
   DisassembleToStdout formatter;
-  if (IsInterpreted()) {
-    bytecode().Disassemble(&formatter);
-    PcDescriptors::Handle(bytecode().pc_descriptors()).Print();
+  if (function().is_declared_in_bytecode()) {
+#if !defined(DART_PRECOMPILED_RUNTIME)
+    ASSERT(function().HasBytecode());
+    const Bytecode& bytecode = Bytecode::Handle(function().bytecode());
+    bytecode.Disassemble(&formatter);
+#else
+    UNREACHABLE();
+#endif  // !defined(DART_PRECOMPILED_RUNTIME)
   } else {
     code().Disassemble(&formatter);
     PcDescriptors::Handle(code().pc_descriptors()).Print();
@@ -702,38 +707,97 @@
   OS::Abort();
 }
 
-// Calculate the context level at the current token index of the frame.
+// Calculate the context level at the current bytecode pc or code deopt id
+// of the frame.
 intptr_t ActivationFrame::ContextLevel() {
-  // TODO(regis): get context level information using
-  //  BytecodeLocalVariablesIterator for interpreted frames and compiled frames
-  //  with a function coming from bytecode (function.is_declared_in_bytecode())
   const Context& ctx = GetSavedCurrentContext();
   if (context_level_ < 0 && !ctx.IsNull()) {
-    ASSERT(!code_.is_optimized());
-
-    GetVarDescriptors();
-    intptr_t deopt_id = DeoptId();
-    if (deopt_id == DeoptId::kNone) {
-      PrintDescriptorsError("Missing deopt id");
-    }
-    intptr_t var_desc_len = var_descriptors_.Length();
-    bool found = false;
-    for (intptr_t cur_idx = 0; cur_idx < var_desc_len; cur_idx++) {
-      RawLocalVarDescriptors::VarInfo var_info;
-      var_descriptors_.GetInfo(cur_idx, &var_info);
-      const int8_t kind = var_info.kind();
-      if ((kind == RawLocalVarDescriptors::kContextLevel) &&
-          (deopt_id >= var_info.begin_pos.value()) &&
-          (deopt_id <= var_info.end_pos.value())) {
-        context_level_ = var_info.index();
-        found = true;
-        break;
+    ASSERT(IsInterpreted() || !code_.is_optimized());
+    if (function().is_declared_in_bytecode()) {
+#if !defined(DART_PRECOMPILED_RUNTIME)
+      // Although this activation frame may not have bytecode, its code was
+      // compiled from bytecode.
+      if (!IsInterpreted()) {
+        // TODO(regis): If this frame was compiled from bytecode, pc_ does not
+        // reflect a bytecode pc. How do we map to one? We should generate new
+        // LocalVarDescriptors for code compiled from bytecode so that they
+        // provide deopt_id to context level mapping.
+        UNIMPLEMENTED();
       }
+      ASSERT(function().HasBytecode());
+      Thread* thread = Thread::Current();
+      Zone* zone = thread->zone();
+      Bytecode& bytecode = Bytecode::Handle(zone, function().bytecode());
+      if (!bytecode.HasLocalVariablesInfo()) {
+        PrintDescriptorsError("Missing local variables info");
+      }
+      intptr_t pc_offset = pc_ - bytecode.PayloadStart();
+      kernel::BytecodeLocalVariablesIterator local_vars(zone, bytecode);
+      while (local_vars.MoveNext()) {
+        if (local_vars.Kind() ==
+            kernel::BytecodeLocalVariablesIterator::kScope) {
+          if (local_vars.StartPC() <= pc_offset &&
+              pc_offset < local_vars.EndPC()) {
+            context_level_ = local_vars.ContextLevel();
+            break;
+          }
+        }
+      }
+      if (context_level_ < 0 && function().IsClosureFunction()) {
+        // Obtain the context level from the parent function.
+        // TODO(alexmarkov): Define scope which includes the whole closure body.
+        Function& parent = Function::Handle(zone, function().parent_function());
+        intptr_t depth = 1;
+        do {
+          bytecode = parent.bytecode();
+          kernel::BytecodeLocalVariablesIterator local_vars(zone, bytecode);
+          while (local_vars.MoveNext()) {
+            if (local_vars.Kind() ==
+                kernel::BytecodeLocalVariablesIterator::kScope) {
+              if (local_vars.StartTokenPos() <= TokenPos() &&
+                  TokenPos() <= local_vars.EndTokenPos()) {
+                context_level_ = local_vars.ContextLevel() + depth;
+                break;
+              }
+            }
+          }
+          if (context_level_ >= 0) break;
+          parent = parent.parent_function();
+          depth++;
+        } while (!parent.IsNull());
+      }
+      if (context_level_ < 0) {
+        PrintDescriptorsError("Missing context level in local variables info");
+      }
+#else
+      UNREACHABLE();
+#endif  // !defined(DART_PRECOMPILED_RUNTIME)
+    } else {
+      ASSERT(!code_.is_optimized());
+      GetVarDescriptors();
+      intptr_t deopt_id = DeoptId();
+      if (deopt_id == DeoptId::kNone) {
+        PrintDescriptorsError("Missing deopt id");
+      }
+      intptr_t var_desc_len = var_descriptors_.Length();
+      bool found = false;
+      for (intptr_t cur_idx = 0; cur_idx < var_desc_len; cur_idx++) {
+        RawLocalVarDescriptors::VarInfo var_info;
+        var_descriptors_.GetInfo(cur_idx, &var_info);
+        const int8_t kind = var_info.kind();
+        if ((kind == RawLocalVarDescriptors::kContextLevel) &&
+            (deopt_id >= var_info.begin_pos.value()) &&
+            (deopt_id <= var_info.end_pos.value())) {
+          context_level_ = var_info.index();
+          found = true;
+          break;
+        }
+      }
+      if (!found) {
+        PrintDescriptorsError("Missing context level");
+      }
+      ASSERT(context_level_ >= 0);
     }
-    if (!found) {
-      PrintDescriptorsError("Missing context level");
-    }
-    ASSERT(context_level_ >= 0);
   }
   return context_level_;
 }
@@ -1135,10 +1199,22 @@
   return *reinterpret_cast<RawObject**>(addr);
 }
 
+// Caution: GetParameter only works for fixed parameters.
 RawObject* ActivationFrame::GetParameter(intptr_t index) {
   intptr_t num_parameters = function().num_fixed_parameters();
   ASSERT(0 <= index && index < num_parameters);
 
+  if (IsInterpreted()) {
+    if (function().NumOptionalParameters() > 0) {
+      // Note that we do not access optional but only fixed parameters, hence
+      // we do not need to replicate the logic of IndexFor() in bytecode reader.
+      return GetVariableValue(fp() + index * kWordSize);
+    } else {
+      return GetVariableValue(
+          fp() - (kKBCParamEndSlotFromFp + num_parameters - index) * kWordSize);
+    }
+  }
+
   if (function().NumOptionalParameters() > 0) {
     // If the function has optional parameters, the first positional parameter
     // can be in a number of places in the caller's frame depending on how many
@@ -1159,6 +1235,13 @@
 }
 
 RawObject* ActivationFrame::GetStackVar(VariableIndex variable_index) {
+  if (IsInterpreted()) {
+    intptr_t slot_index = -variable_index.value();
+    if (slot_index < 0) {
+      slot_index -= kKBCParamEndSlotFromFp;  // Accessing a parameter.
+    }
+    return GetVariableValue(fp() + slot_index * kWordSize);
+  }
   const intptr_t slot_index =
       runtime_frame_layout.FrameSlotForVariableIndex(variable_index.value());
   if (deopt_frame_.IsNull()) {
diff --git a/runtime/vm/scopes.h b/runtime/vm/scopes.h
index 70c6287..b1e0c2c 100644
--- a/runtime/vm/scopes.h
+++ b/runtime/vm/scopes.h
@@ -42,7 +42,7 @@
 //    c) [LocalVariable]s referring to values on the expression stack. Those are
 //       assigned by the flow graph builder. The indices of those variables are
 //       assigned by the flow graph builder (it simulates the expression stack
-//       height), they go from -NumVariabables - ExpressionHeight.
+//       height), they go from -NumVariables - ExpressionHeight.
 //
 //       -> These variables participate only partially in SSA renaming and can
 //          therefore only be used with [LoadLocalInstr]s and with