1 /* 2 * Copyright (C) 2010 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 (function () { 32 33 var DebuggerScript = {}; 34 35 DebuggerScript.PauseOnExceptionsState = { 36 DontPauseOnExceptions : 0, 37 PauseOnAllExceptions : 1, 38 PauseOnUncaughtExceptions: 2 39 }; 40 41 DebuggerScript._pauseOnExceptionsState = DebuggerScript.PauseOnExceptionsState.DontPauseOnExceptions; 42 Debug.clearBreakOnException(); 43 Debug.clearBreakOnUncaughtException(); 44 45 DebuggerScript.getAfterCompileScript = function(eventData) 46 { 47 return DebuggerScript._formatScript(eventData.script_.script_); 48 } 49 50 DebuggerScript.getWorkerScripts = function() 51 { 52 var result = []; 53 var scripts = Debug.scripts(); 54 for (var i = 0; i < scripts.length; ++i) { 55 var script = scripts[i]; 56 // Workers don't share same V8 heap now so there is no need to complicate stuff with 57 // the context id like we do to discriminate between scripts from different pages. 58 // However we need to filter out v8 native scripts. 59 if (script.context_data && script.context_data === "worker") 60 result.push(DebuggerScript._formatScript(script)); 61 } 62 return result; 63 } 64 65 DebuggerScript.getFunctionScopes = function(fun) 66 { 67 var mirror = MakeMirror(fun); 68 var count = mirror.scopeCount(); 69 if (count == 0) 70 return null; 71 var result = []; 72 for (var i = 0; i < count; i++) { 73 var scopeMirror = mirror.scope(i); 74 result[i] = { 75 type: scopeMirror.scopeType(), 76 object: DebuggerScript._buildScopeObject(scopeMirror) 77 }; 78 } 79 return result; 80 } 81 82 DebuggerScript.getInternalProperties = function(value) 83 { 84 var properties = ObjectMirror.GetInternalProperties(value); 85 var result = []; 86 for (var i = 0; i < properties.length; i++) { 87 var mirror = properties[i]; 88 result.push({ 89 name: mirror.name(), 90 value: mirror.value().value() 91 }); 92 } 93 return result; 94 } 95 96 DebuggerScript.setFunctionVariableValue = function(functionValue, scopeIndex, variableName, newValue) 97 { 98 var mirror = MakeMirror(functionValue); 99 if (!mirror.isFunction()) 100 throw new Error("Function value has incorrect type"); 101 return DebuggerScript._setScopeVariableValue(mirror, scopeIndex, variableName, newValue); 102 } 103 104 DebuggerScript._setScopeVariableValue = function(scopeHolder, scopeIndex, variableName, newValue) 105 { 106 var scopeMirror = scopeHolder.scope(scopeIndex); 107 if (!scopeMirror) 108 throw new Error("Incorrect scope index"); 109 scopeMirror.setVariableValue(variableName, newValue); 110 return undefined; 111 } 112 113 DebuggerScript.getScripts = function(contextData) 114 { 115 var result = []; 116 117 if (!contextData) 118 return result; 119 var comma = contextData.indexOf(","); 120 if (comma === -1) 121 return result; 122 // Context data is a string in the following format: 123 // ("page"|"injected")","<page id> 124 var idSuffix = contextData.substring(comma); // including the comma 125 126 var scripts = Debug.scripts(); 127 for (var i = 0; i < scripts.length; ++i) { 128 var script = scripts[i]; 129 if (script.context_data && script.context_data.lastIndexOf(idSuffix) != -1) 130 result.push(DebuggerScript._formatScript(script)); 131 } 132 return result; 133 } 134 135 DebuggerScript._formatScript = function(script) 136 { 137 var lineEnds = script.line_ends; 138 var lineCount = lineEnds.length; 139 var endLine = script.line_offset + lineCount - 1; 140 var endColumn; 141 // V8 will not count last line if script source ends with \n. 142 if (script.source[script.source.length - 1] === '\n') { 143 endLine += 1; 144 endColumn = 0; 145 } else { 146 if (lineCount === 1) 147 endColumn = script.source.length + script.column_offset; 148 else 149 endColumn = script.source.length - (lineEnds[lineCount - 2] + 1); 150 } 151 152 return { 153 id: script.id, 154 name: script.nameOrSourceURL(), 155 source: script.source, 156 startLine: script.line_offset, 157 startColumn: script.column_offset, 158 endLine: endLine, 159 endColumn: endColumn, 160 isContentScript: !!script.context_data && script.context_data.indexOf("injected") == 0 161 }; 162 } 163 164 DebuggerScript.setBreakpoint = function(execState, info) 165 { 166 var positionAlignment = info.interstatementLocation ? Debug.BreakPositionAlignment.BreakPosition : Debug.BreakPositionAlignment.Statement; 167 var breakId = Debug.setScriptBreakPointById(info.sourceID, info.lineNumber, info.columnNumber, info.condition, undefined, positionAlignment); 168 169 var locations = Debug.findBreakPointActualLocations(breakId); 170 if (!locations.length) 171 return undefined; 172 info.lineNumber = locations[0].line; 173 info.columnNumber = locations[0].column; 174 return breakId.toString(); 175 } 176 177 DebuggerScript.removeBreakpoint = function(execState, info) 178 { 179 Debug.findBreakPoint(info.breakpointId, true); 180 } 181 182 DebuggerScript.pauseOnExceptionsState = function() 183 { 184 return DebuggerScript._pauseOnExceptionsState; 185 } 186 187 DebuggerScript.setPauseOnExceptionsState = function(newState) 188 { 189 DebuggerScript._pauseOnExceptionsState = newState; 190 191 if (DebuggerScript.PauseOnExceptionsState.PauseOnAllExceptions === newState) 192 Debug.setBreakOnException(); 193 else 194 Debug.clearBreakOnException(); 195 196 if (DebuggerScript.PauseOnExceptionsState.PauseOnUncaughtExceptions === newState) 197 Debug.setBreakOnUncaughtException(); 198 else 199 Debug.clearBreakOnUncaughtException(); 200 } 201 202 DebuggerScript.currentCallFrame = function(execState, maximumLimit) 203 { 204 var frameCount = execState.frameCount(); 205 if (maximumLimit >= 0 && maximumLimit < frameCount) 206 frameCount = maximumLimit; 207 var topFrame = undefined; 208 for (var i = frameCount - 1; i >= 0; i--) { 209 var frameMirror = execState.frame(i); 210 topFrame = DebuggerScript._frameMirrorToJSCallFrame(frameMirror, topFrame); 211 } 212 return topFrame; 213 } 214 215 DebuggerScript.stepIntoStatement = function(execState) 216 { 217 execState.prepareStep(Debug.StepAction.StepIn, 1); 218 } 219 220 DebuggerScript.stepOverStatement = function(execState, callFrame) 221 { 222 var frameMirror = callFrame ? callFrame.frameMirror : undefined; 223 execState.prepareStep(Debug.StepAction.StepNext, 1, frameMirror); 224 } 225 226 DebuggerScript.stepOutOfFunction = function(execState, callFrame) 227 { 228 var frameMirror = callFrame ? callFrame.frameMirror : undefined; 229 execState.prepareStep(Debug.StepAction.StepOut, 1, frameMirror); 230 } 231 232 // Returns array in form: 233 // [ 0, <v8_result_report> ] in case of success 234 // or [ 1, <general_error_message>, <compiler_message>, <line_number>, <column_number> ] in case of compile error, numbers are 1-based. 235 // or throws exception with message. 236 DebuggerScript.liveEditScriptSource = function(scriptId, newSource, preview) 237 { 238 var scripts = Debug.scripts(); 239 var scriptToEdit = null; 240 for (var i = 0; i < scripts.length; i++) { 241 if (scripts[i].id == scriptId) { 242 scriptToEdit = scripts[i]; 243 break; 244 } 245 } 246 if (!scriptToEdit) 247 throw("Script not found"); 248 249 var changeLog = []; 250 try { 251 var result = Debug.LiveEdit.SetScriptSource(scriptToEdit, newSource, preview, changeLog); 252 return [0, result]; 253 } catch (e) { 254 if (e instanceof Debug.LiveEdit.Failure && "details" in e) { 255 var details = e.details; 256 if (details.type === "liveedit_compile_error") { 257 var startPosition = details.position.start; 258 return [1, String(e), String(details.syntaxErrorMessage), Number(startPosition.line), Number(startPosition.column)]; 259 } 260 } 261 throw e; 262 } 263 } 264 265 DebuggerScript.clearBreakpoints = function(execState, info) 266 { 267 Debug.clearAllBreakPoints(); 268 } 269 270 DebuggerScript.setBreakpointsActivated = function(execState, info) 271 { 272 Debug.debuggerFlags().breakPointsActive.setValue(info.enabled); 273 } 274 275 DebuggerScript.getScriptSource = function(eventData) 276 { 277 return eventData.script().source(); 278 } 279 280 DebuggerScript.setScriptSource = function(eventData, source) 281 { 282 if (eventData.script().data() === "injected-script") 283 return; 284 eventData.script().setSource(source); 285 } 286 287 DebuggerScript.getScriptName = function(eventData) 288 { 289 return eventData.script().script_.nameOrSourceURL(); 290 } 291 292 DebuggerScript.getBreakpointNumbers = function(eventData) 293 { 294 var breakpoints = eventData.breakPointsHit(); 295 var numbers = []; 296 if (!breakpoints) 297 return numbers; 298 299 for (var i = 0; i < breakpoints.length; i++) { 300 var breakpoint = breakpoints[i]; 301 var scriptBreakPoint = breakpoint.script_break_point(); 302 numbers.push(scriptBreakPoint ? scriptBreakPoint.number() : breakpoint.number()); 303 } 304 return numbers; 305 } 306 307 DebuggerScript.isEvalCompilation = function(eventData) 308 { 309 var script = eventData.script(); 310 return (script.compilationType() === Debug.ScriptCompilationType.Eval); 311 } 312 313 DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror, callerFrame) 314 { 315 // Get function name and display name. 316 var funcMirror; 317 var displayName; 318 try { 319 funcMirror = frameMirror.func(); 320 if (funcMirror) { 321 var valueMirror = funcMirror.property("displayName").value(); 322 if (valueMirror && valueMirror.isString()) 323 displayName = valueMirror.value(); 324 } 325 } catch(e) { 326 } 327 var functionName; 328 if (funcMirror) 329 functionName = displayName || funcMirror.name() || funcMirror.inferredName(); 330 331 // Get script ID. 332 var script = funcMirror.script(); 333 var sourceID = script && script.id(); 334 335 // Get location. 336 var location = frameMirror.sourceLocation(); 337 338 // Get this object. 339 var thisObject = frameMirror.details_.receiver(); 340 341 var isAtReturn = !!frameMirror.details_.isAtReturn(); 342 var returnValue = isAtReturn ? frameMirror.details_.returnValue() : undefined; 343 344 var scopeChain = []; 345 var scopeType = []; 346 for (var i = 0; i < frameMirror.scopeCount(); i++) { 347 var scopeMirror = frameMirror.scope(i); 348 scopeType.push(scopeMirror.scopeType()); 349 scopeChain.push(DebuggerScript._buildScopeObject(scopeMirror)); 350 } 351 352 function evaluate(expression) 353 { 354 return frameMirror.evaluate(expression, false).value(); 355 } 356 357 function restart() 358 { 359 return Debug.LiveEdit.RestartFrame(frameMirror); 360 } 361 362 function setVariableValue(scopeNumber, variableName, newValue) 363 { 364 return DebuggerScript._setScopeVariableValue(frameMirror, scopeNumber, variableName, newValue); 365 } 366 367 function stepInPositions() 368 { 369 var stepInPositionsV8 = frameMirror.stepInPositions(); 370 var stepInPositionsProtocol; 371 if (stepInPositionsV8) { 372 stepInPositionsProtocol = []; 373 var script = frameMirror.func().script(); 374 if (script) { 375 var scriptId = String(script.id()); 376 for (var i = 0; i < stepInPositionsV8.length; i++) { 377 var item = { 378 scriptId: scriptId, 379 lineNumber: stepInPositionsV8[i].position.line, 380 columnNumber: stepInPositionsV8[i].position.column 381 }; 382 stepInPositionsProtocol.push(item); 383 } 384 } 385 } 386 return JSON.stringify(stepInPositionsProtocol); 387 } 388 389 return { 390 "sourceID": sourceID, 391 "line": location ? location.line : 0, 392 "column": location ? location.column : 0, 393 "functionName": functionName, 394 "thisObject": thisObject, 395 "scopeChain": scopeChain, 396 "scopeType": scopeType, 397 "evaluate": evaluate, 398 "caller": callerFrame, 399 "restart": restart, 400 "setVariableValue": setVariableValue, 401 "stepInPositions": stepInPositions, 402 "isAtReturn": isAtReturn, 403 "returnValue": returnValue, 404 "frameMirror": frameMirror 405 }; 406 } 407 408 DebuggerScript._buildScopeObject = function(scopeMirror) { 409 var scopeObject; 410 switch (scopeMirror.scopeType()) { 411 case ScopeType.Local: 412 case ScopeType.Closure: 413 case ScopeType.Catch: 414 // For transient objects we create a "persistent" copy that contains 415 // the same properties. 416 scopeObject = {}; 417 // Reset scope object prototype to null so that the proto properties 418 // don't appear in the local scope section. 419 scopeObject.__proto__ = null; 420 var scopeObjectMirror = scopeMirror.scopeObject(); 421 var properties = scopeObjectMirror.properties(); 422 for (var j = 0; j < properties.length; j++) { 423 var name = properties[j].name(); 424 if (name.charAt(0) === ".") 425 continue; // Skip internal variables like ".arguments" 426 scopeObject[name] = properties[j].value_; 427 } 428 break; 429 case ScopeType.Global: 430 case ScopeType.With: 431 scopeObject = scopeMirror.details_.object(); 432 break; 433 case ScopeType.Block: 434 // Unsupported yet. Mustn't be reachable. 435 break; 436 } 437 return scopeObject; 438 } 439 440 return DebuggerScript; 441 })(); 442