Revision: 2548
Author: [email protected]
Date: Tue Jul 28 01:37:22 2009
Log: Merge r2479 and r2531 to branches/1.2.
Review URL: http://codereview.chromium.org/159495
http://code.google.com/p/v8/source/detail?r=2548

Added:
  /branches/1.2/test/mjsunit/debug-stepin-accessor.js
Modified:
  /branches/1.2/src/debug.cc
  /branches/1.2/src/execution.cc
  /branches/1.2/src/ia32/regexp-macro-assembler-ia32.cc
  /branches/1.2/src/objects.cc
  /branches/1.2/src/version.cc
  /branches/1.2/test/cctest/test-debug.cc
  /branches/1.2/test/mjsunit/mjsunit.status

=======================================
--- /dev/null
+++ /branches/1.2/test/mjsunit/debug-stepin-accessor.js Tue Jul 28 01:37:22  
2009
@@ -0,0 +1,248 @@
+// Copyright 2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --expose-debug-as debug
+
+// Get the Debug object exposed from the debug context global object.
+Debug = debug.Debug
+
+var exception = null;
+var state = 1;
+var expected_source_line_text = null;
+var expected_function_name = null;
+
+// Simple debug event handler which first time will cause 'step in' action
+// to get into g.call and than check that execution is pauesed inside
+// function 'g'.
+function listener(event, exec_state, event_data, data) {
+  try {
+    if (event == Debug.DebugEvent.Break) {
+      if (state == 1) {
+        exec_state.prepareStep(Debug.StepAction.StepIn, 2);
+        state = 2;
+      } else if (state == 2) {
+        assertEquals(expected_source_line_text,
+                     event_data.sourceLineText());
+        assertEquals(expected_function_name, event_data.func().name());
+        state = 3;
+      }
+    }
+  } catch(e) {
+    exception = e;
+  }
+};
+
+// Add the debug event listener.
+Debug.setListener(listener);
+
+
+var c = {
+  name: 'name ',
+  get getter1() {
+    return this.name;  // getter 1
+  },
+  get getter2() {
+    return {  // getter 2
+     'a': c.name
+    };
+  },
+  set setter1(n) {
+    this.name = n;  // setter 1
+  }
+};
+
+c.__defineGetter__('y', function getterY() {
+  return this.name;  // getter y
+});
+
+c.__defineGetter__(3, function getter3() {
+  return this.name;  // getter 3
+});
+
+c.__defineSetter__('y', function setterY(n) {
+  this.name = n;  // setter y
+});
+
+c.__defineSetter__(3, function setter3(n) {
+  this.name = n;  // setter 3
+});
+
+var d = {
+  'c': c,
+};
+
+function testGetter1_1() {
+  expected_function_name = 'getter1';
+  expected_source_line_text = '    return this.name;  // getter 1';
+  debugger;
+  var x = c.getter1;
+}
+
+function testGetter1_2() {
+  expected_function_name = 'getter1';
+  expected_source_line_text = '    return this.name;  // getter 1';
+  debugger;
+  var x = c['getter1'];
+}
+
+function testGetter1_3() {
+  expected_function_name = 'getter1';
+  expected_source_line_text = '    return this.name;  // getter 1';
+  debugger;
+  for (var i = 1; i < 2; i++) {
+    var x = c['getter' + i];
+  }
+}
+
+function testGetter1_4() {
+  expected_function_name = 'getter1';
+  expected_source_line_text = '    return this.name;  // getter 1';
+  debugger;
+  var x = d.c.getter1;
+}
+
+function testGetter1_5() {
+  expected_function_name = 'getter1';
+  expected_source_line_text = '    return this.name;  // getter 1';
+  for (var i = 2; i != 1; i--);
+  debugger;
+  var x = d.c['getter' + i];
+}
+
+function testGetter2_1() {
+  expected_function_name = 'getter2';
+  expected_source_line_text = '    return {  // getter 2';
+  for (var i = 2; i != 1; i--);
+  debugger;
+  var t = d.c.getter2.name;
+}
+
+
+function testGetterY_1() {
+  expected_function_name = 'getterY';
+  expected_source_line_text = '  return this.name;  // getter y';
+  debugger;
+  var t = d.c.y;
+}
+
+function testIndexedGetter3_1() {
+  expected_function_name = 'getter3';
+  expected_source_line_text = '  return this.name;  // getter 3';
+  debugger;
+  var r = d.c[3];
+}
+
+function testSetterY_1() {
+  expected_function_name = 'setterY';
+  expected_source_line_text = '  this.name = n;  // setter y';
+  debugger;
+  d.c.y = 'www';
+}
+
+function testIndexedSetter3_1() {
+  expected_function_name = 'setter3';
+  expected_source_line_text = '  this.name = n;  // setter 3';
+  var i = 3
+  debugger;
+  d.c[3] = 'www';
+}
+
+function testSetter1_1() {
+  expected_function_name = 'setter1';
+  expected_source_line_text = '    this.name = n;  // setter 1';
+  debugger;
+  d.c.setter1 = 'aa';
+}
+
+function testSetter1_2() {
+  expected_function_name = 'setter1';
+  expected_source_line_text = '    this.name = n;  // setter 1';
+  debugger;
+  d.c['setter1'] = 'bb';
+}
+
+function testSetter1_3() {
+  expected_function_name = 'setter1';
+  expected_source_line_text = '    this.name = n;  // setter 1';
+  for (var i = 2; i != 1; i--);
+  debugger;
+  d.c['setter' + i] = i;
+}
+
+var e = {
+  name: 'e'
+};
+e.__proto__ = c;
+
+function testProtoGetter1_1() {
+  expected_function_name = 'getter1';
+  expected_source_line_text = '    return this.name;  // getter 1';
+  debugger;
+  var x = e.getter1;
+}
+
+function testProtoSetter1_1() {
+  expected_function_name = 'setter1';
+  expected_source_line_text = '    this.name = n;  // setter 1';
+  debugger;
+  e.setter1 = 'aa';
+}
+
+function testProtoIndexedGetter3_1() {
+  expected_function_name = 'getter3';
+  expected_source_line_text = '  return this.name;  // getter 3';
+  debugger;
+  var x = e[3];
+}
+
+function testProtoIndexedSetter3_1() {
+  expected_function_name = 'setter3';
+  expected_source_line_text = '  this.name = n;  // setter 3';
+  debugger;
+  e[3] = 'new val';
+}
+
+function testProtoSetter1_2() {
+  expected_function_name = 'setter1';
+  expected_source_line_text = '    this.name = n;  // setter 1';
+  for (var i = 2; i != 1; i--);
+  debugger;
+  e['setter' + i] = 'aa';
+}
+
+for (var n in this) {
+  if (n.substr(0, 4) != 'test') {
+    continue;
+  }
+  state = 1;
+  this[n]();
+  assertNull(exception);
+  assertEquals(3, state);
+}
+
+// Get rid of the debug event listener.
+Debug.setListener(null);
=======================================
--- /branches/1.2/src/debug.cc  Mon Jul 13 07:04:26 2009
+++ /branches/1.2/src/debug.cc  Tue Jul 28 01:37:22 2009
@@ -334,8 +334,11 @@
        rinfo()->set_target_address(stub->entry());
      }
    } else {
-    // Step in through constructs call requires no changes to the running  
code.
-    ASSERT(RelocInfo::IsConstructCall(rmode()));
+    // Step in through construct call requires no changes to the running  
code.
+    // Step in through getters/setters should already be prepared as well
+    // because caller of this function (Debug::PrepareStep) is expected to
+    // flood the top frame's function with one shot breakpoints.
+    ASSERT(RelocInfo::IsConstructCall(rmode()) ||  
code->is_inline_cache_stub());
    }
  }

