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