| // 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. |
| |
| #include <stdlib.h> |
| |
| #include "src/init/v8.h" |
| |
| #include "src/api/api-inl.h" |
| #include "src/codegen/compilation-cache.h" |
| #include "src/debug/debug-interface.h" |
| #include "src/debug/debug.h" |
| #include "src/deoptimizer/deoptimizer.h" |
| #include "src/execution/frames.h" |
| #include "src/execution/microtask-queue.h" |
| #include "src/objects/objects-inl.h" |
| #include "src/snapshot/snapshot.h" |
| #include "src/utils/utils.h" |
| #include "test/cctest/cctest.h" |
| |
| using ::v8::internal::Handle; |
| using ::v8::internal::StepNone; // From StepAction enum |
| using ::v8::internal::StepIn; // From StepAction enum |
| using ::v8::internal::StepNext; // From StepAction enum |
| using ::v8::internal::StepOut; // From StepAction enum |
| |
| // --- H e l p e r F u n c t i o n s |
| |
| // Compile and run the supplied source and return the requested function. |
| static v8::Local<v8::Function> CompileFunction(v8::Isolate* isolate, |
| const char* source, |
| const char* function_name) { |
| CompileRunChecked(isolate, source); |
| v8::Local<v8::String> name = v8_str(isolate, function_name); |
| v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
| v8::MaybeLocal<v8::Value> maybe_function = |
| context->Global()->Get(context, name); |
| return v8::Local<v8::Function>::Cast(maybe_function.ToLocalChecked()); |
| } |
| |
| |
| // Compile and run the supplied source and return the requested function. |
| static v8::Local<v8::Function> CompileFunction(LocalContext* env, |
| const char* source, |
| const char* function_name) { |
| return CompileFunction((*env)->GetIsolate(), source, function_name); |
| } |
| |
| // Is there any debug info for the function? |
| static bool HasBreakInfo(v8::Local<v8::Function> fun) { |
| Handle<v8::internal::JSFunction> f = |
| Handle<v8::internal::JSFunction>::cast(v8::Utils::OpenHandle(*fun)); |
| Handle<v8::internal::SharedFunctionInfo> shared(f->shared(), f->GetIsolate()); |
| return shared->HasBreakInfo(); |
| } |
| |
| // Set a break point in a function with a position relative to function start, |
| // and return the associated break point number. |
| static i::Handle<i::BreakPoint> SetBreakPoint(v8::Local<v8::Function> fun, |
| int position, |
| const char* condition = nullptr) { |
| i::Handle<i::JSFunction> function = |
| i::Handle<i::JSFunction>::cast(v8::Utils::OpenHandle(*fun)); |
| position += function->shared().StartPosition(); |
| static int break_point_index = 0; |
| i::Isolate* isolate = function->GetIsolate(); |
| i::Handle<i::String> condition_string = |
| condition ? isolate->factory()->NewStringFromAsciiChecked(condition) |
| : isolate->factory()->empty_string(); |
| i::Debug* debug = isolate->debug(); |
| i::Handle<i::BreakPoint> break_point = |
| isolate->factory()->NewBreakPoint(++break_point_index, condition_string); |
| |
| debug->SetBreakpoint(handle(function->shared(), isolate), break_point, |
| &position); |
| return break_point; |
| } |
| |
| |
| static void ClearBreakPoint(i::Handle<i::BreakPoint> break_point) { |
| v8::internal::Isolate* isolate = CcTest::i_isolate(); |
| v8::internal::Debug* debug = isolate->debug(); |
| debug->ClearBreakPoint(break_point); |
| } |
| |
| |
| // Change break on exception. |
| static void ChangeBreakOnException(bool caught, bool uncaught) { |
| v8::internal::Debug* debug = CcTest::i_isolate()->debug(); |
| debug->ChangeBreakOnException(v8::internal::BreakException, caught); |
| debug->ChangeBreakOnException(v8::internal::BreakUncaughtException, uncaught); |
| } |
| |
| |
| // Prepare to step to next break location. |
| static void PrepareStep(i::StepAction step_action) { |
| v8::internal::Debug* debug = CcTest::i_isolate()->debug(); |
| debug->PrepareStep(step_action); |
| } |
| |
| // This function is in namespace v8::internal to be friend with class |
| // v8::internal::Debug. |
| namespace v8 { |
| namespace internal { |
| |
| // Collect the currently debugged functions. |
| Handle<FixedArray> GetDebuggedFunctions() { |
| Debug* debug = CcTest::i_isolate()->debug(); |
| |
| v8::internal::DebugInfoListNode* node = debug->debug_info_list_; |
| |
| // Find the number of debugged functions. |
| int count = 0; |
| while (node) { |
| count++; |
| node = node->next(); |
| } |
| |
| // Allocate array for the debugged functions |
| Handle<FixedArray> debugged_functions = |
| CcTest::i_isolate()->factory()->NewFixedArray(count); |
| |
| // Run through the debug info objects and collect all functions. |
| count = 0; |
| while (node) { |
| debugged_functions->set(count++, *node->debug_info()); |
| node = node->next(); |
| } |
| |
| return debugged_functions; |
| } |
| |
| |
| // Check that the debugger has been fully unloaded. |
| void CheckDebuggerUnloaded() { |
| // Check that the debugger context is cleared and that there is no debug |
| // information stored for the debugger. |
| CHECK(!CcTest::i_isolate()->debug()->debug_info_list_); |
| |
| // Collect garbage to ensure weak handles are cleared. |
| CcTest::CollectAllGarbage(); |
| CcTest::CollectAllGarbage(); |
| |
| // Iterate the heap and check that there are no debugger related objects left. |
| HeapObjectIterator iterator(CcTest::heap()); |
| for (HeapObject obj = iterator.Next(); !obj.is_null(); |
| obj = iterator.Next()) { |
| CHECK(!obj.IsDebugInfo()); |
| } |
| } |
| |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| |
| // Check that the debugger has been fully unloaded. |
| static void CheckDebuggerUnloaded() { v8::internal::CheckDebuggerUnloaded(); } |
| |
| // --- D e b u g E v e n t H a n d l e r s |
| // --- |
| // --- The different tests uses a number of debug event handlers. |
| // --- |
| |
| // Debug event handler which counts a number of events. |
| int break_point_hit_count = 0; |
| int break_point_hit_count_deoptimize = 0; |
| class DebugEventCounter : public v8::debug::DebugDelegate { |
| public: |
| void BreakProgramRequested( |
| v8::Local<v8::Context>, |
| const std::vector<v8::debug::BreakpointId>&) override { |
| break_point_hit_count++; |
| // Perform a full deoptimization when the specified number of |
| // breaks have been hit. |
| if (break_point_hit_count == break_point_hit_count_deoptimize) { |
| i::Deoptimizer::DeoptimizeAll(CcTest::i_isolate()); |
| } |
| if (step_action_ != StepNone) PrepareStep(step_action_); |
| } |
| |
| void set_step_action(i::StepAction step_action) { |
| step_action_ = step_action; |
| } |
| |
| private: |
| i::StepAction step_action_ = StepNone; |
| }; |
| |
| // Debug event handler which performs a garbage collection. |
| class DebugEventBreakPointCollectGarbage : public v8::debug::DebugDelegate { |
| public: |
| void BreakProgramRequested( |
| v8::Local<v8::Context>, |
| const std::vector<v8::debug::BreakpointId>&) override { |
| // Perform a garbage collection when break point is hit and continue. Based |
| // on the number of break points hit either scavenge or mark compact |
| // collector is used. |
| break_point_hit_count++; |
| if (break_point_hit_count % 2 == 0) { |
| // Scavenge. |
| CcTest::CollectGarbage(v8::internal::NEW_SPACE); |
| } else { |
| // Mark sweep compact. |
| CcTest::CollectAllGarbage(); |
| } |
| } |
| }; |
| |
| // Debug event handler which re-issues a debug break and calls the garbage |
| // collector to have the heap verified. |
| class DebugEventBreak : public v8::debug::DebugDelegate { |
| public: |
| void BreakProgramRequested( |
| v8::Local<v8::Context>, |
| const std::vector<v8::debug::BreakpointId>&) override { |
| // Count the number of breaks. |
| break_point_hit_count++; |
| |
| // Run the garbage collector to enforce heap verification if option |
| // --verify-heap is set. |
| CcTest::CollectGarbage(v8::internal::NEW_SPACE); |
| |
| // Set the break flag again to come back here as soon as possible. |
| v8::debug::SetBreakOnNextFunctionCall(CcTest::isolate()); |
| } |
| }; |
| |
| static void BreakRightNow(v8::Isolate* isolate, void*) { |
| v8::debug::BreakRightNow(isolate); |
| } |
| |
| // Debug event handler which re-issues a debug break until a limit has been |
| // reached. |
| int max_break_point_hit_count = 0; |
| bool terminate_after_max_break_point_hit = false; |
| class DebugEventBreakMax : public v8::debug::DebugDelegate { |
| public: |
| void BreakProgramRequested( |
| v8::Local<v8::Context>, |
| const std::vector<v8::debug::BreakpointId>&) override { |
| v8::Isolate* v8_isolate = CcTest::isolate(); |
| v8::internal::Isolate* isolate = CcTest::i_isolate(); |
| if (break_point_hit_count < max_break_point_hit_count) { |
| // Count the number of breaks. |
| break_point_hit_count++; |
| |
| // Set the break flag again to come back here as soon as possible. |
| v8_isolate->RequestInterrupt(BreakRightNow, nullptr); |
| |
| } else if (terminate_after_max_break_point_hit) { |
| // Terminate execution after the last break if requested. |
| v8_isolate->TerminateExecution(); |
| } |
| |
| // Perform a full deoptimization when the specified number of |
| // breaks have been hit. |
| if (break_point_hit_count == break_point_hit_count_deoptimize) { |
| i::Deoptimizer::DeoptimizeAll(isolate); |
| } |
| } |
| }; |
| |
| // --- T h e A c t u a l T e s t s |
| |
| // Test that the debug info in the VM is in sync with the functions being |
| // debugged. |
| TEST(DebugInfo) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| // Create a couple of functions for the test. |
| v8::Local<v8::Function> foo = |
| CompileFunction(&env, "function foo(){}", "foo"); |
| v8::Local<v8::Function> bar = |
| CompileFunction(&env, "function bar(){}", "bar"); |
| // Initially no functions are debugged. |
| CHECK_EQ(0, v8::internal::GetDebuggedFunctions()->length()); |
| CHECK(!HasBreakInfo(foo)); |
| CHECK(!HasBreakInfo(bar)); |
| EnableDebugger(env->GetIsolate()); |
| // One function (foo) is debugged. |
| i::Handle<i::BreakPoint> bp1 = SetBreakPoint(foo, 0); |
| CHECK_EQ(1, v8::internal::GetDebuggedFunctions()->length()); |
| CHECK(HasBreakInfo(foo)); |
| CHECK(!HasBreakInfo(bar)); |
| // Two functions are debugged. |
| i::Handle<i::BreakPoint> bp2 = SetBreakPoint(bar, 0); |
| CHECK_EQ(2, v8::internal::GetDebuggedFunctions()->length()); |
| CHECK(HasBreakInfo(foo)); |
| CHECK(HasBreakInfo(bar)); |
| // One function (bar) is debugged. |
| ClearBreakPoint(bp1); |
| CHECK_EQ(1, v8::internal::GetDebuggedFunctions()->length()); |
| CHECK(!HasBreakInfo(foo)); |
| CHECK(HasBreakInfo(bar)); |
| // No functions are debugged. |
| ClearBreakPoint(bp2); |
| DisableDebugger(env->GetIsolate()); |
| CHECK_EQ(0, v8::internal::GetDebuggedFunctions()->length()); |
| CHECK(!HasBreakInfo(foo)); |
| CHECK(!HasBreakInfo(bar)); |
| } |
| |
| |
| // Test that a break point can be set at an IC store location. |
| TEST(BreakPointICStore) { |
| break_point_hit_count = 0; |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| v8::Local<v8::Function> foo = |
| CompileFunction(&env, "function foo(){bar=0;}", "foo"); |
| |
| // Run without breakpoints. |
| foo->Call(env.local(), env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint |
| i::Handle<i::BreakPoint> bp = SetBreakPoint(foo, 0); |
| foo->Call(env.local(), env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(1, break_point_hit_count); |
| foo->Call(env.local(), env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| foo->Call(env.local(), env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| // Test that a break point can be set at an IC store location. |
| TEST(BreakPointCondition) { |
| break_point_hit_count = 0; |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| CompileRun("var a = false"); |
| v8::Local<v8::Function> foo = |
| CompileFunction(&env, "function foo() { return 1 }", "foo"); |
| // Run without breakpoints. |
| CompileRun("foo()"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint |
| i::Handle<i::BreakPoint> bp = SetBreakPoint(foo, 0, "a == true"); |
| CompileRun("foo()"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| CompileRun("a = true"); |
| CompileRun("foo()"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| CompileRun("foo()"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| // Test that a break point can be set at an IC load location. |
| TEST(BreakPointICLoad) { |
| break_point_hit_count = 0; |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| CompileRunChecked(env->GetIsolate(), "bar=1"); |
| v8::Local<v8::Function> foo = |
| CompileFunction(&env, "function foo(){var x=bar;}", "foo"); |
| |
| // Run without breakpoints. |
| foo->Call(env.local(), env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint. |
| i::Handle<i::BreakPoint> bp = SetBreakPoint(foo, 0); |
| foo->Call(env.local(), env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(1, break_point_hit_count); |
| foo->Call(env.local(), env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| foo->Call(env.local(), env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| // Test that a break point can be set at an IC call location. |
| TEST(BreakPointICCall) { |
| break_point_hit_count = 0; |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| CompileRunChecked(env->GetIsolate(), "function bar(){}"); |
| v8::Local<v8::Function> foo = |
| CompileFunction(&env, "function foo(){bar();}", "foo"); |
| |
| // Run without breakpoints. |
| foo->Call(env.local(), env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint |
| i::Handle<i::BreakPoint> bp = SetBreakPoint(foo, 0); |
| foo->Call(env.local(), env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(1, break_point_hit_count); |
| foo->Call(env.local(), env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| foo->Call(env.local(), env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| // Test that a break point can be set at an IC call location and survive a GC. |
| TEST(BreakPointICCallWithGC) { |
| break_point_hit_count = 0; |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| DebugEventBreakPointCollectGarbage delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| CompileRunChecked(env->GetIsolate(), "function bar(){return 1;}"); |
| v8::Local<v8::Function> foo = |
| CompileFunction(&env, "function foo(){return bar();}", "foo"); |
| v8::Local<v8::Context> context = env.local(); |
| |
| // Run without breakpoints. |
| CHECK_EQ(1, foo->Call(context, env->Global(), 0, nullptr) |
| .ToLocalChecked() |
| ->Int32Value(context) |
| .FromJust()); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint. |
| i::Handle<i::BreakPoint> bp = SetBreakPoint(foo, 0); |
| CHECK_EQ(1, foo->Call(context, env->Global(), 0, nullptr) |
| .ToLocalChecked() |
| ->Int32Value(context) |
| .FromJust()); |
| CHECK_EQ(1, break_point_hit_count); |
| CHECK_EQ(1, foo->Call(context, env->Global(), 0, nullptr) |
| .ToLocalChecked() |
| ->Int32Value(context) |
| .FromJust()); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| // Test that a break point can be set at an IC call location and survive a GC. |
| TEST(BreakPointConstructCallWithGC) { |
| break_point_hit_count = 0; |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| DebugEventBreakPointCollectGarbage delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| CompileRunChecked(env->GetIsolate(), "function bar(){ this.x = 1;}"); |
| v8::Local<v8::Function> foo = |
| CompileFunction(&env, "function foo(){return new bar(1).x;}", "foo"); |
| v8::Local<v8::Context> context = env.local(); |
| |
| // Run without breakpoints. |
| CHECK_EQ(1, foo->Call(context, env->Global(), 0, nullptr) |
| .ToLocalChecked() |
| ->Int32Value(context) |
| .FromJust()); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint. |
| i::Handle<i::BreakPoint> bp = SetBreakPoint(foo, 0); |
| CHECK_EQ(1, foo->Call(context, env->Global(), 0, nullptr) |
| .ToLocalChecked() |
| ->Int32Value(context) |
| .FromJust()); |
| CHECK_EQ(1, break_point_hit_count); |
| CHECK_EQ(1, foo->Call(context, env->Global(), 0, nullptr) |
| .ToLocalChecked() |
| ->Int32Value(context) |
| .FromJust()); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| TEST(BreakPointBuiltin) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| v8::Local<v8::Function> builtin; |
| i::Handle<i::BreakPoint> bp; |
| |
| // === Test simple builtin === |
| break_point_hit_count = 0; |
| builtin = CompileRun("String.prototype.repeat").As<v8::Function>(); |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(builtin, 0, "this != 1"); |
| ExpectString("'b'.repeat(10)", "bbbbbbbbbb"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| ExpectString("'b'.repeat(10)", "bbbbbbbbbb"); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| ExpectString("'b'.repeat(10)", "bbbbbbbbbb"); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| TEST(BreakPointApiIntrinsics) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| v8::Local<v8::Function> builtin; |
| |
| // === Test that using API-exposed functions won't trigger breakpoints === |
| { |
| v8::Local<v8::Function> weakmap_get = |
| CompileRun("WeakMap.prototype.get").As<v8::Function>(); |
| SetBreakPoint(weakmap_get, 0); |
| v8::Local<v8::Function> weakmap_set = |
| CompileRun("WeakMap.prototype.set").As<v8::Function>(); |
| SetBreakPoint(weakmap_set, 0); |
| |
| // Run with breakpoint. |
| break_point_hit_count = 0; |
| CompileRun("var w = new WeakMap(); w.set(w, 1); w.get(w);"); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| break_point_hit_count = 0; |
| v8::Local<v8::debug::WeakMap> weakmap = |
| v8::debug::WeakMap::New(env->GetIsolate()); |
| CHECK(!weakmap->Set(env.local(), weakmap, v8_num(1)).IsEmpty()); |
| CHECK(!weakmap->Get(env.local(), weakmap).IsEmpty()); |
| CHECK_EQ(0, break_point_hit_count); |
| } |
| |
| { |
| v8::Local<v8::Function> object_to_string = |
| CompileRun("Object.prototype.toString").As<v8::Function>(); |
| SetBreakPoint(object_to_string, 0); |
| |
| // Run with breakpoint. |
| break_point_hit_count = 0; |
| CompileRun("var o = {}; o.toString();"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| break_point_hit_count = 0; |
| v8::Local<v8::Object> object = v8::Object::New(env->GetIsolate()); |
| CHECK(!object->ObjectProtoToString(env.local()).IsEmpty()); |
| CHECK_EQ(0, break_point_hit_count); |
| } |
| |
| { |
| v8::Local<v8::Function> map_set = |
| CompileRun("Map.prototype.set").As<v8::Function>(); |
| v8::Local<v8::Function> map_get = |
| CompileRun("Map.prototype.get").As<v8::Function>(); |
| v8::Local<v8::Function> map_has = |
| CompileRun("Map.prototype.has").As<v8::Function>(); |
| v8::Local<v8::Function> map_delete = |
| CompileRun("Map.prototype.delete").As<v8::Function>(); |
| SetBreakPoint(map_set, 0); |
| SetBreakPoint(map_get, 0); |
| SetBreakPoint(map_has, 0); |
| SetBreakPoint(map_delete, 0); |
| |
| // Run with breakpoint. |
| break_point_hit_count = 0; |
| CompileRun( |
| "var m = new Map(); m.set(m, 1); m.get(m); m.has(m); m.delete(m);"); |
| CHECK_EQ(4, break_point_hit_count); |
| |
| break_point_hit_count = 0; |
| v8::Local<v8::Map> map = v8::Map::New(env->GetIsolate()); |
| CHECK(!map->Set(env.local(), map, v8_num(1)).IsEmpty()); |
| CHECK(!map->Get(env.local(), map).IsEmpty()); |
| CHECK(map->Has(env.local(), map).FromJust()); |
| CHECK(map->Delete(env.local(), map).FromJust()); |
| CHECK_EQ(0, break_point_hit_count); |
| } |
| |
| { |
| v8::Local<v8::Function> set_add = |
| CompileRun("Set.prototype.add").As<v8::Function>(); |
| v8::Local<v8::Function> set_get = |
| CompileRun("Set.prototype.has").As<v8::Function>(); |
| v8::Local<v8::Function> set_delete = |
| CompileRun("Set.prototype.delete").As<v8::Function>(); |
| SetBreakPoint(set_add, 0); |
| SetBreakPoint(set_get, 0); |
| SetBreakPoint(set_delete, 0); |
| |
| // Run with breakpoint. |
| break_point_hit_count = 0; |
| CompileRun("var s = new Set(); s.add(s); s.has(s); s.delete(s);"); |
| CHECK_EQ(3, break_point_hit_count); |
| |
| break_point_hit_count = 0; |
| v8::Local<v8::Set> set = v8::Set::New(env->GetIsolate()); |
| CHECK(!set->Add(env.local(), set).IsEmpty()); |
| CHECK(set->Has(env.local(), set).FromJust()); |
| CHECK(set->Delete(env.local(), set).FromJust()); |
| CHECK_EQ(0, break_point_hit_count); |
| } |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| TEST(BreakPointJSBuiltin) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| v8::Local<v8::Function> builtin; |
| i::Handle<i::BreakPoint> bp; |
| |
| // === Test JS builtin === |
| break_point_hit_count = 0; |
| builtin = CompileRun("Array.prototype.sort").As<v8::Function>(); |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(builtin, 0); |
| CompileRun("[1,2,3].sort()"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| CompileRun("[1,2,3].sort()"); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| CompileRun("[1,2,3].sort()"); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| TEST(BreakPointBoundBuiltin) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| v8::Local<v8::Function> builtin; |
| i::Handle<i::BreakPoint> bp; |
| |
| // === Test bound function from a builtin === |
| break_point_hit_count = 0; |
| builtin = CompileRun( |
| "var boundrepeat = String.prototype.repeat.bind('a');" |
| "String.prototype.repeat") |
| .As<v8::Function>(); |
| ExpectString("boundrepeat(10)", "aaaaaaaaaa"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(builtin, 0); |
| ExpectString("boundrepeat(10)", "aaaaaaaaaa"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| ExpectString("boundrepeat(10)", "aaaaaaaaaa"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| TEST(BreakPointConstructorBuiltin) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| v8::Local<v8::Function> builtin; |
| i::Handle<i::BreakPoint> bp; |
| |
| // === Test Promise constructor === |
| break_point_hit_count = 0; |
| builtin = CompileRun("Promise").As<v8::Function>(); |
| ExpectString("(new Promise(()=>{})).toString()", "[object Promise]"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(builtin, 0, "this != 1"); |
| ExpectString("(new Promise(()=>{})).toString()", "[object Promise]"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| ExpectString("(new Promise(()=>{})).toString()", "[object Promise]"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // === Test Object constructor === |
| break_point_hit_count = 0; |
| builtin = CompileRun("Object").As<v8::Function>(); |
| CompileRun("new Object()"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(builtin, 0); |
| CompileRun("new Object()"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| CompileRun("new Object()"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // === Test Number constructor === |
| break_point_hit_count = 0; |
| builtin = CompileRun("Number").As<v8::Function>(); |
| CompileRun("new Number()"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(builtin, 0); |
| CompileRun("new Number()"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| CompileRun("new Number()"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| TEST(BreakPointInlinedBuiltin) { |
| i::FLAG_allow_natives_syntax = true; |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| v8::Local<v8::Function> builtin; |
| i::Handle<i::BreakPoint> bp; |
| |
| // === Test inlined builtin === |
| break_point_hit_count = 0; |
| builtin = CompileRun("Math.sin").As<v8::Function>(); |
| CompileRun("function test(x) { return 1 + Math.sin(x) }"); |
| CompileRun( |
| "%PrepareFunctionForOptimization(test);" |
| "test(0.5); test(0.6);" |
| "%OptimizeFunctionOnNextCall(test); test(0.7);"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(builtin, 0, "this != 1"); |
| CompileRun("Math.sin(0.1);"); |
| CHECK_EQ(1, break_point_hit_count); |
| CompileRun("test(0.2);"); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| // Re-optimize. |
| CompileRun( |
| "%PrepareFunctionForOptimization(test);" |
| "%OptimizeFunctionOnNextCall(test);"); |
| ExpectBoolean("test(0.3) < 2", true); |
| CHECK_EQ(3, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| CompileRun("test(0.3);"); |
| CHECK_EQ(3, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| TEST(BreakPointInlineBoundBuiltin) { |
| i::FLAG_allow_natives_syntax = true; |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| v8::Local<v8::Function> builtin; |
| i::Handle<i::BreakPoint> bp; |
| |
| // === Test inlined bound builtin === |
| break_point_hit_count = 0; |
| |
| builtin = CompileRun( |
| "var boundrepeat = String.prototype.repeat.bind('a');" |
| "String.prototype.repeat") |
| .As<v8::Function>(); |
| CompileRun("function test(x) { return 'a' + boundrepeat(x) }"); |
| CompileRun( |
| "%PrepareFunctionForOptimization(test);" |
| "test(4); test(5);" |
| "%OptimizeFunctionOnNextCall(test); test(6);"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(builtin, 0, "this != 1"); |
| CompileRun("'a'.repeat(2);"); |
| CHECK_EQ(1, break_point_hit_count); |
| CompileRun("test(7);"); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| // Re-optimize. |
| CompileRun( |
| "%PrepareFunctionForOptimization(f);" |
| "%OptimizeFunctionOnNextCall(test);"); |
| CompileRun("test(8);"); |
| CHECK_EQ(3, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| CompileRun("test(9);"); |
| CHECK_EQ(3, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| TEST(BreakPointInlinedConstructorBuiltin) { |
| i::FLAG_allow_natives_syntax = true; |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| v8::Local<v8::Function> builtin; |
| i::Handle<i::BreakPoint> bp; |
| |
| // === Test inlined constructor builtin (regular construct builtin) === |
| break_point_hit_count = 0; |
| builtin = CompileRun("Promise").As<v8::Function>(); |
| CompileRun("function test(x) { return new Promise(()=>x); }"); |
| CompileRun( |
| "%PrepareFunctionForOptimization(test);" |
| "test(4); test(5);" |
| "%OptimizeFunctionOnNextCall(test); test(6);"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(builtin, 0, "this != 1"); |
| CompileRun("new Promise(()=>{});"); |
| CHECK_EQ(1, break_point_hit_count); |
| CompileRun("test(7);"); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| // Re-optimize. |
| CompileRun( |
| "%PrepareFunctionForOptimization(f);" |
| "%OptimizeFunctionOnNextCall(test);"); |
| CompileRun("test(8);"); |
| CHECK_EQ(3, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| CompileRun("test(9);"); |
| CHECK_EQ(3, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| TEST(BreakPointBuiltinConcurrentOpt) { |
| i::FLAG_allow_natives_syntax = true; |
| i::FLAG_block_concurrent_recompilation = true; |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| v8::Local<v8::Function> builtin; |
| i::Handle<i::BreakPoint> bp; |
| |
| // === Test concurrent optimization === |
| break_point_hit_count = 0; |
| builtin = CompileRun("Math.sin").As<v8::Function>(); |
| CompileRun("function test(x) { return 1 + Math.sin(x) }"); |
| // Trigger concurrent compile job. It is suspended until unblock. |
| CompileRun( |
| "%PrepareFunctionForOptimization(test);" |
| "test(0.5); test(0.6);" |
| "%OptimizeFunctionOnNextCall(test, 'concurrent'); test(0.7);"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(builtin, 0); |
| // Have the concurrent compile job finish now. |
| CompileRun( |
| "%UnblockConcurrentRecompilation();" |
| "%GetOptimizationStatus(test, 'sync');"); |
| CompileRun("test(0.2);"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| CompileRun("test(0.3);"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| TEST(BreakPointBuiltinTFOperator) { |
| i::FLAG_allow_natives_syntax = true; |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| v8::Local<v8::Function> builtin; |
| i::Handle<i::BreakPoint> bp; |
| |
| // === Test builtin represented as operator === |
| break_point_hit_count = 0; |
| builtin = CompileRun("String.prototype.indexOf").As<v8::Function>(); |
| CompileRun("function test(x) { return 1 + 'foo'.indexOf(x) }"); |
| CompileRun( |
| "%PrepareFunctionForOptimization(f);" |
| "test('a'); test('b');" |
| "%OptimizeFunctionOnNextCall(test); test('c');"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(builtin, 0); |
| CompileRun("'bar'.indexOf('x');"); |
| CHECK_EQ(1, break_point_hit_count); |
| CompileRun("test('d');"); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| // Re-optimize. |
| CompileRun( |
| "%PrepareFunctionForOptimization(f);" |
| "%OptimizeFunctionOnNextCall(test);"); |
| CompileRun("test('e');"); |
| CHECK_EQ(3, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| CompileRun("test('f');"); |
| CHECK_EQ(3, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| TEST(BreakPointBuiltinNewContext) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| v8::Local<v8::Function> builtin; |
| i::Handle<i::BreakPoint> bp; |
| |
| // === Test builtin from a new context === |
| break_point_hit_count = 0; |
| builtin = CompileRun("String.prototype.repeat").As<v8::Function>(); |
| CompileRun("'a'.repeat(10)"); |
| CHECK_EQ(0, break_point_hit_count); |
| // Set breakpoint. |
| bp = SetBreakPoint(builtin, 0); |
| |
| { |
| // Create and use new context after breakpoint has been set. |
| v8::HandleScope handle_scope(env->GetIsolate()); |
| v8::Local<v8::Context> new_context = v8::Context::New(env->GetIsolate()); |
| v8::Context::Scope context_scope(new_context); |
| |
| // Run with breakpoint. |
| CompileRun("'b'.repeat(10)"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| CompileRun("'b'.repeat(10)"); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| CompileRun("'b'.repeat(10)"); |
| CHECK_EQ(2, break_point_hit_count); |
| } |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| void NoOpFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| args.GetReturnValue().Set(v8_num(2)); |
| } |
| |
| TEST(BreakPointApiFunction) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| i::Handle<i::BreakPoint> bp; |
| |
| v8::Local<v8::FunctionTemplate> function_template = |
| v8::FunctionTemplate::New(env->GetIsolate(), NoOpFunctionCallback); |
| |
| v8::Local<v8::Function> function = |
| function_template->GetFunction(env.local()).ToLocalChecked(); |
| |
| env->Global()->Set(env.local(), v8_str("f"), function).ToChecked(); |
| |
| // === Test simple builtin === |
| break_point_hit_count = 0; |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(function, 0, "this != 1"); |
| ExpectInt32("f()", 2); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| ExpectInt32("f()", 2); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| // Direct call through API does not trigger breakpoint. |
| function->Call(env.local(), v8::Undefined(env->GetIsolate()), 0, nullptr) |
| .ToLocalChecked(); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| ExpectInt32("f()", 2); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| TEST(BreakPointApiConstructor) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| i::Handle<i::BreakPoint> bp; |
| |
| v8::Local<v8::FunctionTemplate> function_template = |
| v8::FunctionTemplate::New(env->GetIsolate(), NoOpFunctionCallback); |
| |
| v8::Local<v8::Function> function = |
| function_template->GetFunction(env.local()).ToLocalChecked(); |
| |
| env->Global()->Set(env.local(), v8_str("f"), function).ToChecked(); |
| |
| // === Test simple builtin === |
| break_point_hit_count = 0; |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(function, 0, "this != 1"); |
| CompileRun("new f()"); |
| CHECK_EQ(1, break_point_hit_count); |
| CompileRun("new f()"); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| // Direct call through API does not trigger breakpoint. |
| function->NewInstance(env.local()).ToLocalChecked(); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| CompileRun("new f()"); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| void GetWrapperCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| args.GetReturnValue().Set( |
| args[0] |
| .As<v8::Object>() |
| ->Get(args.GetIsolate()->GetCurrentContext(), args[1]) |
| .ToLocalChecked()); |
| } |
| |
| TEST(BreakPointApiGetter) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| i::Handle<i::BreakPoint> bp; |
| |
| v8::Local<v8::FunctionTemplate> function_template = |
| v8::FunctionTemplate::New(env->GetIsolate(), NoOpFunctionCallback); |
| v8::Local<v8::FunctionTemplate> get_template = |
| v8::FunctionTemplate::New(env->GetIsolate(), GetWrapperCallback); |
| |
| v8::Local<v8::Function> function = |
| function_template->GetFunction(env.local()).ToLocalChecked(); |
| v8::Local<v8::Function> get = |
| get_template->GetFunction(env.local()).ToLocalChecked(); |
| |
| env->Global()->Set(env.local(), v8_str("f"), function).ToChecked(); |
| env->Global()->Set(env.local(), v8_str("get_wrapper"), get).ToChecked(); |
| CompileRun( |
| "var o = {};" |
| "Object.defineProperty(o, 'f', { get: f, enumerable: true });"); |
| |
| // === Test API builtin as getter === |
| break_point_hit_count = 0; |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(function, 0); |
| CompileRun("get_wrapper(o, 'f')"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| CompileRun("o.f"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| CompileRun("get_wrapper(o, 'f', 2)"); |
| CompileRun("o.f"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| void SetWrapperCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { |
| CHECK(args[0] |
| .As<v8::Object>() |
| ->Set(args.GetIsolate()->GetCurrentContext(), args[1], args[2]) |
| .FromJust()); |
| } |
| |
| TEST(BreakPointApiSetter) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| i::Handle<i::BreakPoint> bp; |
| |
| v8::Local<v8::FunctionTemplate> function_template = |
| v8::FunctionTemplate::New(env->GetIsolate(), NoOpFunctionCallback); |
| v8::Local<v8::FunctionTemplate> set_template = |
| v8::FunctionTemplate::New(env->GetIsolate(), SetWrapperCallback); |
| |
| v8::Local<v8::Function> function = |
| function_template->GetFunction(env.local()).ToLocalChecked(); |
| v8::Local<v8::Function> set = |
| set_template->GetFunction(env.local()).ToLocalChecked(); |
| |
| env->Global()->Set(env.local(), v8_str("f"), function).ToChecked(); |
| env->Global()->Set(env.local(), v8_str("set_wrapper"), set).ToChecked(); |
| |
| CompileRun( |
| "var o = {};" |
| "Object.defineProperty(o, 'f', { set: f, enumerable: true });"); |
| |
| // === Test API builtin as setter === |
| break_point_hit_count = 0; |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(function, 0); |
| |
| CompileRun("o.f = 3"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| CompileRun("set_wrapper(o, 'f', 2)"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| CompileRun("o.f = 3"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // === Test API builtin as setter, with condition === |
| break_point_hit_count = 0; |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(function, 0, "arguments[0] == 3"); |
| CompileRun("set_wrapper(o, 'f', 2)"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| CompileRun("set_wrapper(o, 'f', 3)"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| CompileRun("o.f = 3"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| CompileRun("set_wrapper(o, 'f', 2)"); |
| CompileRun("o.f = 3"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| TEST(BreakPointApiAccessor) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| i::Handle<i::BreakPoint> bp; |
| |
| // Create 'foo' class, with a hidden property. |
| v8::Local<v8::ObjectTemplate> obj_template = |
| v8::ObjectTemplate::New(env->GetIsolate()); |
| v8::Local<v8::FunctionTemplate> accessor_template = |
| v8::FunctionTemplate::New(env->GetIsolate(), NoOpFunctionCallback); |
| obj_template->SetAccessorProperty(v8_str("f"), accessor_template, |
| accessor_template); |
| v8::Local<v8::Object> obj = |
| obj_template->NewInstance(env.local()).ToLocalChecked(); |
| env->Global()->Set(env.local(), v8_str("o"), obj).ToChecked(); |
| |
| v8::Local<v8::Function> function = |
| CompileRun("Object.getOwnPropertyDescriptor(o, 'f').set") |
| .As<v8::Function>(); |
| |
| // === Test API accessor === |
| break_point_hit_count = 0; |
| |
| CompileRun("function get_loop() { for (var i = 0; i < 10; i++) o.f }"); |
| CompileRun("function set_loop() { for (var i = 0; i < 10; i++) o.f = 2 }"); |
| |
| CompileRun("get_loop(); set_loop();"); // Initialize ICs. |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(function, 0); |
| |
| CompileRun("o.f = 3"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| CompileRun("o.f"); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| CompileRun("for (var i = 0; i < 10; i++) o.f"); |
| CHECK_EQ(12, break_point_hit_count); |
| |
| CompileRun("get_loop();"); |
| CHECK_EQ(22, break_point_hit_count); |
| |
| CompileRun("for (var i = 0; i < 10; i++) o.f = 2"); |
| CHECK_EQ(32, break_point_hit_count); |
| |
| CompileRun("set_loop();"); |
| CHECK_EQ(42, break_point_hit_count); |
| |
| // Test that the break point also works when we install the function |
| // template on a new property (with a fresh AccessorPair instance). |
| v8::Local<v8::ObjectTemplate> baz_template = |
| v8::ObjectTemplate::New(env->GetIsolate()); |
| baz_template->SetAccessorProperty(v8_str("g"), accessor_template, |
| accessor_template); |
| v8::Local<v8::Object> baz = |
| baz_template->NewInstance(env.local()).ToLocalChecked(); |
| env->Global()->Set(env.local(), v8_str("b"), baz).ToChecked(); |
| |
| CompileRun("b.g = 4"); |
| CHECK_EQ(43, break_point_hit_count); |
| |
| CompileRun("b.g"); |
| CHECK_EQ(44, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| CompileRun("o.f = 3"); |
| CompileRun("o.f"); |
| CHECK_EQ(44, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| TEST(BreakPointInlineApiFunction) { |
| i::FLAG_allow_natives_syntax = true; |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| i::Handle<i::BreakPoint> bp; |
| |
| v8::Local<v8::FunctionTemplate> function_template = |
| v8::FunctionTemplate::New(env->GetIsolate(), NoOpFunctionCallback); |
| |
| v8::Local<v8::Function> function = |
| function_template->GetFunction(env.local()).ToLocalChecked(); |
| |
| env->Global()->Set(env.local(), v8_str("f"), function).ToChecked(); |
| CompileRun( |
| "function g() { return 1 + f(); };" |
| "%PrepareFunctionForOptimization(g);"); |
| |
| // === Test simple builtin === |
| break_point_hit_count = 0; |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(function, 0); |
| ExpectInt32("g()", 3); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| ExpectInt32("g()", 3); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| CompileRun("%OptimizeFunctionOnNextCall(g)"); |
| ExpectInt32("g()", 3); |
| CHECK_EQ(3, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| ExpectInt32("g()", 3); |
| CHECK_EQ(3, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| // Test that a break point can be set at a return store location. |
| TEST(BreakPointConditionBuiltin) { |
| i::FLAG_allow_natives_syntax = true; |
| i::FLAG_block_concurrent_recompilation = true; |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| v8::Local<v8::Function> builtin; |
| i::Handle<i::BreakPoint> bp; |
| |
| // === Test global variable === |
| break_point_hit_count = 0; |
| builtin = CompileRun("String.prototype.repeat").As<v8::Function>(); |
| CompileRun("var condition = false"); |
| CompileRun("'a'.repeat(10)"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(builtin, 0, "condition == true"); |
| CompileRun("'b'.repeat(10)"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| CompileRun("condition = true"); |
| CompileRun("'b'.repeat(10)"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| CompileRun("'b'.repeat(10)"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // === Test arguments === |
| break_point_hit_count = 0; |
| builtin = CompileRun("String.prototype.repeat").As<v8::Function>(); |
| CompileRun("function f(x) { return 'a'.repeat(x * 2); }"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(builtin, 0, "arguments[0] == 20"); |
| ExpectString("f(5)", "aaaaaaaaaa"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| ExpectString("f(10)", "aaaaaaaaaaaaaaaaaaaa"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| ExpectString("f(10)", "aaaaaaaaaaaaaaaaaaaa"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // === Test adapted arguments === |
| break_point_hit_count = 0; |
| builtin = CompileRun("String.prototype.repeat").As<v8::Function>(); |
| CompileRun("function f(x) { return 'a'.repeat(x * 2, x); }"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(builtin, 0, |
| "arguments[1] == 10 && arguments[2] == undefined"); |
| ExpectString("f(5)", "aaaaaaaaaa"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| ExpectString("f(10)", "aaaaaaaaaaaaaaaaaaaa"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| ExpectString("f(10)", "aaaaaaaaaaaaaaaaaaaa"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // === Test var-arg builtins === |
| break_point_hit_count = 0; |
| builtin = CompileRun("String.fromCharCode").As<v8::Function>(); |
| CompileRun("function f() { return String.fromCharCode(1, 2, 3); }"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(builtin, 0, "arguments.length == 3 && arguments[1] == 2"); |
| CompileRun("f(1, 2, 3)"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| CompileRun("f(1, 2, 3)"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // === Test rest arguments === |
| break_point_hit_count = 0; |
| builtin = CompileRun("String.fromCharCode").As<v8::Function>(); |
| CompileRun("function f(...args) { return String.fromCharCode(...args); }"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(builtin, 0, "arguments.length == 3 && arguments[1] == 2"); |
| CompileRun("f(1, 2, 3)"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| ClearBreakPoint(bp); |
| CompileRun("f(1, 3, 3)"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| CompileRun("f(1, 2, 3)"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // === Test receiver === |
| break_point_hit_count = 0; |
| builtin = CompileRun("String.prototype.repeat").As<v8::Function>(); |
| CompileRun("function f(x) { return x.repeat(10); }"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint. |
| bp = SetBreakPoint(builtin, 0, "this == 'a'"); |
| ExpectString("f('b')", "bbbbbbbbbb"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| ExpectString("f('a')", "aaaaaaaaaa"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| ExpectString("f('a')", "aaaaaaaaaa"); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| TEST(BreakPointInlining) { |
| i::FLAG_allow_natives_syntax = true; |
| break_point_hit_count = 0; |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| break_point_hit_count = 0; |
| v8::Local<v8::Function> inlinee = |
| CompileRun("function f(x) { return x*2; } f").As<v8::Function>(); |
| CompileRun("function test(x) { return 1 + f(x) }"); |
| CompileRun( |
| "%PrepareFunctionForOptimization(test);" |
| "test(0.5); test(0.6);" |
| "%OptimizeFunctionOnNextCall(test); test(0.7);"); |
| CHECK_EQ(0, break_point_hit_count); |
| |
| // Run with breakpoint. |
| i::Handle<i::BreakPoint> bp = SetBreakPoint(inlinee, 0); |
| CompileRun("f(0.1);"); |
| CHECK_EQ(1, break_point_hit_count); |
| CompileRun("test(0.2);"); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| // Re-optimize. |
| CompileRun( |
| "%PrepareFunctionForOptimization(test);" |
| "%OptimizeFunctionOnNextCall(test);"); |
| CompileRun("test(0.3);"); |
| CHECK_EQ(3, break_point_hit_count); |
| |
| // Run without breakpoints. |
| ClearBreakPoint(bp); |
| CompileRun("test(0.3);"); |
| CHECK_EQ(3, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| static void CallWithBreakPoints(v8::Local<v8::Context> context, |
| v8::Local<v8::Object> recv, |
| v8::Local<v8::Function> f, |
| int break_point_count, int call_count) { |
| break_point_hit_count = 0; |
| for (int i = 0; i < call_count; i++) { |
| f->Call(context, recv, 0, nullptr).ToLocalChecked(); |
| CHECK_EQ((i + 1) * break_point_count, break_point_hit_count); |
| } |
| } |
| |
| |
| // Test GC during break point processing. |
| TEST(GCDuringBreakPointProcessing) { |
| break_point_hit_count = 0; |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Local<v8::Context> context = env.local(); |
| |
| DebugEventBreakPointCollectGarbage delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| v8::Local<v8::Function> foo; |
| |
| // Test IC store break point with garbage collection. |
| foo = CompileFunction(&env, "function foo(){bar=0;}", "foo"); |
| SetBreakPoint(foo, 0); |
| CallWithBreakPoints(context, env->Global(), foo, 1, 10); |
| |
| // Test IC load break point with garbage collection. |
| foo = CompileFunction(&env, "bar=1;function foo(){var x=bar;}", "foo"); |
| SetBreakPoint(foo, 0); |
| CallWithBreakPoints(context, env->Global(), foo, 1, 10); |
| |
| // Test IC call break point with garbage collection. |
| foo = CompileFunction(&env, "function bar(){};function foo(){bar();}", "foo"); |
| SetBreakPoint(foo, 0); |
| CallWithBreakPoints(context, env->Global(), foo, 1, 10); |
| |
| // Test return break point with garbage collection. |
| foo = CompileFunction(&env, "function foo(){}", "foo"); |
| SetBreakPoint(foo, 0); |
| CallWithBreakPoints(context, env->Global(), foo, 1, 25); |
| |
| // Test debug break slot break point with garbage collection. |
| foo = CompileFunction(&env, "function foo(){var a;}", "foo"); |
| SetBreakPoint(foo, 0); |
| CallWithBreakPoints(context, env->Global(), foo, 1, 25); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| // Call the function three times with different garbage collections in between |
| // and make sure that the break point survives. |
| static void CallAndGC(v8::Local<v8::Context> context, |
| v8::Local<v8::Object> recv, v8::Local<v8::Function> f) { |
| break_point_hit_count = 0; |
| |
| for (int i = 0; i < 3; i++) { |
| // Call function. |
| f->Call(context, recv, 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(1 + i * 3, break_point_hit_count); |
| |
| // Scavenge and call function. |
| CcTest::CollectGarbage(v8::internal::NEW_SPACE); |
| f->Call(context, recv, 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(2 + i * 3, break_point_hit_count); |
| |
| // Mark sweep (and perhaps compact) and call function. |
| CcTest::CollectAllGarbage(); |
| f->Call(context, recv, 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(3 + i * 3, break_point_hit_count); |
| } |
| } |
| |
| |
| // Test that a break point can be set at a return store location. |
| TEST(BreakPointSurviveGC) { |
| break_point_hit_count = 0; |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Local<v8::Context> context = env.local(); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| v8::Local<v8::Function> foo; |
| |
| // Test IC store break point with garbage collection. |
| { |
| CompileFunction(&env, "function foo(){}", "foo"); |
| foo = CompileFunction(&env, "function foo(){bar=0;}", "foo"); |
| SetBreakPoint(foo, 0); |
| } |
| CallAndGC(context, env->Global(), foo); |
| |
| // Test IC load break point with garbage collection. |
| { |
| CompileFunction(&env, "function foo(){}", "foo"); |
| foo = CompileFunction(&env, "bar=1;function foo(){var x=bar;}", "foo"); |
| SetBreakPoint(foo, 0); |
| } |
| CallAndGC(context, env->Global(), foo); |
| |
| // Test IC call break point with garbage collection. |
| { |
| CompileFunction(&env, "function foo(){}", "foo"); |
| foo = CompileFunction(&env, |
| "function bar(){};function foo(){bar();}", |
| "foo"); |
| SetBreakPoint(foo, 0); |
| } |
| CallAndGC(context, env->Global(), foo); |
| |
| // Test return break point with garbage collection. |
| { |
| CompileFunction(&env, "function foo(){}", "foo"); |
| foo = CompileFunction(&env, "function foo(){}", "foo"); |
| SetBreakPoint(foo, 0); |
| } |
| CallAndGC(context, env->Global(), foo); |
| |
| // Test non IC break point with garbage collection. |
| { |
| CompileFunction(&env, "function foo(){}", "foo"); |
| foo = CompileFunction(&env, "function foo(){var bar=0;}", "foo"); |
| SetBreakPoint(foo, 0); |
| } |
| CallAndGC(context, env->Global(), foo); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| // Test that the debugger statement causes a break. |
| TEST(DebuggerStatement) { |
| break_point_hit_count = 0; |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| v8::Local<v8::Context> context = env.local(); |
| v8::Script::Compile(context, |
| v8_str(env->GetIsolate(), "function bar(){debugger}")) |
| .ToLocalChecked() |
| ->Run(context) |
| .ToLocalChecked(); |
| v8::Script::Compile( |
| context, v8_str(env->GetIsolate(), "function foo(){debugger;debugger;}")) |
| .ToLocalChecked() |
| ->Run(context) |
| .ToLocalChecked(); |
| v8::Local<v8::Function> foo = v8::Local<v8::Function>::Cast( |
| env->Global() |
| ->Get(context, v8_str(env->GetIsolate(), "foo")) |
| .ToLocalChecked()); |
| v8::Local<v8::Function> bar = v8::Local<v8::Function>::Cast( |
| env->Global() |
| ->Get(context, v8_str(env->GetIsolate(), "bar")) |
| .ToLocalChecked()); |
| |
| // Run function with debugger statement |
| bar->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // Run function with two debugger statement |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(3, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| // Test setting a breakpoint on the debugger statement. |
| TEST(DebuggerStatementBreakpoint) { |
| break_point_hit_count = 0; |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| v8::Local<v8::Context> context = env.local(); |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| v8::Script::Compile(context, |
| v8_str(env->GetIsolate(), "function foo(){debugger;}")) |
| .ToLocalChecked() |
| ->Run(context) |
| .ToLocalChecked(); |
| v8::Local<v8::Function> foo = v8::Local<v8::Function>::Cast( |
| env->Global() |
| ->Get(context, v8_str(env->GetIsolate(), "foo")) |
| .ToLocalChecked()); |
| |
| // The debugger statement triggers breakpoint hit |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| i::Handle<i::BreakPoint> bp = SetBreakPoint(foo, 0); |
| |
| // Set breakpoint does not duplicate hits |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| ClearBreakPoint(bp); |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| // Test that the conditional breakpoints work event if code generation from |
| // strings is prohibited in the debugee context. |
| TEST(ConditionalBreakpointWithCodeGenerationDisallowed) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| v8::Local<v8::Context> context = env.local(); |
| v8::Local<v8::Function> foo = CompileFunction(&env, |
| "function foo(x) {\n" |
| " var s = 'String value2';\n" |
| " return s + x;\n" |
| "}", |
| "foo"); |
| |
| // Set conditional breakpoint with condition 'true'. |
| SetBreakPoint(foo, 4, "true"); |
| |
| break_point_hit_count = 0; |
| env->AllowCodeGenerationFromStrings(false); |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| // Simple test of the stepping mechanism using only store ICs. |
| TEST(DebugStepLinear) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| // Create a function for testing stepping. |
| v8::Local<v8::Function> foo = CompileFunction(&env, |
| "function foo(){a=1;b=1;c=1;}", |
| "foo"); |
| |
| // Run foo to allow it to get optimized. |
| CompileRun("a=0; b=0; c=0; foo();"); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| |
| SetBreakPoint(foo, 3); |
| |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| v8::Local<v8::Context> context = env.local(); |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| |
| // With stepping all break locations are hit. |
| CHECK_EQ(4, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| |
| // Register a debug event listener which just counts. |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| SetBreakPoint(foo, 3); |
| break_point_hit_count = 0; |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| |
| // Without stepping only active break points are hit. |
| CHECK_EQ(1, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| // Test of the stepping mechanism for keyed load in a loop. |
| TEST(DebugStepKeyedLoadLoop) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| |
| // Create a function for testing stepping of keyed load. The statement 'y=1' |
| // is there to have more than one breakable statement in the loop, TODO(315). |
| v8::Local<v8::Function> foo = CompileFunction( |
| &env, |
| "function foo(a) {\n" |
| " var x;\n" |
| " var len = a.length;\n" |
| " for (var i = 0; i < len; i++) {\n" |
| " y = 1;\n" |
| " x = a[i];\n" |
| " }\n" |
| "}\n" |
| "y=0\n", |
| "foo"); |
| |
| v8::Local<v8::Context> context = env.local(); |
| // Create array [0,1,2,3,4,5,6,7,8,9] |
| v8::Local<v8::Array> a = v8::Array::New(env->GetIsolate(), 10); |
| for (int i = 0; i < 10; i++) { |
| CHECK(a->Set(context, v8::Number::New(env->GetIsolate(), i), |
| v8::Number::New(env->GetIsolate(), i)) |
| .FromJust()); |
| } |
| |
| // Call function without any break points to ensure inlining is in place. |
| const int kArgc = 1; |
| v8::Local<v8::Value> args[kArgc] = {a}; |
| foo->Call(context, env->Global(), kArgc, args).ToLocalChecked(); |
| |
| // Set up break point and step through the function. |
| SetBreakPoint(foo, 3); |
| run_step.set_step_action(StepNext); |
| break_point_hit_count = 0; |
| foo->Call(context, env->Global(), kArgc, args).ToLocalChecked(); |
| |
| // With stepping all break locations are hit. |
| CHECK_EQ(44, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| // Test of the stepping mechanism for keyed store in a loop. |
| TEST(DebugStepKeyedStoreLoop) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| |
| // Create a function for testing stepping of keyed store. The statement 'y=1' |
| // is there to have more than one breakable statement in the loop, TODO(315). |
| v8::Local<v8::Function> foo = CompileFunction( |
| &env, |
| "function foo(a) {\n" |
| " var len = a.length;\n" |
| " for (var i = 0; i < len; i++) {\n" |
| " y = 1;\n" |
| " a[i] = 42;\n" |
| " }\n" |
| "}\n" |
| "y=0\n", |
| "foo"); |
| |
| v8::Local<v8::Context> context = env.local(); |
| // Create array [0,1,2,3,4,5,6,7,8,9] |
| v8::Local<v8::Array> a = v8::Array::New(env->GetIsolate(), 10); |
| for (int i = 0; i < 10; i++) { |
| CHECK(a->Set(context, v8::Number::New(env->GetIsolate(), i), |
| v8::Number::New(env->GetIsolate(), i)) |
| .FromJust()); |
| } |
| |
| // Call function without any break points to ensure inlining is in place. |
| const int kArgc = 1; |
| v8::Local<v8::Value> args[kArgc] = {a}; |
| foo->Call(context, env->Global(), kArgc, args).ToLocalChecked(); |
| |
| // Set up break point and step through the function. |
| SetBreakPoint(foo, 3); |
| run_step.set_step_action(StepNext); |
| break_point_hit_count = 0; |
| foo->Call(context, env->Global(), kArgc, args).ToLocalChecked(); |
| |
| // With stepping all break locations are hit. |
| CHECK_EQ(44, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| // Test of the stepping mechanism for named load in a loop. |
| TEST(DebugStepNamedLoadLoop) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| |
| v8::Local<v8::Context> context = env.local(); |
| // Create a function for testing stepping of named load. |
| v8::Local<v8::Function> foo = CompileFunction( |
| &env, |
| "function foo() {\n" |
| " var a = [];\n" |
| " var s = \"\";\n" |
| " for (var i = 0; i < 10; i++) {\n" |
| " var v = new V(i, i + 1);\n" |
| " v.y;\n" |
| " a.length;\n" // Special case: array length. |
| " s.length;\n" // Special case: string length. |
| " }\n" |
| "}\n" |
| "function V(x, y) {\n" |
| " this.x = x;\n" |
| " this.y = y;\n" |
| "}\n", |
| "foo"); |
| |
| // Call function without any break points to ensure inlining is in place. |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| |
| // Set up break point and step through the function. |
| SetBreakPoint(foo, 4); |
| run_step.set_step_action(StepNext); |
| break_point_hit_count = 0; |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| |
| // With stepping all break locations are hit. |
| CHECK_EQ(65, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| static void DoDebugStepNamedStoreLoop(int expected) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| |
| // Create a function for testing stepping of named store. |
| v8::Local<v8::Context> context = env.local(); |
| v8::Local<v8::Function> foo = CompileFunction( |
| &env, |
| "function foo() {\n" |
| " var a = {a:1};\n" |
| " for (var i = 0; i < 10; i++) {\n" |
| " a.a = 2\n" |
| " }\n" |
| "}\n", |
| "foo"); |
| |
| // Call function without any break points to ensure inlining is in place. |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| |
| // Set up break point and step through the function. |
| SetBreakPoint(foo, 3); |
| run_step.set_step_action(StepNext); |
| break_point_hit_count = 0; |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| |
| // With stepping all expected break locations are hit. |
| CHECK_EQ(expected, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| // Test of the stepping mechanism for named load in a loop. |
| TEST(DebugStepNamedStoreLoop) { DoDebugStepNamedStoreLoop(34); } |
| |
| // Test the stepping mechanism with different ICs. |
| TEST(DebugStepLinearMixedICs) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| |
| v8::Local<v8::Context> context = env.local(); |
| // Create a function for testing stepping. |
| v8::Local<v8::Function> foo = CompileFunction(&env, |
| "function bar() {};" |
| "function foo() {" |
| " var x;" |
| " var index='name';" |
| " var y = {};" |
| " a=1;b=2;x=a;y[index]=3;x=y[index];bar();}", "foo"); |
| |
| // Run functions to allow them to get optimized. |
| CompileRun("a=0; b=0; bar(); foo();"); |
| |
| SetBreakPoint(foo, 0); |
| |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| |
| // With stepping all break locations are hit. |
| CHECK_EQ(10, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| |
| // Register a debug event listener which just counts. |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| SetBreakPoint(foo, 0); |
| break_point_hit_count = 0; |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| |
| // Without stepping only active break points are hit. |
| CHECK_EQ(1, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| TEST(DebugStepDeclarations) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| |
| v8::Local<v8::Context> context = env.local(); |
| // Create a function for testing stepping. Run it to allow it to get |
| // optimized. |
| const char* src = "function foo() { " |
| " var a;" |
| " var b = 1;" |
| " var c = foo;" |
| " var d = Math.floor;" |
| " var e = b + d(1.2);" |
| "}" |
| "foo()"; |
| v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo"); |
| |
| SetBreakPoint(foo, 0); |
| |
| // Stepping through the declarations. |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(5, break_point_hit_count); |
| |
| // Get rid of the debug event listener. |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| TEST(DebugStepLocals) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| |
| v8::Local<v8::Context> context = env.local(); |
| // Create a function for testing stepping. Run it to allow it to get |
| // optimized. |
| const char* src = "function foo() { " |
| " var a,b;" |
| " a = 1;" |
| " b = a + 2;" |
| " b = 1 + 2 + 3;" |
| " a = Math.floor(b);" |
| "}" |
| "foo()"; |
| v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo"); |
| |
| SetBreakPoint(foo, 0); |
| |
| // Stepping through the declarations. |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(5, break_point_hit_count); |
| |
| // Get rid of the debug event listener. |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| TEST(DebugStepIf) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| |
| v8::Local<v8::Context> context = env.local(); |
| // Create a function for testing stepping. Run it to allow it to get |
| // optimized. |
| const int argc = 1; |
| const char* src = "function foo(x) { " |
| " a = 1;" |
| " if (x) {" |
| " b = 1;" |
| " } else {" |
| " c = 1;" |
| " d = 1;" |
| " }" |
| "}" |
| "a=0; b=0; c=0; d=0; foo()"; |
| v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo"); |
| SetBreakPoint(foo, 0); |
| |
| // Stepping through the true part. |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| v8::Local<v8::Value> argv_true[argc] = {v8::True(isolate)}; |
| foo->Call(context, env->Global(), argc, argv_true).ToLocalChecked(); |
| CHECK_EQ(4, break_point_hit_count); |
| |
| // Stepping through the false part. |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| v8::Local<v8::Value> argv_false[argc] = {v8::False(isolate)}; |
| foo->Call(context, env->Global(), argc, argv_false).ToLocalChecked(); |
| CHECK_EQ(5, break_point_hit_count); |
| |
| // Get rid of the debug event listener. |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| TEST(DebugStepSwitch) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| |
| v8::Local<v8::Context> context = env.local(); |
| // Create a function for testing stepping. Run it to allow it to get |
| // optimized. |
| const int argc = 1; |
| const char* src = "function foo(x) { " |
| " a = 1;" |
| " switch (x) {" |
| " case 1:" |
| " b = 1;" |
| " case 2:" |
| " c = 1;" |
| " break;" |
| " case 3:" |
| " d = 1;" |
| " e = 1;" |
| " f = 1;" |
| " break;" |
| " }" |
| "}" |
| "a=0; b=0; c=0; d=0; e=0; f=0; foo()"; |
| v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo"); |
| SetBreakPoint(foo, 0); |
| |
| // One case with fall-through. |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| v8::Local<v8::Value> argv_1[argc] = {v8::Number::New(isolate, 1)}; |
| foo->Call(context, env->Global(), argc, argv_1).ToLocalChecked(); |
| CHECK_EQ(6, break_point_hit_count); |
| |
| // Another case. |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| v8::Local<v8::Value> argv_2[argc] = {v8::Number::New(isolate, 2)}; |
| foo->Call(context, env->Global(), argc, argv_2).ToLocalChecked(); |
| CHECK_EQ(5, break_point_hit_count); |
| |
| // Last case. |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| v8::Local<v8::Value> argv_3[argc] = {v8::Number::New(isolate, 3)}; |
| foo->Call(context, env->Global(), argc, argv_3).ToLocalChecked(); |
| CHECK_EQ(7, break_point_hit_count); |
| |
| // Get rid of the debug event listener. |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| TEST(DebugStepWhile) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| |
| v8::Local<v8::Context> context = env.local(); |
| // Create a function for testing stepping. Run it to allow it to get |
| // optimized. |
| const int argc = 1; |
| const char* src = "function foo(x) { " |
| " var a = 0;" |
| " while (a < x) {" |
| " a++;" |
| " }" |
| "}" |
| "foo()"; |
| v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo"); |
| SetBreakPoint(foo, 8); // "var a = 0;" |
| |
| // Looping 0 times. We still should break at the while-condition once. |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| v8::Local<v8::Value> argv_0[argc] = {v8::Number::New(isolate, 0)}; |
| foo->Call(context, env->Global(), argc, argv_0).ToLocalChecked(); |
| CHECK_EQ(3, break_point_hit_count); |
| |
| // Looping 10 times. |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| v8::Local<v8::Value> argv_10[argc] = {v8::Number::New(isolate, 10)}; |
| foo->Call(context, env->Global(), argc, argv_10).ToLocalChecked(); |
| CHECK_EQ(23, break_point_hit_count); |
| |
| // Looping 100 times. |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| v8::Local<v8::Value> argv_100[argc] = {v8::Number::New(isolate, 100)}; |
| foo->Call(context, env->Global(), argc, argv_100).ToLocalChecked(); |
| CHECK_EQ(203, break_point_hit_count); |
| |
| // Get rid of the debug event listener. |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| TEST(DebugStepDoWhile) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| |
| v8::Local<v8::Context> context = env.local(); |
| // Create a function for testing stepping. Run it to allow it to get |
| // optimized. |
| const int argc = 1; |
| const char* src = "function foo(x) { " |
| " var a = 0;" |
| " do {" |
| " a++;" |
| " } while (a < x)" |
| "}" |
| "foo()"; |
| v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo"); |
| SetBreakPoint(foo, 8); // "var a = 0;" |
| |
| // Looping 0 times. |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| v8::Local<v8::Value> argv_0[argc] = {v8::Number::New(isolate, 0)}; |
| foo->Call(context, env->Global(), argc, argv_0).ToLocalChecked(); |
| CHECK_EQ(4, break_point_hit_count); |
| |
| // Looping 10 times. |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| v8::Local<v8::Value> argv_10[argc] = {v8::Number::New(isolate, 10)}; |
| foo->Call(context, env->Global(), argc, argv_10).ToLocalChecked(); |
| CHECK_EQ(22, break_point_hit_count); |
| |
| // Looping 100 times. |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| v8::Local<v8::Value> argv_100[argc] = {v8::Number::New(isolate, 100)}; |
| foo->Call(context, env->Global(), argc, argv_100).ToLocalChecked(); |
| CHECK_EQ(202, break_point_hit_count); |
| |
| // Get rid of the debug event listener. |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| TEST(DebugStepFor) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| |
| v8::Local<v8::Context> context = env.local(); |
| // Create a function for testing stepping. Run it to allow it to get |
| // optimized. |
| const int argc = 1; |
| const char* src = "function foo(x) { " |
| " a = 1;" |
| " for (i = 0; i < x; i++) {" |
| " b = 1;" |
| " }" |
| "}" |
| "a=0; b=0; i=0; foo()"; |
| v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo"); |
| |
| SetBreakPoint(foo, 8); // "a = 1;" |
| |
| // Looping 0 times. |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| v8::Local<v8::Value> argv_0[argc] = {v8::Number::New(isolate, 0)}; |
| foo->Call(context, env->Global(), argc, argv_0).ToLocalChecked(); |
| CHECK_EQ(4, break_point_hit_count); |
| |
| // Looping 10 times. |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| v8::Local<v8::Value> argv_10[argc] = {v8::Number::New(isolate, 10)}; |
| foo->Call(context, env->Global(), argc, argv_10).ToLocalChecked(); |
| CHECK_EQ(34, break_point_hit_count); |
| |
| // Looping 100 times. |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| v8::Local<v8::Value> argv_100[argc] = {v8::Number::New(isolate, 100)}; |
| foo->Call(context, env->Global(), argc, argv_100).ToLocalChecked(); |
| CHECK_EQ(304, break_point_hit_count); |
| |
| // Get rid of the debug event listener. |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| TEST(DebugStepForContinue) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| |
| v8::Local<v8::Context> context = env.local(); |
| // Create a function for testing stepping. Run it to allow it to get |
| // optimized. |
| const int argc = 1; |
| const char* src = "function foo(x) { " |
| " var a = 0;" |
| " var b = 0;" |
| " var c = 0;" |
| " for (var i = 0; i < x; i++) {" |
| " a++;" |
| " if (a % 2 == 0) continue;" |
| " b++;" |
| " c++;" |
| " }" |
| " return b;" |
| "}" |
| "foo()"; |
| v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo"); |
| v8::Local<v8::Value> result; |
| SetBreakPoint(foo, 8); // "var a = 0;" |
| |
| // Each loop generates 4 or 5 steps depending on whether a is equal. |
| |
| // Looping 10 times. |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| v8::Local<v8::Value> argv_10[argc] = {v8::Number::New(isolate, 10)}; |
| result = foo->Call(context, env->Global(), argc, argv_10).ToLocalChecked(); |
| CHECK_EQ(5, result->Int32Value(context).FromJust()); |
| CHECK_EQ(62, break_point_hit_count); |
| |
| // Looping 100 times. |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| v8::Local<v8::Value> argv_100[argc] = {v8::Number::New(isolate, 100)}; |
| result = foo->Call(context, env->Global(), argc, argv_100).ToLocalChecked(); |
| CHECK_EQ(50, result->Int32Value(context).FromJust()); |
| CHECK_EQ(557, break_point_hit_count); |
| |
| // Get rid of the debug event listener. |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| TEST(DebugStepForBreak) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| |
| v8::Local<v8::Context> context = env.local(); |
| // Create a function for testing stepping. Run it to allow it to get |
| // optimized. |
| const int argc = 1; |
| const char* src = "function foo(x) { " |
| " var a = 0;" |
| " var b = 0;" |
| " var c = 0;" |
| " for (var i = 0; i < 1000; i++) {" |
| " a++;" |
| " if (a == x) break;" |
| " b++;" |
| " c++;" |
| " }" |
| " return b;" |
| "}" |
| "foo()"; |
| v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo"); |
| v8::Local<v8::Value> result; |
| SetBreakPoint(foo, 8); // "var a = 0;" |
| |
| // Each loop generates 5 steps except for the last (when break is executed) |
| // which only generates 4. |
| |
| // Looping 10 times. |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| v8::Local<v8::Value> argv_10[argc] = {v8::Number::New(isolate, 10)}; |
| result = foo->Call(context, env->Global(), argc, argv_10).ToLocalChecked(); |
| CHECK_EQ(9, result->Int32Value(context).FromJust()); |
| CHECK_EQ(64, break_point_hit_count); |
| |
| // Looping 100 times. |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| v8::Local<v8::Value> argv_100[argc] = {v8::Number::New(isolate, 100)}; |
| result = foo->Call(context, env->Global(), argc, argv_100).ToLocalChecked(); |
| CHECK_EQ(99, result->Int32Value(context).FromJust()); |
| CHECK_EQ(604, break_point_hit_count); |
| |
| // Get rid of the debug event listener. |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| TEST(DebugStepForIn) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| |
| v8::Local<v8::Context> context = env.local(); |
| // Create a function for testing stepping. Run it to allow it to get |
| // optimized. |
| v8::Local<v8::Function> foo; |
| const char* src_1 = "function foo() { " |
| " var a = [1, 2];" |
| " for (x in a) {" |
| " b = 0;" |
| " }" |
| "}" |
| "foo()"; |
| foo = CompileFunction(&env, src_1, "foo"); |
| SetBreakPoint(foo, 0); // "var a = ..." |
| |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(8, break_point_hit_count); |
| |
| // Create a function for testing stepping. Run it to allow it to get |
| // optimized. |
| const char* src_2 = "function foo() { " |
| " var a = {a:[1, 2, 3]};" |
| " for (x in a.a) {" |
| " b = 0;" |
| " }" |
| "}" |
| "foo()"; |
| foo = CompileFunction(&env, src_2, "foo"); |
| SetBreakPoint(foo, 0); // "var a = ..." |
| |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(10, break_point_hit_count); |
| |
| // Get rid of the debug event listener. |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| TEST(DebugStepWith) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| |
| v8::Local<v8::Context> context = env.local(); |
| // Create a function for testing stepping. Run it to allow it to get |
| // optimized. |
| const char* src = "function foo(x) { " |
| " var a = {};" |
| " with (a) {}" |
| " with (b) {}" |
| "}" |
| "foo()"; |
| CHECK(env->Global() |
| ->Set(context, v8_str(env->GetIsolate(), "b"), |
| v8::Object::New(env->GetIsolate())) |
| .FromJust()); |
| v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo"); |
| v8::Local<v8::Value> result; |
| SetBreakPoint(foo, 8); // "var a = {};" |
| |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(4, break_point_hit_count); |
| |
| // Get rid of the debug event listener. |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| TEST(DebugConditional) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| |
| v8::Local<v8::Context> context = env.local(); |
| // Create a function for testing stepping. Run it to allow it to get |
| // optimized. |
| const char* src = |
| "function foo(x) { " |
| " return x ? 1 : 2;" |
| "}" |
| "foo()"; |
| v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo"); |
| SetBreakPoint(foo, 0); // "var a;" |
| |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| const int argc = 1; |
| v8::Local<v8::Value> argv_true[argc] = {v8::True(isolate)}; |
| foo->Call(context, env->Global(), argc, argv_true).ToLocalChecked(); |
| CHECK_EQ(2, break_point_hit_count); |
| |
| // Get rid of the debug event listener. |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| // Test that step in does not step into native functions. |
| TEST(DebugStepNatives) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| // Create a function for testing stepping. |
| v8::Local<v8::Function> foo = |
| CompileFunction(&env, "function foo(){debugger;Math.sin(1);}", "foo"); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| |
| v8::Local<v8::Context> context = env.local(); |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| |
| // With stepping all break locations are hit. |
| CHECK_EQ(3, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| |
| // Register a debug event listener which just counts. |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| break_point_hit_count = 0; |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| |
| // Without stepping only active break points are hit. |
| CHECK_EQ(1, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| // Test that step in works with function.apply. |
| TEST(DebugStepFunctionApply) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| |
| // Create a function for testing stepping. |
| v8::Local<v8::Function> foo = |
| CompileFunction(&env, |
| "function bar(x, y, z) { if (x == 1) { a = y; b = z; } }" |
| "function foo(){ debugger; bar.apply(this, [1,2,3]); }", |
| "foo"); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| |
| v8::Local<v8::Context> context = env.local(); |
| run_step.set_step_action(StepIn); |
| break_point_hit_count = 0; |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| |
| // With stepping all break locations are hit. |
| CHECK_EQ(7, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| |
| // Register a debug event listener which just counts. |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| break_point_hit_count = 0; |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| |
| // Without stepping only the debugger statement is hit. |
| CHECK_EQ(1, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| // Test that step in works with function.call. |
| TEST(DebugStepFunctionCall) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| v8::Local<v8::Context> context = env.local(); |
| // Create a function for testing stepping. |
| v8::Local<v8::Function> foo = CompileFunction( |
| &env, |
| "function bar(x, y, z) { if (x == 1) { a = y; b = z; } }" |
| "function foo(a){ debugger;" |
| " if (a) {" |
| " bar.call(this, 1, 2, 3);" |
| " } else {" |
| " bar.call(this, 0);" |
| " }" |
| "}", |
| "foo"); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| run_step.set_step_action(StepIn); |
| |
| // Check stepping where the if condition in bar is false. |
| break_point_hit_count = 0; |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(6, break_point_hit_count); |
| |
| // Check stepping where the if condition in bar is true. |
| break_point_hit_count = 0; |
| const int argc = 1; |
| v8::Local<v8::Value> argv[argc] = {v8::True(isolate)}; |
| foo->Call(context, env->Global(), argc, argv).ToLocalChecked(); |
| CHECK_EQ(8, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| |
| // Register a debug event listener which just counts. |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| break_point_hit_count = 0; |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| |
| // Without stepping only the debugger statement is hit. |
| CHECK_EQ(1, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(isolate, nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| // Test that step in works with Function.call.apply. |
| TEST(DebugStepFunctionCallApply) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| v8::Local<v8::Context> context = env.local(); |
| // Create a function for testing stepping. |
| v8::Local<v8::Function> foo = |
| CompileFunction(&env, |
| "function bar() { }" |
| "function foo(){ debugger;" |
| " Function.call.apply(bar);" |
| " Function.call.apply(Function.call, " |
| "[Function.call, bar]);" |
| "}", |
| "foo"); |
| |
| // Register a debug event listener which steps and counts. |
| DebugEventCounter run_step; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &run_step); |
| run_step.set_step_action(StepIn); |
| |
| break_point_hit_count = 0; |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| CHECK_EQ(6, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| |
| // Register a debug event listener which just counts. |
| DebugEventCounter delegate; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &delegate); |
| |
| break_point_hit_count = 0; |
| foo->Call(context, env->Global(), 0, nullptr).ToLocalChecked(); |
| |
| // Without stepping only the debugger statement is hit. |
| CHECK_EQ(1, break_point_hit_count); |
| |
| v8::debug::SetDebugDelegate(isolate, nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| |
| // Tests that breakpoint will be hit if it's set in script. |
| TEST(PauseInScript) { |
| LocalContext env; |
| v8::HandleScope scope(env->GetIsolate()); |
| i::Isolate* isolate = reinterpret_cast<i::Isolate*>(env->GetIsolate()); |
| |
| // Register a debug event listener which counts. |
| DebugEventCounter event_counter; |
| v8::debug::SetDebugDelegate(env->GetIsolate(), &event_counter); |
| |
| v8::Local<v8::Context> context = env.local(); |
| // Create a script that returns a function. |
| const char* src = "(function (evt) {})"; |
| const char* script_name = "StepInHandlerTest"; |
| |
| v8::ScriptOrigin origin(v8_str(env->GetIsolate(), script_name), |
| v8::Integer::New(env->GetIsolate(), 0)); |
| v8::Local<v8::Script> script = |
| v8::Script::Compile(context, v8_str(env->GetIsolate(), src), &origin) |
| .ToLocalChecked(); |
| |
| // Set breakpoint in the script. |
| i::Handle<i::Script> i_script( |
| i::Script::cast(v8::Utils::OpenHandle(*script)->shared().script()), |
| isolate); |
| i::Handle<i::String> condition = isolate->factory()->empty_string(); |
| int position = 0; |
| int id; |
| isolate->debug()->SetBreakPointForScript(i_script, condition, &position, &id); |
| break_point_hit_count = 0; |
| |
| v8::Local<v8::Value> r = script->Run(context).ToLocalChecked(); |
| |
| CHECK(r->IsFunction()); |
| CHECK_EQ(1, break_point_hit_count); |
| |
| // Get rid of the debug delegate. |
| v8::debug::SetDebugDelegate(env->GetIsolate(), nullptr); |
| CheckDebuggerUnloaded(); |
| } |
| |
| int message_callback_count = 0; |
| |
| TEST(DebugBreak) { |
| i::FLAG_stress_compaction = false; |
| #ifdef VERIFY_HEAP |
| i::FLAG_verify_heap = true; |
| #endif |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| |
| // Register a debug event listener which sets the break flag and counts. |
| DebugEventBreak delegate; |
| v8::debug::SetDebugDelegate(isolate, &delegate); |
| |
| v8::Local<v8::Context> context = env.local(); |
| // Create a function for testing stepping. |
| const char* src = "function f0() {}" |
| "function f1(x1) {}" |
| "function f2(x1,x2) {}" |
| "function f3(x1,x2,x3) {}"; |
| v8::Local<v8::Function> f0 = CompileFunction(&env, src, "f0"); |
| v8::Local<v8::Function> f1 = CompileFunction(&env, src, "f1"); |
| v8::Local<v8::Function> f2 = CompileFunction(&env, src, "f2"); |
| v8::Local<v8::Function> f3 = CompileFunction(&env, src, "f3"); |
| |
| // Call the function to make sure it is compiled. |
| v8::Local |