@@ -1087,10 +1090,18 @@

    // Compute whether or not the target is a call target.
    bool is_call_target = false;
+  bool is_load_or_store = false;
+  bool is_inline_cache_stub = false;
    if (RelocInfo::IsCodeTarget(it.rinfo()->rmode())) {
      Address target = it.rinfo()->target_address();
      Code* code = Code::GetCodeFromTargetAddress(target);
-    if (code->is_call_stub()) is_call_target = true;
+    if (code->is_call_stub()) {
+      is_call_target = true;
+    }
+    if (code->is_inline_cache_stub()) {
+      is_inline_cache_stub = true;
+      is_load_or_store = !is_call_target;
+    }
    }

    // If this is the last break code target step out is the only  
possibility.
@@ -1103,8 +1114,8 @@
        JSFunction* function =  
JSFunction::cast(frames_it.frame()->function());
        FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared()));
      }
-  } else if (!(is_call_target || RelocInfo::IsConstructCall(it.rmode())) ||
-             step_action == StepNext || step_action == StepMin) {
+  } else if (!(is_inline_cache_stub ||  
RelocInfo::IsConstructCall(it.rmode()))
+             || step_action == StepNext || step_action == StepMin) {
      // Step next or step min.

      // Fill the current function with one-shot break points.
@@ -1117,9 +1128,20 @@
    } else {
      // Fill the current function with one-shot break points even for step  
in on
      // a call target as the function called might be a native function for
-    // which step in will not stop.
+    // which step in will not stop. It also prepares for stepping in
+    // getters/setters.
      FloodWithOneShot(shared);

+    if (is_load_or_store) {
+      // Remember source position and frame to handle step in  
getter/setter. If
+      // there is a custom getter/setter it will be handled in
+      // Object::Get/SetPropertyWithCallback, otherwise the step action  
will be
+      // propagated on the next Debug::Break.
+      thread_local_.last_statement_position_ =
+          debug_info->code()->SourceStatementPosition(frame->pc());
+      thread_local_.last_fp_ = frame->fp();
+    }
+
      // Step in or Step in min
      it.PrepareStepIn();
      ActivateStepIn(frame);
=======================================
--- /branches/1.2/src/execution.cc      Wed Jul 15 01:24:12 2009
+++ /branches/1.2/src/execution.cc      Tue Jul 28 01:37:22 2009
@@ -586,6 +586,23 @@
    if (Debug::disable_break()) {
      return Heap::undefined_value();
    }
+
+  {
+    JavaScriptFrameIterator it;
+    ASSERT(!it.done());
+    Object* fun = it.frame()->function();
+    if (fun && fun->IsJSFunction()) {
+      GlobalObject* global = JSFunction::cast(fun)->context()->global();
+      // Don't stop in builtin functions.
+      if (global == Top::context()->builtins()) {
+       return Heap::undefined_value();
+      }
+      // Don't stop in debugger functions.
+      if (Debug::IsDebugGlobal(global)) {
+       return Heap::undefined_value();
+      }
+    }
+  }

    // Collect the break state before clearing the flags.
    bool debug_command_only =
=======================================
--- /branches/1.2/src/ia32/regexp-macro-assembler-ia32.cc       Fri May 29  
04:04:38 2009
+++ /branches/1.2/src/ia32/regexp-macro-assembler-ia32.cc       Tue Jul 28  
01:37:22 2009
@@ -634,11 +634,9 @@
    __ push(Immediate(0));  // Make room for "input start - 1" constant.

    // Check if we have space on the stack for registers.
-  Label retry_stack_check;
    Label stack_limit_hit;
    Label stack_ok;

-  __ bind(&retry_stack_check);
    ExternalReference stack_guard_limit =
        ExternalReference::address_of_stack_guard_limit();
    __ mov(ecx, esp);
@@ -658,10 +656,7 @@
    CallCheckStackGuardState(ebx);
    __ or_(eax, Operand(eax));
    // If returned value is non-zero, we exit with the returned value as  
result.
-  // Otherwise it was a preemption and we just check the limit again.
-  __ j(equal, &retry_stack_check);
-  // Return value was non-zero. Exit with exception or retry.
-  __ jmp(&exit_label_);
+  __ j(not_zero, &exit_label_);

    __ bind(&stack_ok);

@@ -762,19 +757,11 @@
      __ push(backtrack_stackpointer());
      __ push(edi);

-    Label retry;
-
-    __ bind(&retry);
      CallCheckStackGuardState(ebx);
      __ or_(eax, Operand(eax));
      // If returning non-zero, we should end execution with the given
      // result as return value.
      __ j(not_zero, &exit_label_);
-    // Check if we are still preempted.
-    ExternalReference stack_guard_limit =
-        ExternalReference::address_of_stack_guard_limit();
-    __ cmp(esp, Operand::StaticVariable(stack_guard_limit));
-    __ j(below_equal, &retry);

      __ pop(edi);
      __ pop(backtrack_stackpointer());
=======================================
--- /branches/1.2/src/objects.cc        Mon Jul 13 07:04:26 2009
+++ /branches/1.2/src/objects.cc        Tue Jul 28 01:37:22 2009
@@ -216,6 +216,12 @@
    HandleScope scope;
    Handle<JSFunction> fun(JSFunction::cast(getter));
    Handle<Object> self(receiver);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+  // Handle stepping into a getter if step into is active.
+  if (Debug::StepInActive()) {
+    Debug::HandleStepIn(fun, Handle<Object>::null(), 0, false);
+  }
+#endif
    bool has_pending_exception;
    Handle<Object> result =
        Execution::Call(fun, self, 0, NULL, &has_pending_exception);
@@ -1624,6 +1630,12 @@
    Handle<Object> value_handle(value);
    Handle<JSFunction> fun(JSFunction::cast(setter));
    Handle<JSObject> self(this);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+  // Handle stepping into a setter if step into is active.
+  if (Debug::StepInActive()) {
+    Debug::HandleStepIn(fun, Handle<Object>::null(), 0, false);
+  }
+#endif
    bool has_pending_exception;
    Object** argv[] = { value_handle.location() };
    Execution::Call(fun, self, 1, argv, &has_pending_exception);
=======================================
--- /branches/1.2/src/version.cc        Tue Jul 28 01:14:49 2009
+++ /branches/1.2/src/version.cc        Tue Jul 28 01:37:22 2009
@@ -35,7 +35,7 @@
  #define MAJOR_VERSION     1
  #define MINOR_VERSION     2
  #define BUILD_NUMBER      14
-#define PATCH_LEVEL       7
+#define PATCH_LEVEL       8
  #define CANDIDATE_VERSION false

  // Define SONAME to have the SCons build the put a specific SONAME into the
=======================================
--- /branches/1.2/test/cctest/test-debug.cc     Wed Jul  8 23:00:35 2009
+++ /branches/1.2/test/cctest/test-debug.cc     Tue Jul 28 01:37:22 2009
@@ -4875,7 +4875,7 @@
    v8::Debug::SetMessageHandler2(DebugBreakMessageHandler);

    // Test functions.
-  const char* script = "function f() { debugger; } function g() { }";
+  const char* script = "function f() { debugger; g(); } function g() { }";
    CompileRun(script);
    v8::Local<v8::Function> f =
         
v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
@@ -4954,8 +4954,10 @@
    v8::Debug::DebugBreak();
    result = f->Call(env->Global(), argc, argv);

-  CHECK_EQ(20, break_point_hit_count);
-  CHECK_EQ("exec", last_function_hit);
+  // Check that there was only one break event. Matching RegExp should not
+  // cause Break events.
+  CHECK_EQ(1, break_point_hit_count);
+  CHECK_EQ("f", last_function_hit);
  }
  #endif  // V8_NATIVE_REGEXP

