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. 34 Debug = debug.Debug; 35 36 var test_name; 37 var listener_delegate; 38 var listener_called; 39 var exception; 40 var begin_test_count = 0; 41 var end_test_count = 0; 42 var break_count = 0; 43 44 45 // Debug event listener which delegates. 46 function 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. 59 Debug.setListener(listener); 60 61 62 // Initialize for a new test. 63 function 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. 73 function 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. 81 function 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. 124 function 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. 199 BeginTest("Closure 1"); 200 201 function 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 214 listener_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 }; 223 closure_1(1)(); 224 EndTest(); 225