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 // Skip property with empty name. 148 if (!scope.scopeObject().property('').isUndefined()) { 149 scope_size--; 150 } 151 152 if (count != scope_size) { 153 print('Names found in scope:'); 154 var names = scope.scopeObject().propertyNames(); 155 for (var i = 0; i < names.length; i++) { 156 print(names[i]); 157 } 158 } 159 assertEquals(count, scope_size); 160 161 // Get the debug command processor. 162 var dcp = exec_state.debugCommandProcessor("unspecified_running_state"); 163 164 // Send a scope request for information on a single scope and check the 165 // result. 166 var request_json = '{"seq":0,"type":"request","command":"scope","arguments":{"number":'; 167 request_json += scope.scopeIndex(); 168 request_json += '}}'; 169 var response_json = dcp.processDebugJSONRequest(request_json); 170 var response = JSON.parse(response_json); 171 assertEquals(scope.scopeType(), response.body.type); 172 assertEquals(number, response.body.index); 173 if (scope.scopeType() == debug.ScopeType.Local || 174 scope.scopeType() == debug.ScopeType.Closure) { 175 assertTrue(response.body.object.ref < 0); 176 } else { 177 assertTrue(response.body.object.ref >= 0); 178 } 179 var found = false; 180 for (var i = 0; i < response.refs.length && !found; i++) { 181 found = response.refs[i].handle == response.body.object.ref; 182 } 183 assertTrue(found, "Scope object " + response.body.object.ref + " not found"); 184 } 185 186 187 // Simple closure formed by returning an inner function referering to an outer 188 // block local variable and an outer function's parameter. Due to VM 189 // optimizations parts of the actual closure is missing from the debugger 190 // information. 191 BeginTest("Closure 1"); 192 193 function closure_1(a) { 194 var x = 2; 195 let y = 3; 196 if (true) { 197 let z = 4; 198 function f() { 199 debugger; 200 return a + x + y + z; 201 }; 202 return f; 203 } 204 } 205 206 listener_delegate = function(exec_state) { 207 CheckScopeChain([debug.ScopeType.Local, 208 debug.ScopeType.Block, 209 debug.ScopeType.Closure, 210 debug.ScopeType.Global], exec_state); 211 CheckScopeContent({}, 0, exec_state); 212 CheckScopeContent({z:4}, 1, exec_state); 213 CheckScopeContent({a:1,x:2,y:3}, 2, exec_state); 214 }; 215 closure_1(1)(); 216 EndTest(); 217