@@ -5295,3 +5297,63 @@
    ClearBreakPointFromJS(sbp2);
    v8::Debug::SetMessageHandler2(NULL);
  }
+
+
+static void BreakMessageHandler(const v8::Debug::Message& message) {
+  if (message.IsEvent() && message.GetEvent() == v8::Break) {
+    // Count the number of breaks.
+    break_point_hit_count++;
+
+    v8::HandleScope scope;
+    v8::Handle<v8::String> json = message.GetJSON();
+
+    SendContinueCommand();
+  } else if (message.IsEvent() && message.GetEvent() == v8::AfterCompile) {
+    v8::HandleScope scope;
+
+    bool is_debug_break = i::StackGuard::IsDebugBreak();
+    // Force DebugBreak flag while serializer is working.
+    i::StackGuard::DebugBreak();
+
+    // Force serialization to trigger some internal JS execution.
+    v8::Handle<v8::String> json = message.GetJSON();
+
+    // Restore previous state.
+    if (is_debug_break) {
+      i::StackGuard::DebugBreak();
+    } else {
+      i::StackGuard::Continue(i::DEBUGBREAK);
+    }
+  }
+}
+
+
+// Test that if DebugBreak is forced it is ignored when code from
+// debug-delay.js is executed.
+TEST(NoDebugBreakInAfterCompileMessageHandler) {
+  v8::HandleScope scope;
+  DebugLocalContext env;
+
+  // Register a debug event listener which sets the break flag and counts.
+  v8::Debug::SetMessageHandler2(BreakMessageHandler);
+
+  // Set the debug break flag.
+  v8::Debug::DebugBreak();
+
+  // Create a function for testing stepping.
+  const char* src = "function f() { eval('var x = 10;'); } ";
+  v8::Local<v8::Function> f = CompileFunction(&env, src, "f");
+
+  // There should be only one break event.
+  CHECK_EQ(1, break_point_hit_count);
+
+  // Set the debug break flag again.
+  v8::Debug::DebugBreak();
+  f->Call(env->Global(), 0, NULL);
+  // There should be one more break event when the script is evaluated  
in 'f'.
+  CHECK_EQ(2, break_point_hit_count);
+
+  // Get rid of the debug message handler.
+  v8::Debug::SetMessageHandler2(NULL);
+  CheckDebuggerUnloaded();
+}
=======================================
--- /branches/1.2/test/mjsunit/mjsunit.status   Mon Jul 13 07:04:26 2009
+++ /branches/1.2/test/mjsunit/mjsunit.status   Tue Jul 28 01:37:22 2009
@@ -58,6 +58,7 @@
  debug-multiple-breakpoints: CRASH || FAIL
  debug-setbreakpoint: CRASH || FAIL || PASS
  debug-step-stub-callfunction: SKIP
+debug-stepin-accessor: CRASH || FAIL
  debug-stepin-constructor: CRASH, FAIL
  debug-stepin-function-call: CRASH || FAIL
  debug-step: SKIP

--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---

Reply via email to