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 --harmony-scoping 29// The functions used for testing backtraces. They are at the top to make the 30// testing of source line/column easier. 31 32"use strict"; 33 34// Get the Debug object exposed from the debug context global object. 35var Debug = debug.Debug; 36 37var test_name; 38var listener_delegate; 39var listener_called; 40var exception; 41var begin_test_count = 0; 42var end_test_count = 0; 43var break_count = 0; 44 45 46// Debug event listener which delegates. 47function listener(event, exec_state, event_data, data) { 48 try { 49 if (event == Debug.DebugEvent.Break) { 50 break_count++; 51 listener_called = true; 52 listener_delegate(exec_state); 53 } 54 } catch (e) { 55 exception = e; 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 80var global_object = this; 81 82// Check that the scope chain contains the expected types of scopes. 83function CheckScopeChain(scopes, exec_state) { 84 assertEquals(scopes.length, exec_state.frame().scopeCount()); 85 for (var i = 0; i < scopes.length; i++) { 86 var scope = exec_state.frame().scope(i); 87 assertTrue(scope.isScope()); 88 assertEquals(scopes[i], scope.scopeType()); 89 90 // Check the global object when hitting the global scope. 91 if (scopes[i] == debug.ScopeType.Global) { 92 // Objects don't have same class (one is "global", other is "Object", 93 // so just check the properties directly. 94 assertPropertiesEqual(global_object, scope.scopeObject().value()); 95 } 96 } 97 98 // Get the debug command processor. 99 var dcp = exec_state.debugCommandProcessor("unspecified_running_state"); 100 101 // Send a scopes request and check the result. 102 var json; 103 var request_json = '{"seq":0,"type":"request","command":"scopes"}'; 104 var response_json = dcp.processDebugJSONRequest(request_json); 105 var response = JSON.parse(response_json); 106 assertEquals(scopes.length, response.body.scopes.length); 107 for (var i = 0; i < scopes.length; i++) { 108 assertEquals(i, response.body.scopes[i].index); 109 assertEquals(scopes[i], response.body.scopes[i].type); 110 if (scopes[i] == debug.ScopeType.Local || 111 scopes[i] == debug.ScopeType.Closure) { 112 assertTrue(response.body.scopes[i].object.ref < 0); 113 } else { 114 assertTrue(response.body.scopes[i].object.ref >= 0); 115 } 116 var found = false; 117 for (var j = 0; j < response.refs.length && !found; j++) { 118 found = response.refs[j].handle == response.body.scopes[i].object.ref; 119 } 120 assertTrue(found, "Scope object " + response.body.scopes[i].object.ref + " not found"); 121 } 122} 123 124// Check that the content of the scope is as expected. For functions just check 125// that there is a function. 126function CheckScopeContent(content, number, exec_state) { 127 var scope = exec_state.frame().scope(number); 128 var count = 0; 129 for (var p in content) { 130 var property_mirror = scope.scopeObject().property(p); 131 if (property_mirror.isUndefined()) { 132 print('property ' + p + ' not found in scope'); 133 } 134 assertFalse(property_mirror.isUndefined(), 'property ' + p + ' not found in scope'); 135 if (typeof(content[p]) === 'function') { 136 assertTrue(property_mirror.value().isFunction()); 137 } else { 138 assertEquals(content[p], property_mirror.value().value(), 'property ' + p + ' has unexpected value'); 139 } 140 count++; 141 } 142 143 // 'arguments' and might be exposed in the local and closure scope. Just 144 // ignore this. 145 var scope_size = scope.scopeObject().properties().length; 146 if (!scope.scopeObject().property('arguments').isUndefined()) { 147 scope_size--; 148 } 149 // Skip property with empty name. 150 if (!scope.scopeObject().property('').isUndefined()) { 151 scope_size--; 152 } 153 154 if (count != scope_size) { 155 print('Names found in scope:'); 156 var names = scope.scopeObject().propertyNames(); 157 for (var i = 0; i < names.length; i++) { 158 print(names[i]); 159 } 160 } 161 assertEquals(count, scope_size); 162 163 // Get the debug command processor. 164 var dcp = exec_state.debugCommandProcessor("unspecified_running_state"); 165 166 // Send a scope request for information on a single scope and check the 167 // result. 168 var request_json = '{"seq":0,"type":"request","command":"scope","arguments":{"number":'; 169 request_json += scope.scopeIndex(); 170 request_json += '}}'; 171 var response_json = dcp.processDebugJSONRequest(request_json); 172 var response = JSON.parse(response_json); 173 assertEquals(scope.scopeType(), response.body.type); 174 assertEquals(number, response.body.index); 175 if (scope.scopeType() == debug.ScopeType.Local || 176 scope.scopeType() == debug.ScopeType.Closure) { 177 assertTrue(response.body.object.ref < 0); 178 } else { 179 assertTrue(response.body.object.ref >= 0); 180 } 181 var found = false; 182 for (var i = 0; i < response.refs.length && !found; i++) { 183 found = response.refs[i].handle == response.body.object.ref; 184 } 185 assertTrue(found, "Scope object " + response.body.object.ref + " not found"); 186} 187 188 189// Simple empty block scope in local scope. 190BeginTest("Local block 1"); 191 192function local_block_1() { 193 { 194 debugger; 195 } 196} 197 198listener_delegate = function(exec_state) { 199 CheckScopeChain([debug.ScopeType.Local, 200 debug.ScopeType.Global], exec_state); 201 CheckScopeContent({}, 0, exec_state); 202}; 203local_block_1(); 204EndTest(); 205 206 207// Simple empty block scope in local scope with a parameter. 208BeginTest("Local 2"); 209 210function local_2(a) { 211 { 212 debugger; 213 } 214} 215 216listener_delegate = function(exec_state) { 217 CheckScopeChain([debug.ScopeType.Local, 218 debug.ScopeType.Global], exec_state); 219 CheckScopeContent({a:1}, 0, exec_state); 220}; 221local_2(1); 222EndTest(); 223 224 225// Local scope with a parameter and a local variable. 226BeginTest("Local 3"); 227 228function local_3(a) { 229 let x = 3; 230 debugger; 231} 232 233listener_delegate = function(exec_state) { 234 CheckScopeChain([debug.ScopeType.Local, 235 debug.ScopeType.Global], exec_state); 236 CheckScopeContent({a:1,x:3}, 0, exec_state); 237}; 238local_3(1); 239EndTest(); 240 241 242// Local scope with parameters and local variables. 243BeginTest("Local 4"); 244 245function local_4(a, b) { 246 let x = 3; 247 let y = 4; 248 debugger; 249} 250 251listener_delegate = function(exec_state) { 252 CheckScopeChain([debug.ScopeType.Local, 253 debug.ScopeType.Global], exec_state); 254 CheckScopeContent({a:1,b:2,x:3,y:4}, 0, exec_state); 255}; 256local_4(1, 2); 257EndTest(); 258 259 260// Single variable in a block scope. 261BeginTest("Local 5"); 262 263function local_5(a) { 264 { 265 let x = 5; 266 debugger; 267 } 268} 269 270listener_delegate = function(exec_state) { 271 CheckScopeChain([debug.ScopeType.Block, 272 debug.ScopeType.Local, 273 debug.ScopeType.Global], exec_state); 274 CheckScopeContent({x:5}, 0, exec_state); 275 CheckScopeContent({a:1}, 1, exec_state); 276}; 277local_5(1); 278EndTest(); 279 280 281// Two variables in a block scope. 282BeginTest("Local 6"); 283 284function local_6(a) { 285 { 286 let x = 6; 287 let y = 7; 288 debugger; 289 } 290} 291 292listener_delegate = function(exec_state) { 293 CheckScopeChain([debug.ScopeType.Block, 294 debug.ScopeType.Local, 295 debug.ScopeType.Global], exec_state); 296 CheckScopeContent({x:6,y:7}, 0, exec_state); 297 CheckScopeContent({a:1}, 1, exec_state); 298}; 299local_6(1); 300EndTest(); 301 302 303// Two variables in a block scope. 304BeginTest("Local 7"); 305 306function local_7(a) { 307 { 308 { 309 let x = 8; 310 debugger; 311 } 312 } 313} 314 315listener_delegate = function(exec_state) { 316 CheckScopeChain([debug.ScopeType.Block, 317 debug.ScopeType.Local, 318 debug.ScopeType.Global], exec_state); 319 CheckScopeContent({x:8}, 0, exec_state); 320 CheckScopeContent({a:1}, 1, exec_state); 321}; 322local_7(1); 323EndTest(); 324 325 326// Simple closure formed by returning an inner function referering to an outer 327// block local variable and an outer function's parameter. 328BeginTest("Closure 1"); 329 330function closure_1(a) { 331 var x = 2; 332 let y = 3; 333 if (true) { 334 let z = 4; 335 function f() { 336 debugger; 337 return a + x + y + z; 338 }; 339 return f; 340 } 341} 342 343listener_delegate = function(exec_state) { 344 CheckScopeChain([debug.ScopeType.Local, 345 debug.ScopeType.Block, 346 debug.ScopeType.Closure, 347 debug.ScopeType.Global], exec_state); 348 CheckScopeContent({}, 0, exec_state); 349 CheckScopeContent({a:1,x:2,y:3}, 2, exec_state); 350}; 351closure_1(1)(); 352EndTest(); 353 354 355// Simple for-in loop over the keys of an object. 356BeginTest("For loop 1"); 357 358function for_loop_1() { 359 for (let x in {y:undefined}) { 360 debugger; 361 } 362} 363 364listener_delegate = function(exec_state) { 365 CheckScopeChain([debug.ScopeType.Block, 366 debug.ScopeType.Local, 367 debug.ScopeType.Global], exec_state); 368 CheckScopeContent({x:'y'}, 0, exec_state); 369 // The function scope contains a temporary iteration variable, but it is 370 // hidden to the debugger. 371 CheckScopeContent({}, 1, exec_state); 372}; 373for_loop_1(); 374EndTest(); 375 376 377// For-in loop over the keys of an object with a block scoped let variable 378// shadowing the iteration variable. 379BeginTest("For loop 2"); 380 381function for_loop_2() { 382 for (let x in {y:undefined}) { 383 let x = 3; 384 debugger; 385 } 386} 387 388listener_delegate = function(exec_state) { 389 CheckScopeChain([debug.ScopeType.Block, 390 debug.ScopeType.Block, 391 debug.ScopeType.Local, 392 debug.ScopeType.Global], exec_state); 393 CheckScopeContent({x:3}, 0, exec_state); 394 CheckScopeContent({x:'y'}, 1, exec_state); 395 // The function scope contains a temporary iteration variable, hidden to the 396 // debugger. 397 CheckScopeContent({}, 2, exec_state); 398}; 399for_loop_2(); 400EndTest(); 401 402 403// Simple for loop. 404BeginTest("For loop 3"); 405 406function for_loop_3() { 407 for (let x = 3; x < 4; ++x) { 408 debugger; 409 } 410} 411 412listener_delegate = function(exec_state) { 413 CheckScopeChain([debug.ScopeType.Block, 414 debug.ScopeType.Block, 415 debug.ScopeType.Local, 416 debug.ScopeType.Global], exec_state); 417 CheckScopeContent({x:3}, 0, exec_state); 418 CheckScopeContent({x:3}, 1, exec_state); 419 CheckScopeContent({}, 2, exec_state); 420}; 421for_loop_3(); 422EndTest(); 423 424 425// For loop with a block scoped let variable shadowing the iteration variable. 426BeginTest("For loop 4"); 427 428function for_loop_4() { 429 for (let x = 3; x < 4; ++x) { 430 let x = 5; 431 debugger; 432 } 433} 434 435listener_delegate = function(exec_state) { 436 CheckScopeChain([debug.ScopeType.Block, 437 debug.ScopeType.Block, 438 debug.ScopeType.Block, 439 debug.ScopeType.Local, 440 debug.ScopeType.Global], exec_state); 441 CheckScopeContent({x:5}, 0, exec_state); 442 CheckScopeContent({x:3}, 1, exec_state); 443 CheckScopeContent({x:3}, 2, exec_state); 444 CheckScopeContent({}, 3, exec_state); 445}; 446for_loop_4(); 447EndTest(); 448 449 450// For loop with two variable declarations. 451BeginTest("For loop 5"); 452 453function for_loop_5() { 454 for (let x = 3, y = 5; x < 4; ++x) { 455 debugger; 456 } 457} 458 459listener_delegate = function(exec_state) { 460 CheckScopeChain([debug.ScopeType.Block, 461 debug.ScopeType.Block, 462 debug.ScopeType.Local, 463 debug.ScopeType.Global], exec_state); 464 CheckScopeContent({x:3,y:5}, 0, exec_state); 465 CheckScopeContent({x:3,y:5}, 1, exec_state); 466 CheckScopeContent({}, 2, exec_state); 467}; 468for_loop_5(); 469EndTest(); 470