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 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 } 56} 57 58// Add the debug event listener. 59Debug.setListener(listener); 60 61 62// Initialize for a new test. 63function BeginTest(name) { 64 test_name = name; 65 listener_delegate = null; 66 listener_called = false; 67 exception = null; 68 begin_test_count++; 69} 70 71 72// Check result of a test. 73function EndTest() { 74 assertTrue(listener_called, "listerner not called for " + test_name); 75 assertNull(exception, test_name); 76 end_test_count++; 77} 78 79 80// Check that the scope chain contains the expected types of scopes. 81function CheckScopeChain(scopes, exec_state) { 82 assertEquals(scopes.length, exec_state.frame().scopeCount()); 83 for (var i = 0; i < scopes.length; i++) { 84 var scope = exec_state.frame().scope(i); 85 assertTrue(scope.isScope()); 86 assertEquals(scopes[i], scope.scopeType()); 87 88 // Check the global object when hitting the global scope. 89 if (scopes[i] == debug.ScopeType.Global) { 90 // Objects don't have same class (one is "global", other is "Object", 91 // so just check the properties directly. 92 assertPropertiesEqual(this, scope.scopeObject().value()); 93 } 94 } 95 96 // Get the debug command processor. 97 var dcp = exec_state.debugCommandProcessor("unspecified_running_state"); 98 99 // Send a scopes request and check the result. 100 var json; 101 var request_json = '{"seq":0,"type":"request","command":"scopes"}'; 102 var response_json = dcp.processDebugJSONRequest(request_json); 103 var response = JSON.parse(response_json); 104 assertEquals(scopes.length, response.body.scopes.length); 105 for (var i = 0; i < scopes.length; i++) { 106 assertEquals(i, response.body.scopes[i].index); 107 assertEquals(scopes[i], response.body.scopes[i].type); 108 if (scopes[i] == debug.ScopeType.Local || 109 scopes[i] == debug.ScopeType.Closure) { 110 assertTrue(response.body.scopes[i].object.ref < 0); 111 } else { 112 assertTrue(response.body.scopes[i].object.ref >= 0); 113 } 114 var found = false; 115 for (var j = 0; j < response.refs.length && !found; j++) { 116 found = response.refs[j].handle == response.body.scopes[i].object.ref; 117 } 118 assertTrue(found, "Scope object " + response.body.scopes[i].object.ref + " not found"); 119 } 120} 121 122// Check that the content of the scope is as expected. For functions just check 123// that there is a function. 124function CheckScopeContent(content, number, exec_state) { 125 var scope = exec_state.frame().scope(number); 126 var count = 0; 127 for (var p in content) { 128 var property_mirror = scope.scopeObject().property(p); 129 if (property_mirror.isUndefined()) { 130 print('property ' + p + ' not found in scope'); 131 } 132 assertFalse(property_mirror.isUndefined(), 'property ' + p + ' not found in scope'); 133 if (typeof(content[p]) === 'function') { 134 assertTrue(property_mirror.value().isFunction()); 135 } else { 136 assertEquals(content[p], property_mirror.value().value(), 'property ' + p + ' has unexpected value'); 137 } 138 count++; 139 } 140 141 // 'arguments' and might be exposed in the local and closure scope. Just 142 // ignore this. 143 var scope_size = scope.scopeObject().properties().length; 144 if (!scope.scopeObject().property('arguments').isUndefined()) { 145 scope_size--; 146 } 147 // Also ignore synthetic variable from catch block. 148 if (!scope.scopeObject().property('.catch-var').isUndefined()) { 149 scope_size--; 150 } 151 // Skip property with empty name. 152 if (!scope.scopeObject().property('').isUndefined()) { 153 scope_size--; 154 } 155 // Also ignore synthetic variable from block scopes. 156 if (!scope.scopeObject().property('.block').isUndefined()) { 157 scope_size--; 158 } 159 160 if (count != scope_size) { 161 print('Names found in scope:'); 162 var names = scope.scopeObject().propertyNames(); 163 for (var i = 0; i < names.length; i++) { 164 print(names[i]); 165 } 166 } 167 assertEquals(count, scope_size); 168 169 // Get the debug command processor. 170 var dcp = exec_state.debugCommandProcessor("unspecified_running_state"); 171 172 // Send a scope request for information on a single scope and check the 173 // result. 174 var request_json = '{"seq":0,"type":"request","command":"scope","arguments":{"number":'; 175 request_json += scope.scopeIndex(); 176 request_json += '}}'; 177 var response_json = dcp.processDebugJSONRequest(request_json); 178 var response = JSON.parse(response_json); 179 assertEquals(scope.scopeType(), response.body.type); 180 assertEquals(number, response.body.index); 181 if (scope.scopeType() == debug.ScopeType.Local || 182 scope.scopeType() == debug.ScopeType.Closure) { 183 assertTrue(response.body.object.ref < 0); 184 } else { 185 assertTrue(response.body.object.ref >= 0); 186 } 187 var found = false; 188 for (var i = 0; i < response.refs.length && !found; i++) { 189 found = response.refs[i].handle == response.body.object.ref; 190 } 191 assertTrue(found, "Scope object " + response.body.object.ref + " not found"); 192} 193 194 195// Simple closure formed by returning an inner function referering to an outer 196// block local variable and an outer function's parameter. Due to VM 197// optimizations parts of the actual closure is missing from the debugger 198// information. 199BeginTest("Closure 1"); 200 201function closure_1(a) { 202 var x = 2; 203 let y = 3; 204 if (true) { 205 let z = 4; 206 function f() { 207 debugger; 208 return a + x + y + z; 209 }; 210 return f; 211 } 212} 213 214listener_delegate = function(exec_state) { 215 CheckScopeChain([debug.ScopeType.Local, 216 debug.ScopeType.Block, 217 debug.ScopeType.Closure, 218 debug.ScopeType.Global], exec_state); 219 CheckScopeContent({}, 0, exec_state); 220 CheckScopeContent({z:4}, 1, exec_state); 221 CheckScopeContent({a:1,x:2,y:3}, 2, exec_state); 222}; 223closure_1(1)(); 224EndTest(); 225