From 83973a0a2ea76e21d12c93dccf6bbe25496321fa Mon Sep 17 00:00:00 2001 From: "peter.rybin@gmail.com" Date: Fri, 22 Jun 2012 20:50:03 +0000 Subject: [PATCH] Correctly support several consecutive stack modifications. Review URL: https://chromiumcodereview.appspot.com/10637003 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@11914 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/debug.cc | 4 +- src/debug.h | 3 +- src/liveedit.cc | 13 ++- test/mjsunit/debug-liveedit-double-call.js | 142 +++++++++++++++++++++++++++++ 4 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 test/mjsunit/debug-liveedit-double-call.js diff --git a/src/debug.cc b/src/debug.cc index 668e6a1..ffe5b0d 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -2331,7 +2331,9 @@ bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) { void Debug::FramesHaveBeenDropped(StackFrame::Id new_break_frame_id, FrameDropMode mode, Object** restarter_frame_function_pointer) { - thread_local_.frame_drop_mode_ = mode; + if (mode != CURRENTLY_SET_MODE) { + thread_local_.frame_drop_mode_ = mode; + } thread_local_.break_frame_id_ = new_break_frame_id; thread_local_.restarter_frame_function_pointer_ = restarter_frame_function_pointer; diff --git a/src/debug.h b/src/debug.h index 607dab7..445137c 100644 --- a/src/debug.h +++ b/src/debug.h @@ -448,7 +448,8 @@ class Debug { // The top JS frame had been calling some C++ function. The return address // gets patched automatically. FRAME_DROPPED_IN_DIRECT_CALL, - FRAME_DROPPED_IN_RETURN_CALL + FRAME_DROPPED_IN_RETURN_CALL, + CURRENTLY_SET_MODE }; void FramesHaveBeenDropped(StackFrame::Id new_break_frame_id, diff --git a/src/liveedit.cc b/src/liveedit.cc index 7f64554..80e18bb 100644 --- a/src/liveedit.cc +++ b/src/liveedit.cc @@ -1468,7 +1468,9 @@ static const char* DropFrames(Vector frames, isolate->builtins()->builtin( Builtins::kFrameDropper_LiveEdit)) { // OK, we can drop our own code. - *mode = Debug::FRAME_DROPPED_IN_DIRECT_CALL; + pre_top_frame = frames[top_frame_index - 2]; + top_frame = frames[top_frame_index - 1]; + *mode = Debug::CURRENTLY_SET_MODE; frame_has_padding = false; } else if (pre_top_frame_code == isolate->builtins()->builtin(Builtins::kReturn_DebugBreak)) { @@ -1483,6 +1485,15 @@ static const char* DropFrames(Vector frames, // Here the stub is CEntry, it's not debug-only and can't be padded. // If anyone would complain, a proxy padded stub could be added. frame_has_padding = false; + } else if (pre_top_frame->type() == StackFrame::ARGUMENTS_ADAPTOR) { + // This must be adaptor that remain from the frame dropping that + // is still on stack. A frame dropper frame must be above it. + ASSERT(frames[top_frame_index - 2]->LookupCode() == + isolate->builtins()->builtin(Builtins::kFrameDropper_LiveEdit)); + pre_top_frame = frames[top_frame_index - 3]; + top_frame = frames[top_frame_index - 2]; + *mode = Debug::CURRENTLY_SET_MODE; + frame_has_padding = false; } else { return "Unknown structure of stack above changing function"; } diff --git a/test/mjsunit/debug-liveedit-double-call.js b/test/mjsunit/debug-liveedit-double-call.js new file mode 100644 index 0000000..1df806a --- /dev/null +++ b/test/mjsunit/debug-liveedit-double-call.js @@ -0,0 +1,142 @@ +// Copyright 2012 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 + + +function TestCase(test_scenario, expected_output) { + // Global variable, accessed from eval'd script. + test_output = ""; + + var script_text_generator = (function() { + var variables = { a: 1, b: 1, c: 1, d: 1, e: 1, f: 1 }; + + return { + get: function() { + return "(function() {\n " + + " function A() {\n " + + " test_output += 'a' + " + variables.a + ";\n " + + " test_output += '=';\n " + + " debugger;\n " + + " return 'Capybara';\n " + + " }\n " + + " function B(p1, p2) {\n " + + " test_output += 'b' + " + variables.b + ";\n " + + " return A();\n " + + " }\n " + + " function C() {\n " + + " test_output += 'c' + " + variables.c + ";\n " + + " // Function call with argument adaptor is intentional.\n " + + " return B();\n " + + " }\n " + + " function D() {\n " + + " test_output += 'd' + " + variables.d + ";\n " + + " // Function call with argument adaptor is intentional.\n " + + " return C(1, 2);\n " + + " }\n " + + " function E() {\n " + + " test_output += 'e' + " + variables.e + ";\n " + + " return D();\n " + + " }\n " + + " function F() {\n " + + " test_output += 'f' + " + variables.f + ";\n " + + " return E();\n " + + " }\n " + + " return F();\n " + + "})\n"; + }, + change: function(var_name) { + variables[var_name]++; + } + }; + })(); + + var test_fun = eval(script_text_generator.get()); + + var script = Debug.findScript(test_fun); + + var scenario_pos = 0; + + function DebuggerStatementHandler() { + while (true) { + assertTrue(scenario_pos < test_scenario.length); + var change_var = test_scenario[scenario_pos++]; + if (change_var == '=') { + // Continue. + return; + } + script_text_generator.change(change_var); + try { + Debug.LiveEdit.SetScriptSource(script, script_text_generator.get(), + false, []); + } catch (e) { + print("LiveEdit exception: " + e); + throw e; + } + } + } + + var saved_exception = null; + + function listener(event, exec_state, event_data, data) { + if (event == Debug.DebugEvent.Break) { + try { + DebuggerStatementHandler(); + } catch (e) { + saved_exception = e; + } + } else { + print("Other: " + event); + } + } + + Debug.setListener(listener); + assertEquals("Capybara", test_fun()); + Debug.setListener(null); + + if (saved_exception) { + print("Exception: " + saved_exception); + assertUnreachable(); + } + + print(test_output); + + assertEquals(expected_output, test_output); +} + +TestCase(['='], "f1e1d1c1b1a1="); + +TestCase(['c', '=', '='], "f1e1d1c1b1a1=c2b1a1="); + +TestCase(['b', 'c', 'd', 'e', '=', '='], "f1e1d1c1b1a1=e2d2c2b2a1="); + +TestCase(['b', 'c', '=', 'b', 'c', 'd', 'e', '=', '='], "f1e1d1c1b1a1=c2b2a1=e2d2c3b3a1="); + +TestCase(['e', 'f', '=', '='], "f1e1d1c1b1a1=f2e2d1c1b1a1="); -- 2.7.4