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
-~----------~----~----~----~------~----~------~--~---