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. 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 print(e + e.stack); 56 } 57 } 58 59 // Add the debug event listener. 60 Debug.setListener(listener); 61 62 63 // Initialize for a new test. 64 function 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. 74 function 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. 82 function 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. 125 function 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. 192 BeginTest("Closure 1"); 193 194 function 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 207 listener_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 }; 216 closure_1(1)(); 217 EndTest(); 218