Support scope information and evaluation in optimized frames
authorsgjesse@chromium.org <sgjesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 13 Jul 2011 12:49:27 +0000 (12:49 +0000)
committersgjesse@chromium.org <sgjesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 13 Jul 2011 12:49:27 +0000 (12:49 +0000)
R=svenpanne@chromium.org

BUG=v8:1140
TEST=test/mjsunit/debug-evaluate-locals-optimized.js,test/mjsunit/debug-evaluate-locals-optimized-double.js

Review URL: http://codereview.chromium.org//7343005

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8633 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/mirror-debugger.js
src/runtime.cc
src/runtime.h
test/mjsunit/debug-evaluate-locals-optimized-double.js
test/mjsunit/debug-evaluate-locals-optimized.js

index 57de6c6..0a32597 100644 (file)
@@ -1250,9 +1250,9 @@ const kFrameDetailsNameIndex = 0;
 const kFrameDetailsValueIndex = 1;
 const kFrameDetailsNameValueSize = 2;
 
-const kFrameDetailsFlagDebuggerFrame = 1;
-const kFrameDetailsFlagOptimizedFrame = 2;
-const kFrameDetailsFlagInlinedFrame = 4;
+const kFrameDetailsFlagDebuggerFrameMask = 1 << 0;
+const kFrameDetailsFlagOptimizedFrameMask = 1 << 1;
+const kFrameDetailsFlagInlinedFrameIndexMask = 7 << 2;
 
 /**
  * Wrapper for the frame details information retreived from the VM. The frame
@@ -1266,7 +1266,7 @@ const kFrameDetailsFlagInlinedFrame = 4;
  *     5: Source position
  *     6: Construct call
  *     7: Is at return
- *     8: Flags (debugger frame, optimized frame, inlined frame)
+ *     8: Flags (debugger frame, optimized frame, inlined frame index)
  *     Arguments name, value
  *     Locals name, value
  *     Return value if any
@@ -1312,22 +1312,27 @@ FrameDetails.prototype.isAtReturn = function() {
 
 FrameDetails.prototype.isDebuggerFrame = function() {
   %CheckExecutionState(this.break_id_);
-  var f = kFrameDetailsFlagDebuggerFrame;
+  var f = kFrameDetailsFlagDebuggerFrameMask;
   return (this.details_[kFrameDetailsFlagsIndex] & f) == f;
 }
 
 
 FrameDetails.prototype.isOptimizedFrame = function() {
   %CheckExecutionState(this.break_id_);
-  var f = kFrameDetailsFlagOptimizedFrame;
+  var f = kFrameDetailsFlagOptimizedFrameMask;
   return (this.details_[kFrameDetailsFlagsIndex] & f) == f;
 }
 
 
 FrameDetails.prototype.isInlinedFrame = function() {
+  return this.inlinedFrameIndex() > 0;
+}
+
+
+FrameDetails.prototype.inlinedFrameIndex = function() {
   %CheckExecutionState(this.break_id_);
-  var f = kFrameDetailsFlagInlinedFrame;
-  return (this.details_[kFrameDetailsFlagsIndex] & f) == f;
+  var f = kFrameDetailsFlagInlinedFrameIndexMask;
+  return (this.details_[kFrameDetailsFlagsIndex] & f) >> 2
 }
 
 
@@ -1476,6 +1481,11 @@ FrameMirror.prototype.isInlinedFrame = function() {
 };
 
 
+FrameMirror.prototype.inlinedFrameIndex = function() {
+  return this.details_.inlinedFrameIndex();
+};
+
+
 FrameMirror.prototype.argumentCount = function() {
   return this.details_.argumentCount();
 };
@@ -1565,8 +1575,12 @@ FrameMirror.prototype.scope = function(index) {
 
 
 FrameMirror.prototype.evaluate = function(source, disable_break, opt_context_object) {
-  var result = %DebugEvaluate(this.break_id_, this.details_.frameId(),
-                              source, Boolean(disable_break), opt_context_object);
+  var result = %DebugEvaluate(this.break_id_,
+                              this.details_.frameId(),
+                              this.details_.inlinedFrameIndex(),
+                              source,
+                              Boolean(disable_break),
+                              opt_context_object);
   return MakeMirror(result);
 };
 
@@ -1719,6 +1733,7 @@ function ScopeDetails(frame, index) {
   this.break_id_ = frame.break_id_;
   this.details_ = %GetScopeDetails(frame.break_id_,
                                    frame.details_.frameId(),
+                                   frame.details_.inlinedFrameIndex(),
                                    index);
 }
 
index 56507ae..c6b2649 100644 (file)
@@ -9976,6 +9976,71 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameCount) {
 }
 
 
+class FrameInspector {
+ public:
+  FrameInspector(JavaScriptFrame* frame,
+                 int inlined_frame_index,
+                 Isolate* isolate)
+      : frame_(frame), deoptimized_frame_(NULL), isolate_(isolate) {
+    // Calculate the deoptimized frame.
+    if (frame->is_optimized()) {
+      deoptimized_frame_ = Deoptimizer::DebuggerInspectableFrame(
+          frame, inlined_frame_index, isolate);
+    }
+    has_adapted_arguments_ = frame_->has_adapted_arguments();
+    is_optimized_ = frame_->is_optimized();
+  }
+
+  ~FrameInspector() {
+    // Get rid of the calculated deoptimized frame if any.
+    if (deoptimized_frame_ != NULL) {
+      Deoptimizer::DeleteDebuggerInspectableFrame(deoptimized_frame_,
+                                                  isolate_);
+    }
+  }
+
+  int GetParametersCount() {
+    return is_optimized_
+        ? deoptimized_frame_->parameters_count()
+        : frame_->ComputeParametersCount();
+  }
+  int expression_count() { return deoptimized_frame_->expression_count(); }
+  Object* GetFunction() {
+    return is_optimized_
+        ? deoptimized_frame_->GetFunction()
+        : frame_->function();
+  }
+  Object* GetParameter(int index) {
+    return is_optimized_
+        ? deoptimized_frame_->GetParameter(index)
+        : frame_->GetParameter(index);
+  }
+  Object* GetExpression(int index) {
+    return is_optimized_
+        ? deoptimized_frame_->GetExpression(index)
+        : frame_->GetExpression(index);
+  }
+
+  // To inspect all the provided arguments the frame might need to be
+  // replaced with the arguments frame.
+  void SetArgumentsFrame(JavaScriptFrame* frame) {
+    ASSERT(has_adapted_arguments_);
+    frame_ = frame;
+    is_optimized_ = frame_->is_optimized();
+    ASSERT(!is_optimized_);
+  }
+
+ private:
+  JavaScriptFrame* frame_;
+  DeoptimizedFrameInfo* deoptimized_frame_;
+  Isolate* isolate_;
+  bool is_optimized_;
+  bool has_adapted_arguments_;
+
+  DISALLOW_COPY_AND_ASSIGN(FrameInspector);
+};
+
+
 static const int kFrameDetailsFrameIdIndex = 0;
 static const int kFrameDetailsReceiverIndex = 1;
 static const int kFrameDetailsFunctionIndex = 2;
@@ -10024,8 +10089,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
     return heap->undefined_value();
   }
 
-  int deoptimized_frame_index = -1;  // Frame index in optimized frame.
-  DeoptimizedFrameInfo* deoptimized_frame = NULL;
+  int inlined_frame_index = 0;  // Inlined frame index in optimized frame.
 
   int count = 0;
   JavaScriptFrameIterator it(isolate, id);
@@ -10036,13 +10100,10 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
   if (it.done()) return heap->undefined_value();
 
   if (it.frame()->is_optimized()) {
-    deoptimized_frame_index =
+    inlined_frame_index =
         it.frame()->GetInlineCount() - (index - count) - 1;
-    deoptimized_frame = Deoptimizer::DebuggerInspectableFrame(
-        it.frame(),
-        deoptimized_frame_index,
-        isolate);
   }
+  FrameInspector frame_inspector(it.frame(), inlined_frame_index, isolate);
 
   // Traverse the saved contexts chain to find the active context for the
   // selected frame.
@@ -10061,7 +10122,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
 
   // Check for constructor frame. Inlined frames cannot be construct calls.
   bool inlined_frame =
-      it.frame()->is_optimized() && deoptimized_frame_index != 0;
+      it.frame()->is_optimized() && inlined_frame_index != 0;
   bool constructor = !inlined_frame && it.frame()->IsConstructor();
 
   // Get scope info and read from it for local variable information.
@@ -10083,14 +10144,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
   for (; i < info.number_of_stack_slots(); ++i) {
     // Use the value from the stack.
     locals->set(i * 2, *info.LocalName(i));
-    if (it.frame()->is_optimized()) {
-      // Get the value from the deoptimized frame.
-      locals->set(i * 2 + 1,
-                  deoptimized_frame->GetExpression(i));
-    } else {
-      // Get the value from the stack.
-      locals->set(i * 2 + 1, it.frame()->GetExpression(i));
-    }
+    locals->set(i * 2 + 1, frame_inspector.GetExpression(i));
   }
   if (i < info.NumberOfLocals()) {
     // Get the context containing declarations.
@@ -10147,18 +10201,22 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
   // the provided parameters whereas the function frame always have the number
   // of arguments matching the functions parameters. The rest of the
   // information (except for what is collected above) is the same.
-  it.AdvanceToArgumentsFrame();
+  if (it.frame()->has_adapted_arguments()) {
+    it.AdvanceToArgumentsFrame();
+    frame_inspector.SetArgumentsFrame(it.frame());
+  }
 
   // Find the number of arguments to fill. At least fill the number of
   // parameters for the function and fill more if more parameters are provided.
   int argument_count = info.number_of_parameters();
+  if (argument_count < frame_inspector.GetParametersCount()) {
+    argument_count = frame_inspector.GetParametersCount();
+  }
+#ifdef DEBUG
   if (it.frame()->is_optimized()) {
-    ASSERT_EQ(argument_count, deoptimized_frame->parameters_count());
-  } else {
-    if (argument_count < it.frame()->ComputeParametersCount()) {
-      argument_count = it.frame()->ComputeParametersCount();
-    }
+    ASSERT_EQ(argument_count, frame_inspector.GetParametersCount());
   }
+#endif
 
   // Calculate the size of the result.
   int details_size = kFrameDetailsFirstDynamicIndex +
@@ -10170,13 +10228,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
   details->set(kFrameDetailsFrameIdIndex, *frame_id);
 
   // Add the function (same as in function frame).
-  if (it.frame()->is_optimized()) {
-    // Get the function from the deoptimized frame.
-    details->set(kFrameDetailsFunctionIndex, deoptimized_frame->GetFunction());
-  } else {
-    // Get the function from the stack.
-    details->set(kFrameDetailsFunctionIndex, it.frame()->function());
-  }
+  details->set(kFrameDetailsFunctionIndex, frame_inspector.GetFunction());
 
   // Add the arguments count.
   details->set(kFrameDetailsArgumentCountIndex, Smi::FromInt(argument_count));
@@ -10208,9 +10260,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
   }
   if (it.frame()->is_optimized()) {
     flags |= 1 << 1;
-    if (deoptimized_frame_index > 0) {
-      flags |= 1 << 2;
-    }
+    flags |= inlined_frame_index << 2;
   }
   details->set(kFrameDetailsFlagsIndex, Smi::FromInt(flags));
 
@@ -10227,16 +10277,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
     }
 
     // Parameter value.
-    if (it.frame()->is_optimized()) {
-      // Get the value from the deoptimized frame.
-      details->set(details_index++, deoptimized_frame->GetParameter(i));
+    if (i < it.frame()->ComputeParametersCount()) {
+      // Get the value from the stack.
+      details->set(details_index++, frame_inspector.GetParameter(i));
     } else {
-      if (i < it.frame()->ComputeParametersCount()) {
-        // Get the value from the stack.
-        details->set(details_index++, it.frame()->GetParameter(i));
-      } else {
-        details->set(details_index++, heap->undefined_value());
-      }
+      details->set(details_index++, heap->undefined_value());
     }
   }
 
@@ -10268,12 +10313,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFrameDetails) {
   }
   details->set(kFrameDetailsReceiverIndex, *receiver);
 
-  // Get rid of the calculated deoptimized frame if any.
-  if (deoptimized_frame != NULL) {
-    Deoptimizer::DeleteDebuggerInspectableFrame(deoptimized_frame,
-                                                isolate);
-  }
-
   ASSERT_EQ(details_size, details_index);
   return *isolate->factory()->NewJSArrayWithElements(details);
 }
@@ -10309,12 +10348,15 @@ static bool CopyContextLocalsToScopeObject(
 
 // Create a plain JSObject which materializes the local scope for the specified
 // frame.
-static Handle<JSObject> MaterializeLocalScope(Isolate* isolate,
-                                              JavaScriptFrame* frame) {
+static Handle<JSObject> MaterializeLocalScope(
+    Isolate* isolate,
+    JavaScriptFrame* frame,
+    int inlined_frame_index) {
   Handle<JSFunction> function(JSFunction::cast(frame->function()));
   Handle<SharedFunctionInfo> shared(function->shared());
   Handle<SerializedScopeInfo> serialized_scope_info(shared->scope_info());
   ScopeInfo<> scope_info(*serialized_scope_info);
+  FrameInspector frame_inspector(frame, inlined_frame_index, isolate);
 
   // Allocate and initialize a JSObject with all the arguments, stack locals
   // heap locals and extension properties of the debugged function.
@@ -10327,7 +10369,7 @@ static Handle<JSObject> MaterializeLocalScope(Isolate* isolate,
         isolate,
         SetProperty(local_scope,
                     scope_info.parameter_name(i),
-                    Handle<Object>(frame->GetParameter(i), isolate),
+                    Handle<Object>(frame_inspector.GetParameter(i)),
                     NONE,
                     kNonStrictMode),
         Handle<JSObject>());
@@ -10339,7 +10381,7 @@ static Handle<JSObject> MaterializeLocalScope(Isolate* isolate,
         isolate,
         SetProperty(local_scope,
                     scope_info.stack_slot_name(i),
-                    Handle<Object>(frame->GetExpression(i), isolate),
+                    Handle<Object>(frame_inspector.GetExpression(i)),
                     NONE,
                     kNonStrictMode),
         Handle<JSObject>());
@@ -10459,9 +10501,12 @@ class ScopeIterator {
     ScopeTypeCatch
   };
 
-  ScopeIterator(Isolate* isolate, JavaScriptFrame* frame)
+  ScopeIterator(Isolate* isolate,
+                JavaScriptFrame* frame,
+                int inlined_frame_index)
     : isolate_(isolate),
       frame_(frame),
+      inlined_frame_index_(inlined_frame_index),
       function_(JSFunction::cast(frame->function())),
       context_(Context::cast(frame->context())),
       local_done_(false),
@@ -10546,7 +10591,7 @@ class ScopeIterator {
         return Handle<JSObject>(CurrentContext()->global());
       case ScopeIterator::ScopeTypeLocal:
         // Materialize the content of the local scope into a JSObject.
-        return MaterializeLocalScope(isolate_, frame_);
+        return MaterializeLocalScope(isolate_, frame_, inlined_frame_index_);
       case ScopeIterator::ScopeTypeWith:
         // Return the with object.
         return Handle<JSObject>(JSObject::cast(CurrentContext()->extension()));
@@ -10626,6 +10671,7 @@ class ScopeIterator {
  private:
   Isolate* isolate_;
   JavaScriptFrame* frame_;
+  int inlined_frame_index_;
   Handle<JSFunction> function_;
   Handle<Context> context_;
   bool local_done_;
@@ -10654,7 +10700,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetScopeCount) {
 
   // Count the visible scopes.
   int n = 0;
-  for (ScopeIterator it(isolate, frame); !it.Done(); it.Next()) {
+  for (ScopeIterator it(isolate, frame, 0);
+       !it.Done();
+       it.Next()) {
     n++;
   }
 
@@ -10669,14 +10717,15 @@ static const int kScopeDetailsSize = 2;
 // Return an array with scope details
 // args[0]: number: break id
 // args[1]: number: frame index
-// args[2]: number: scope index
+// args[2]: number: inlined frame index
+// args[3]: number: scope index
 //
 // The array returned contains the following information:
 // 0: Scope type
 // 1: Scope object
 RUNTIME_FUNCTION(MaybeObject*, Runtime_GetScopeDetails) {
   HandleScope scope(isolate);
-  ASSERT(args.length() == 3);
+  ASSERT(args.length() == 4);
 
   // Check arguments.
   Object* check;
@@ -10685,7 +10734,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetScopeDetails) {
     if (!maybe_check->ToObject(&check)) return maybe_check;
   }
   CONVERT_CHECKED(Smi, wrapped_id, args[1]);
-  CONVERT_NUMBER_CHECKED(int, index, Int32, args[2]);
+  CONVERT_NUMBER_CHECKED(int, inlined_frame_index, Int32, args[2]);
+  CONVERT_NUMBER_CHECKED(int, index, Int32, args[3]);
 
   // Get the frame where the debugging is performed.
   StackFrame::Id id = UnwrapFrameId(wrapped_id);
@@ -10694,7 +10744,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetScopeDetails) {
 
   // Find the requested scope.
   int n = 0;
-  ScopeIterator it(isolate, frame);
+  ScopeIterator it(isolate, frame, inlined_frame_index);
   for (; !it.Done() && n < index; it.Next()) {
     n++;
   }
@@ -10724,7 +10774,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugPrintScopes) {
   // Print the scopes for the top frame.
   StackFrameLocator locator;
   JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
-  for (ScopeIterator it(isolate, frame); !it.Done(); it.Next()) {
+  for (ScopeIterator it(isolate, frame, 0);
+       !it.Done();
+       it.Next()) {
     it.DebugPrint();
   }
 #endif
@@ -11117,6 +11169,7 @@ static Handle<Context> CopyWithContextChain(Isolate* isolate,
 // Runtime_DebugEvaluate.
 static Handle<Object> GetArgumentsObject(Isolate* isolate,
                                          JavaScriptFrame* frame,
+                                         int inlined_frame_index,
                                          Handle<JSFunction> function,
                                          Handle<SerializedScopeInfo> scope_info,
                                          const ScopeInfo<>* sinfo,
@@ -11128,6 +11181,7 @@ static Handle<Object> GetArgumentsObject(Isolate* isolate,
   if (sinfo->number_of_stack_slots() > 0) {
     index = scope_info->StackSlotIndex(isolate->heap()->arguments_symbol());
     if (index != -1) {
+      CHECK(false);
       return Handle<Object>(frame->GetExpression(index), isolate);
     }
   }
@@ -11140,7 +11194,9 @@ static Handle<Object> GetArgumentsObject(Isolate* isolate,
     }
   }
 
-  const int length = frame->ComputeParametersCount();
+  FrameInspector frame_inspector(frame, inlined_frame_index, isolate);
+
+  int length = frame_inspector.GetParametersCount();
   Handle<JSObject> arguments =
       isolate->factory()->NewArgumentsObject(function, length);
   Handle<FixedArray> array = isolate->factory()->NewFixedArray(length);
@@ -11148,7 +11204,7 @@ static Handle<Object> GetArgumentsObject(Isolate* isolate,
   AssertNoAllocation no_gc;
   WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
   for (int i = 0; i < length; i++) {
-    array->set(i, frame->GetParameter(i), mode);
+    array->set(i, frame_inspector.GetParameter(i), mode);
   }
   arguments->set_elements(*array);
   return arguments;
@@ -11175,7 +11231,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) {
 
   // Check the execution state and decode arguments frame and source to be
   // evaluated.
-  ASSERT(args.length() == 5);
+  ASSERT(args.length() == 6);
   Object* check_result;
   { MaybeObject* maybe_check_result = Runtime_CheckExecutionState(
       RUNTIME_ARGUMENTS(isolate, args));
@@ -11184,9 +11240,10 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) {
     }
   }
   CONVERT_CHECKED(Smi, wrapped_id, args[1]);
-  CONVERT_ARG_CHECKED(String, source, 2);
-  CONVERT_BOOLEAN_CHECKED(disable_break, args[3]);
-  Handle<Object> additional_context(args[4]);
+  CONVERT_NUMBER_CHECKED(int, inlined_frame_index, Int32, args[2]);
+  CONVERT_ARG_CHECKED(String, source, 3);
+  CONVERT_BOOLEAN_CHECKED(disable_break, args[4]);
+  Handle<Object> additional_context(args[5]);
 
   // Handle the processing of break.
   DisableBreak disable_break_save(disable_break);
@@ -11226,7 +11283,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) {
 #endif
 
   // Materialize the content of the local scope into a JSObject.
-  Handle<JSObject> local_scope = MaterializeLocalScope(isolate, frame);
+  Handle<JSObject> local_scope = MaterializeLocalScope(
+      isolate, frame, inlined_frame_index);
   RETURN_IF_EMPTY_HANDLE(isolate, local_scope);
 
   // Allocate a new context for the debug evaluation and set the extension
@@ -11275,7 +11333,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) {
                       &has_pending_exception);
   if (has_pending_exception) return Failure::Exception();
 
-  Handle<Object> arguments = GetArgumentsObject(isolate, frame,
+  Handle<Object> arguments = GetArgumentsObject(isolate,
+                                                frame, inlined_frame_index,
                                                 function, scope_info,
                                                 &sinfo, function_context);
 
index e59c82c..cafd3d5 100644 (file)
@@ -372,7 +372,7 @@ namespace internal {
   F(GetFrameCount, 1, 1) \
   F(GetFrameDetails, 2, 1) \
   F(GetScopeCount, 2, 1) \
-  F(GetScopeDetails, 3, 1) \
+  F(GetScopeDetails, 4, 1) \
   F(DebugPrintScopes, 0, 1) \
   F(GetThreadCount, 1, 1) \
   F(GetThreadDetails, 2, 1) \
@@ -385,7 +385,7 @@ namespace internal {
   F(IsBreakOnException, 1, 1) \
   F(PrepareStep, 3, 1) \
   F(ClearStepping, 0, 1) \
-  F(DebugEvaluate, 5, 1) \
+  F(DebugEvaluate, 6, 1) \
   F(DebugEvaluateGlobal, 4, 1) \
   F(DebugGetLoadedScripts, 0, 1) \
   F(DebugReferencedBy, 3, 1) \
index 584d1af..9ed1dbb 100644 (file)
@@ -44,21 +44,44 @@ function listener(event, exec_state, event_data, data) {
       for (var i = 0; i < exec_state.frameCount(); i++) {
         var frame = exec_state.frame(i);
         if (i < exec_state.frameCount() - 1) {
-        // All frames except the bottom one has normal variables a and b.
+          var expected_a = i * 2 + 1 + (i * 2 + 1) / 100;
+          var expected_b = i * 2 + 2 + (i * 2 + 2) / 100;
+          var expected_x = (i + 1) * 2 + 1 + ((i + 1) * 2 + 1) / 100;
+          var expected_y = (i + 1) * 2 + 2 + ((i + 1) * 2 + 2) / 100;
+
+          // All frames except the bottom one has normal variables a and b.
           assertEquals('a', frame.localName(0));
           assertEquals('b', frame.localName(1));
-          assertEquals(i * 2 + 1 + (i * 2 + 1) / 100,
-                       frame.localValue(0).value());
-          assertEquals(i * 2 + 2 + (i * 2 + 2) / 100,
-                       frame.localValue(1).value());
+          assertEquals(expected_a, frame.localValue(0).value());
+          assertEquals(expected_b, frame.localValue(1).value());
 
           // All frames except the bottom one has arguments variables x and y.
           assertEquals('x', frame.argumentName(0));
           assertEquals('y', frame.argumentName(1));
-          assertEquals((i + 1) * 2 + 1 + ((i + 1) * 2 + 1) / 100,
-                       frame.argumentValue(0).value());
-          assertEquals((i + 1) * 2 + 2 + ((i + 1) * 2 + 2) / 100,
-                       frame.argumentValue(1).value());
+          assertEquals(expected_x, frame.argumentValue(0).value());
+          assertEquals(expected_y, frame.argumentValue(1).value());
+
+          // All frames except the bottom one have two scopes.
+          assertEquals(2, frame.scopeCount());
+          assertEquals(debug.ScopeType.Local, frame.scope(0).scopeType());
+          assertEquals(debug.ScopeType.Global, frame.scope(1).scopeType());
+          assertEquals(expected_a, frame.scope(0).scopeObject().value()['a']);
+          assertEquals(expected_b, frame.scope(0).scopeObject().value()['b']);
+          assertEquals(expected_x, frame.scope(0).scopeObject().value()['x']);
+          assertEquals(expected_y, frame.scope(0).scopeObject().value()['y']);
+
+          // Evaluate in the inlined frame.
+          assertEquals(expected_a, frame.evaluate('a').value());
+          assertEquals(expected_x, frame.evaluate('x').value());
+          assertEquals(expected_x, frame.evaluate('arguments[0]').value());
+          assertEquals(expected_a + expected_b + expected_x + expected_y,
+                       frame.evaluate('a + b + x + y').value());
+          assertEquals(expected_x + expected_y,
+                       frame.evaluate('arguments[0] + arguments[1]').value());
+        } else {
+          // The bottom frame only have the global scope.
+          assertEquals(1, frame.scopeCount());
+          assertEquals(debug.ScopeType.Global, frame.scope(0).scopeType());
         }
 
         // Check the frame function.
@@ -75,12 +98,13 @@ function listener(event, exec_state, event_data, data) {
         // Check for construct call.
         assertEquals(testingConstructCall && i == 4, frame.isConstructCall());
 
-        // When function f is optimized (2 means YES, see runtime.cc) we
+        // When function f is optimized (1 means YES, see runtime.cc) we
         // expect an optimized frame for f with g1, g2 and g3 inlined.
-        if (%GetOptimizationStatus(f) == 2) {
+        if (%GetOptimizationStatus(f) == 1) {
           if (i == 1 || i == 2 || i == 3) {
             assertTrue(frame.isOptimizedFrame());
             assertTrue(frame.isInlinedFrame());
+            assertEquals(4 - i, frame.inlinedFrameIndex());
           } else if (i == 4) {
             assertTrue(frame.isOptimizedFrame());
             assertFalse(frame.isInlinedFrame());
index 1aaf296..683c139 100644 (file)
@@ -44,17 +44,44 @@ function listener(event, exec_state, event_data, data) {
       for (var i = 0; i < exec_state.frameCount(); i++) {
         var frame = exec_state.frame(i);
         if (i < exec_state.frameCount() - 1) {
+          var expected_a = i * 2 + 1;
+          var expected_b = i * 2 + 2;
+          var expected_x = (i + 1) * 2 + 1;
+          var expected_y = (i + 1) * 2 + 2;
+
           // All frames except the bottom one has normal variables a and b.
           assertEquals('a', frame.localName(0));
           assertEquals('b', frame.localName(1));
-          assertEquals(i * 2 + 1, frame.localValue(0).value());
-          assertEquals(i * 2 + 2, frame.localValue(1).value());
+          assertEquals(expected_a, frame.localValue(0).value());
+          assertEquals(expected_b, frame.localValue(1).value());
 
           // All frames except the bottom one has arguments variables x and y.
           assertEquals('x', frame.argumentName(0));
           assertEquals('y', frame.argumentName(1));
-          assertEquals((i + 1) * 2 + 1, frame.argumentValue(0).value());
-          assertEquals((i + 1) * 2 + 2, frame.argumentValue(1).value());
+          assertEquals(expected_x, frame.argumentValue(0).value());
+          assertEquals(expected_y, frame.argumentValue(1).value());
+
+          // All frames except the bottom one have two scopes.
+          assertEquals(2, frame.scopeCount());
+          assertEquals(debug.ScopeType.Local, frame.scope(0).scopeType());
+          assertEquals(debug.ScopeType.Global, frame.scope(1).scopeType());
+          assertEquals(expected_a, frame.scope(0).scopeObject().value()['a']);
+          assertEquals(expected_b, frame.scope(0).scopeObject().value()['b']);
+          assertEquals(expected_x, frame.scope(0).scopeObject().value()['x']);
+          assertEquals(expected_y, frame.scope(0).scopeObject().value()['y']);
+
+          // Evaluate in the inlined frame.
+          assertEquals(expected_a, frame.evaluate('a').value());
+          assertEquals(expected_x, frame.evaluate('x').value());
+          assertEquals(expected_x, frame.evaluate('arguments[0]').value());
+          assertEquals(expected_a + expected_b + expected_x + expected_y,
+                       frame.evaluate('a + b + x + y').value());
+          assertEquals(expected_x + expected_y,
+                       frame.evaluate('arguments[0] + arguments[1]').value());
+        } else {
+          // The bottom frame only have the global scope.
+          assertEquals(1, frame.scopeCount());
+          assertEquals(debug.ScopeType.Global, frame.scope(0).scopeType());
         }
 
         // Check the frame function.
@@ -71,12 +98,13 @@ function listener(event, exec_state, event_data, data) {
         // Check for construct call.
         assertEquals(testingConstructCall && i == 4, frame.isConstructCall());
 
-        // When function f is optimized (2 means YES, see runtime.cc) we
+        // When function f is optimized (1 means YES, see runtime.cc) we
         // expect an optimized frame for f with g1, g2 and g3 inlined.
-        if (%GetOptimizationStatus(f) == 2) {
+        if (%GetOptimizationStatus(f) == 1) {
           if (i == 1 || i == 2 || i == 3) {
             assertTrue(frame.isOptimizedFrame());
             assertTrue(frame.isInlinedFrame());
+            assertEquals(4 - i, frame.inlinedFrameIndex());
           } else if (i == 4) {
             assertTrue(frame.isOptimizedFrame());
             assertFalse(frame.isInlinedFrame());