1// Copyright 2011 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28// Flags: --expose-debug-as debug 29// The functions used for testing backtraces. They are at the top to make the 30// testing of source line/column easier. 31 32 33// Get the Debug object exposed from the debug context global object. 34Debug = debug.Debug; 35 36var test_name; 37var listener_delegate; 38var listener_called; 39var exception; 40var begin_test_count = 0; 41var end_test_count = 0; 42var break_count = 0; 43 44 45// Debug event listener which delegates. 46function listener(event, exec_state, event_data, data) { 47 try { 48 if (event == Debug.DebugEvent.Break) { 49 break_count++; 50 listener_called = true; 51 listener_delegate(exec_state); 52 } 53 } catch (e) { 54 exception = e; 55 print(e + e.stack); 56 } 57} 58 59// Add the debug event listener. 60Debug.setListener(listener); 61 62 63// Initialize for a new test. 64function BeginTest(name) { 65 test_name = name; 66 listener_delegate = null; 67 listener_called = false; 68 exception = null; 69 begin_test_count++; 70} 71 72 73// Check result of a test. 74function EndTest() { 75 assertTrue(listener_called, "listerner not called for " + test_name); 76 assertNull(exception, test_name); 77 end_test_count++; 78} 79 80 81// Check that the scope chain contains the expected types of scopes. 82function CheckScopeChain(scopes, exec_state) { 83 assertEquals(scopes.length, exec_state.frame().scopeCount()); 84 for (var i = 0; i < scopes.length; i++) { 85 var scope = exec_state.frame().scope(i); 86 assertTrue(scope.isScope()); 87 assertEquals(scopes[i], scope.scopeType()); 88 89 // Check the global object when hitting the global scope. 90 if (scopes[i] == debug.ScopeType.Global) { 91 // Objects don't have same class (one is "global", other is "Object", 92 // so just check the properties directly. 93 assertPropertiesEqual(this, scope.scopeObject().value()); 94 } 95 } 96 97 // Get the debug command processor. 98 var dcp = exec_state.debugCommandProcessor("unspecified_running_state"); 99 100 // Send a scopes request and check the result. 101 var json; 102 var request_json = '{"seq":0,"type":"request","command":"scopes"}'; 103 var response_json = dcp.processDebugJSONRequest(request_json); 104 var response = JSON.parse(response_json); 105 assertEquals(scopes.length, response.body.scopes.length); 106 for (var i = 0; i < scopes.length; i++) { 107 assertEquals(i, response.body.scopes[i].index); 108 assertEquals(scopes[i], response.body.scopes[i].type); 109 if (scopes[i] == debug.ScopeType.Local || 110 scopes[i] == debug.ScopeType.Closure) { 111 assertTrue(response.body.scopes[i].object.ref < 0); 112 } else { 113 assertTrue(response.body.scopes[i].object.ref >= 0); 114 } 115 var found = false; 116 for (var j = 0; j < response.refs.length && !found; j++) { 117 found = response.refs[j].handle == response.body.scopes[i].object.ref; 118 } 119 assertTrue(found, "Scope object " + response.body.scopes[i].object.ref + " not found"); 120 } 121} 122 123// Check that the content of the scope is as expected. For functions just check 124// that there is a function. 125function CheckScopeContent(content, number, exec_state) { 126 var scope = exec_state.frame().scope(number); 127 var count = 0; 128 for (var p in content) { 129 var property_mirror = scope.scopeObject().property(p); 130 if (property_mirror.isUndefined()) { 131 print('property ' + p + ' not found in scope'); 132 } 133 assertFalse(property_mirror.isUndefined(), 'property ' + p + ' not found in scope'); 134 if (typeof(content[p]) === 'function') { 135 assertTrue(property_mirror.value().isFunction()); 136 } else { 137 assertEquals(content[p], property_mirror.value().value(), 'property ' + p + ' has unexpected value'); 138 } 139 count++; 140 } 141 142 // 'arguments' and might be exposed in the local and closure scope. Just 143 // ignore this. 144 var scope_size = scope.scopeObject().properties().length; 145 if (!scope.scopeObject().property('arguments').isUndefined()) { 146 scope_size--; 147 } 148 // Skip property with empty name. 149 if (!scope.scopeObject().property('').isUndefined()) { 150 scope_size--; 151 } 152 153 if (count != scope_size) { 154 print('Names found in scope:'); 155 var names = scope.scopeObject().propertyNames(); 156 for (var i = 0; i < names.length; i++) { 157 print(names[i]); 158 } 159 } 160 assertEquals(count, scope_size); 161 162 // Get the debug command processor. 163 var dcp = exec_state.debugCommandProcessor("unspecified_running_state"); 164 165 // Send a scope request for information on a single scope and check the 166 // result. 167 var request_json = '{"seq":0,"type":"request","command":"scope","arguments":{"number":'; 168 request_json += scope.scopeIndex(); 169 request_json += '}}'; 170 var response_json = dcp.processDebugJSONRequest(request_json); 171 var response = JSON.parse(response_json); 172 assertEquals(scope.scopeType(), response.body.type); 173 assertEquals(number, response.body.index); 174 if (scope.scopeType() == debug.ScopeType.Local || 175 scope.scopeType() == debug.ScopeType.Closure) { 176 assertTrue(response.body.object.ref < 0); 177 } else { 178 assertTrue(response.body.object.ref >= 0); 179 } 180 var found = false; 181 for (var i = 0; i < response.refs.length && !found; i++) { 182 found = response.refs[i].handle == response.body.object.ref; 183 } 184 assertTrue(found, "Scope object " + response.body.object.ref + " not found"); 185} 186 187 188// Simple closure formed by returning an inner function referering to an outer 189// block local variable and an outer function's parameter. Due to VM 190// optimizations parts of the actual closure is missing from the debugger 191// information. 192BeginTest("Closure 1"); 193 194function closure_1(a) { 195 var x = 2; 196 let y = 3; 197 if (true) { 198 let z = 4; 199 function f() { 200 debugger; 201 return a + x + y + z; 202 }; 203 return f; 204 } 205} 206 207listener_delegate = function(exec_state) { 208 CheckScopeChain([debug.ScopeType.Local, 209 debug.ScopeType.Block, 210 debug.ScopeType.Closure, 211 debug.ScopeType.Global], exec_state); 212 CheckScopeContent({}, 0, exec_state); 213 CheckScopeContent({z:4}, 1, exec_state); 214 CheckScopeContent({a:1,x:2,y:3}, 2, exec_state); 215}; 216closure_1(1)(); 217EndTest(); 218