Index: sky/engine/bindings/core/v8/DebuggerScript.js |
diff --git a/sky/engine/bindings/core/v8/DebuggerScript.js b/sky/engine/bindings/core/v8/DebuggerScript.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..695586874c2434e848972ee71d7c81babf9ba90e |
--- /dev/null |
+++ b/sky/engine/bindings/core/v8/DebuggerScript.js |
@@ -0,0 +1,548 @@ |
+/* |
+ * Copyright (C) 2010 Google Inc. All rights reserved. |
+ * |
+ * Redistribution and use in source and binary forms, with or without |
+ * modification, are permitted provided that the following conditions are |
+ * met: |
+ * |
+ * * Redistributions of source code must retain the above copyright |
+ * notice, this list of conditions and the following disclaimer. |
+ * * Redistributions in binary form must reproduce the above |
+ * copyright notice, this list of conditions and the following disclaimer |
+ * in the documentation and/or other materials provided with the |
+ * distribution. |
+ * * Neither the name of Google Inc. nor the names of its |
+ * contributors may be used to endorse or promote products derived from |
+ * this software without specific prior written permission. |
+ * |
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ */ |
+ |
+(function () { |
+ |
+var DebuggerScript = {}; |
+ |
+DebuggerScript.PauseOnExceptionsState = { |
+ DontPauseOnExceptions: 0, |
+ PauseOnAllExceptions: 1, |
+ PauseOnUncaughtExceptions: 2 |
+}; |
+ |
+DebuggerScript.ScopeInfoDetails = { |
+ AllScopes: 0, |
+ FastAsyncScopes: 1, |
+ NoScopes: 2 |
+}; |
+ |
+DebuggerScript._pauseOnExceptionsState = DebuggerScript.PauseOnExceptionsState.DontPauseOnExceptions; |
+Debug.clearBreakOnException(); |
+Debug.clearBreakOnUncaughtException(); |
+ |
+DebuggerScript.getAfterCompileScript = function(eventData) |
+{ |
+ return DebuggerScript._formatScript(eventData.script_.script_); |
+} |
+ |
+DebuggerScript.getWorkerScripts = function() |
+{ |
+ var result = []; |
+ var scripts = Debug.scripts(); |
+ for (var i = 0; i < scripts.length; ++i) { |
+ var script = scripts[i]; |
+ // Workers don't share same V8 heap now so there is no need to complicate stuff with |
+ // the context id like we do to discriminate between scripts from different pages. |
+ // However we need to filter out v8 native scripts. |
+ if (script.context_data && script.context_data === "worker") |
+ result.push(DebuggerScript._formatScript(script)); |
+ } |
+ return result; |
+} |
+ |
+DebuggerScript.getFunctionScopes = function(fun) |
+{ |
+ var mirror = MakeMirror(fun); |
+ var count = mirror.scopeCount(); |
+ if (count == 0) |
+ return null; |
+ var result = []; |
+ for (var i = 0; i < count; i++) { |
+ var scopeDetails = mirror.scope(i).details(); |
+ result[i] = { |
+ type: scopeDetails.type(), |
+ object: DebuggerScript._buildScopeObject(scopeDetails.type(), scopeDetails.object()) |
+ }; |
+ } |
+ return result; |
+} |
+ |
+DebuggerScript.getCollectionEntries = function(object) |
+{ |
+ var mirror = MakeMirror(object, true /* transient */); |
+ if (mirror.isMap()) |
+ return mirror.entries(); |
+ if (mirror.isSet()) { |
+ var result = []; |
+ var values = mirror.values(); |
+ for (var i = 0; i < values.length; ++i) |
+ result.push({ value: values[i] }); |
+ return result; |
+ } |
+} |
+ |
+DebuggerScript.getInternalProperties = function(value) |
+{ |
+ var properties = ObjectMirror.GetInternalProperties(value); |
+ var result = []; |
+ for (var i = 0; i < properties.length; i++) { |
+ var mirror = properties[i]; |
+ result.push({ |
+ name: mirror.name(), |
+ value: mirror.value().value() |
+ }); |
+ } |
+ return result; |
+} |
+ |
+DebuggerScript.setFunctionVariableValue = function(functionValue, scopeIndex, variableName, newValue) |
+{ |
+ var mirror = MakeMirror(functionValue); |
+ if (!mirror.isFunction()) |
+ throw new Error("Function value has incorrect type"); |
+ return DebuggerScript._setScopeVariableValue(mirror, scopeIndex, variableName, newValue); |
+} |
+ |
+DebuggerScript._setScopeVariableValue = function(scopeHolder, scopeIndex, variableName, newValue) |
+{ |
+ var scopeMirror = scopeHolder.scope(scopeIndex); |
+ if (!scopeMirror) |
+ throw new Error("Incorrect scope index"); |
+ scopeMirror.setVariableValue(variableName, newValue); |
+ return undefined; |
+} |
+ |
+DebuggerScript.getScripts = function(contextData) |
+{ |
+ var result = []; |
+ |
+ if (!contextData) |
+ return result; |
+ var comma = contextData.indexOf(","); |
+ if (comma === -1) |
+ return result; |
+ // Context data is a string in the following format: |
+ // ("page"|"injected")","<page id> |
+ var idSuffix = contextData.substring(comma); // including the comma |
+ |
+ var scripts = Debug.scripts(); |
+ for (var i = 0; i < scripts.length; ++i) { |
+ var script = scripts[i]; |
+ if (script.context_data && script.context_data.lastIndexOf(idSuffix) != -1) |
+ result.push(DebuggerScript._formatScript(script)); |
+ } |
+ return result; |
+} |
+ |
+DebuggerScript._formatScript = function(script) |
+{ |
+ var lineEnds = script.line_ends; |
+ var lineCount = lineEnds.length; |
+ var endLine = script.line_offset + lineCount - 1; |
+ var endColumn; |
+ // V8 will not count last line if script source ends with \n. |
+ if (script.source[script.source.length - 1] === '\n') { |
+ endLine += 1; |
+ endColumn = 0; |
+ } else { |
+ if (lineCount === 1) |
+ endColumn = script.source.length + script.column_offset; |
+ else |
+ endColumn = script.source.length - (lineEnds[lineCount - 2] + 1); |
+ } |
+ |
+ return { |
+ id: script.id, |
+ name: script.nameOrSourceURL(), |
+ sourceURL: script.source_url, |
+ sourceMappingURL: script.source_mapping_url, |
+ source: script.source, |
+ startLine: script.line_offset, |
+ startColumn: script.column_offset, |
+ endLine: endLine, |
+ endColumn: endColumn, |
+ isContentScript: !!script.context_data && script.context_data.indexOf("injected") == 0 |
+ }; |
+} |
+ |
+DebuggerScript.setBreakpoint = function(execState, info) |
+{ |
+ var positionAlignment = info.interstatementLocation ? Debug.BreakPositionAlignment.BreakPosition : Debug.BreakPositionAlignment.Statement; |
+ var breakId = Debug.setScriptBreakPointById(info.sourceID, info.lineNumber, info.columnNumber, info.condition, undefined, positionAlignment); |
+ |
+ var locations = Debug.findBreakPointActualLocations(breakId); |
+ if (!locations.length) |
+ return undefined; |
+ info.lineNumber = locations[0].line; |
+ info.columnNumber = locations[0].column; |
+ return breakId.toString(); |
+} |
+ |
+DebuggerScript.removeBreakpoint = function(execState, info) |
+{ |
+ Debug.findBreakPoint(info.breakpointId, true); |
+} |
+ |
+DebuggerScript.pauseOnExceptionsState = function() |
+{ |
+ return DebuggerScript._pauseOnExceptionsState; |
+} |
+ |
+DebuggerScript.setPauseOnExceptionsState = function(newState) |
+{ |
+ DebuggerScript._pauseOnExceptionsState = newState; |
+ |
+ if (DebuggerScript.PauseOnExceptionsState.PauseOnAllExceptions === newState) |
+ Debug.setBreakOnException(); |
+ else |
+ Debug.clearBreakOnException(); |
+ |
+ if (DebuggerScript.PauseOnExceptionsState.PauseOnUncaughtExceptions === newState) |
+ Debug.setBreakOnUncaughtException(); |
+ else |
+ Debug.clearBreakOnUncaughtException(); |
+} |
+ |
+DebuggerScript.frameCount = function(execState) |
+{ |
+ return execState.frameCount(); |
+} |
+ |
+DebuggerScript.currentCallFrame = function(execState, data) |
+{ |
+ var maximumLimit = data >> 2; |
+ var scopeDetailsLevel = data & 3; |
+ |
+ var frameCount = execState.frameCount(); |
+ if (maximumLimit && maximumLimit < frameCount) |
+ frameCount = maximumLimit; |
+ var topFrame = undefined; |
+ for (var i = frameCount - 1; i >= 0; i--) { |
+ var frameMirror = execState.frame(i); |
+ topFrame = DebuggerScript._frameMirrorToJSCallFrame(frameMirror, topFrame, scopeDetailsLevel); |
+ } |
+ return topFrame; |
+} |
+ |
+DebuggerScript.currentCallFrameByIndex = function(execState, index) |
+{ |
+ if (index < 0) |
+ return undefined; |
+ var frameCount = execState.frameCount(); |
+ if (index >= frameCount) |
+ return undefined; |
+ return DebuggerScript._frameMirrorToJSCallFrame(execState.frame(index), undefined, DebuggerScript.ScopeInfoDetails.NoScopes); |
+} |
+ |
+DebuggerScript.stepIntoStatement = function(execState) |
+{ |
+ execState.prepareStep(Debug.StepAction.StepIn, 1); |
+} |
+ |
+DebuggerScript.stepOverStatement = function(execState, callFrame) |
+{ |
+ execState.prepareStep(Debug.StepAction.StepNext, 1); |
+} |
+ |
+DebuggerScript.stepOutOfFunction = function(execState, callFrame) |
+{ |
+ execState.prepareStep(Debug.StepAction.StepOut, 1); |
+} |
+ |
+// Returns array in form: |
+// [ 0, <v8_result_report> ] in case of success |
+// or [ 1, <general_error_message>, <compiler_message>, <line_number>, <column_number> ] in case of compile error, numbers are 1-based. |
+// or throws exception with message. |
+DebuggerScript.liveEditScriptSource = function(scriptId, newSource, preview) |
+{ |
+ var scripts = Debug.scripts(); |
+ var scriptToEdit = null; |
+ for (var i = 0; i < scripts.length; i++) { |
+ if (scripts[i].id == scriptId) { |
+ scriptToEdit = scripts[i]; |
+ break; |
+ } |
+ } |
+ if (!scriptToEdit) |
+ throw("Script not found"); |
+ |
+ var changeLog = []; |
+ try { |
+ var result = Debug.LiveEdit.SetScriptSource(scriptToEdit, newSource, preview, changeLog); |
+ return [0, result]; |
+ } catch (e) { |
+ if (e instanceof Debug.LiveEdit.Failure && "details" in e) { |
+ var details = e.details; |
+ if (details.type === "liveedit_compile_error") { |
+ var startPosition = details.position.start; |
+ return [1, String(e), String(details.syntaxErrorMessage), Number(startPosition.line), Number(startPosition.column)]; |
+ } |
+ } |
+ throw e; |
+ } |
+} |
+ |
+DebuggerScript.clearBreakpoints = function(execState, info) |
+{ |
+ Debug.clearAllBreakPoints(); |
+} |
+ |
+DebuggerScript.setBreakpointsActivated = function(execState, info) |
+{ |
+ Debug.debuggerFlags().breakPointsActive.setValue(info.enabled); |
+} |
+ |
+DebuggerScript.getScriptSource = function(eventData) |
+{ |
+ return eventData.script().source(); |
+} |
+ |
+DebuggerScript.setScriptSource = function(eventData, source) |
+{ |
+ if (eventData.script().data() === "injected-script") |
+ return; |
+ eventData.script().setSource(source); |
+} |
+ |
+DebuggerScript.getScriptName = function(eventData) |
+{ |
+ return eventData.script().script_.nameOrSourceURL(); |
+} |
+ |
+DebuggerScript.getBreakpointNumbers = function(eventData) |
+{ |
+ var breakpoints = eventData.breakPointsHit(); |
+ var numbers = []; |
+ if (!breakpoints) |
+ return numbers; |
+ |
+ for (var i = 0; i < breakpoints.length; i++) { |
+ var breakpoint = breakpoints[i]; |
+ var scriptBreakPoint = breakpoint.script_break_point(); |
+ numbers.push(scriptBreakPoint ? scriptBreakPoint.number() : breakpoint.number()); |
+ } |
+ return numbers; |
+} |
+ |
+DebuggerScript.isEvalCompilation = function(eventData) |
+{ |
+ var script = eventData.script(); |
+ return (script.compilationType() === Debug.ScriptCompilationType.Eval); |
+} |
+ |
+// NOTE: This function is performance critical, as it can be run on every |
+// statement that generates an async event (like addEventListener) to support |
+// asynchronous call stacks. Thus, when possible, initialize the data lazily. |
+DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror, callerFrame, scopeDetailsLevel) |
+{ |
+ // Stuff that can not be initialized lazily (i.e. valid while paused with a valid break_id). |
+ // The frameMirror and scopeMirror can be accessed only while paused on the debugger. |
+ var frameDetails = frameMirror.details(); |
+ |
+ var funcObject = frameDetails.func(); |
+ var sourcePosition = frameDetails.sourcePosition(); |
+ var thisObject = frameDetails.receiver(); |
+ |
+ var isAtReturn = !!frameDetails.isAtReturn(); |
+ var returnValue = isAtReturn ? frameDetails.returnValue() : undefined; |
+ |
+ var scopeMirrors = (scopeDetailsLevel === DebuggerScript.ScopeInfoDetails.NoScopes ? [] : frameMirror.allScopes(scopeDetailsLevel === DebuggerScript.ScopeInfoDetails.FastAsyncScopes)); |
+ var scopeTypes = new Array(scopeMirrors.length); |
+ var scopeObjects = new Array(scopeMirrors.length); |
+ for (var i = 0; i < scopeMirrors.length; ++i) { |
+ var scopeDetails = scopeMirrors[i].details(); |
+ scopeTypes[i] = scopeDetails.type(); |
+ scopeObjects[i] = scopeDetails.object(); |
+ } |
+ |
+ // Calculated lazily. |
+ var scopeChain; |
+ var funcMirror; |
+ var location; |
+ |
+ function lazyScopeChain() |
+ { |
+ if (!scopeChain) { |
+ scopeChain = []; |
+ for (var i = 0; i < scopeObjects.length; ++i) |
+ scopeChain.push(DebuggerScript._buildScopeObject(scopeTypes[i], scopeObjects[i])); |
+ scopeObjects = null; // Free for GC. |
+ } |
+ return scopeChain; |
+ } |
+ |
+ function ensureFuncMirror() |
+ { |
+ if (!funcMirror) { |
+ funcMirror = MakeMirror(funcObject); |
+ if (!funcMirror.isFunction()) |
+ funcMirror = new UnresolvedFunctionMirror(funcObject); |
+ } |
+ return funcMirror; |
+ } |
+ |
+ function ensureLocation() |
+ { |
+ if (!location) { |
+ var script = ensureFuncMirror().script(); |
+ if (script) |
+ location = script.locationFromPosition(sourcePosition, true); |
+ if (!location) |
+ location = { line: 0, column: 0 }; |
+ } |
+ return location; |
+ } |
+ |
+ function line() |
+ { |
+ return ensureLocation().line; |
+ } |
+ |
+ function column() |
+ { |
+ return ensureLocation().column; |
+ } |
+ |
+ function sourceID() |
+ { |
+ var script = ensureFuncMirror().script(); |
+ return script && script.id(); |
+ } |
+ |
+ function scriptName() |
+ { |
+ var script = ensureFuncMirror().script(); |
+ return script && script.name(); |
+ } |
+ |
+ function functionName() |
+ { |
+ var func = ensureFuncMirror(); |
+ if (!func.resolved()) |
+ return undefined; |
+ var displayName; |
+ var valueMirror = func.property("displayName").value(); |
+ if (valueMirror && valueMirror.isString()) |
+ displayName = valueMirror.value(); |
+ return displayName || func.name() || func.inferredName(); |
+ } |
+ |
+ function evaluate(expression) |
+ { |
+ return frameMirror.evaluate(expression, false).value(); |
+ } |
+ |
+ function restart() |
+ { |
+ return Debug.LiveEdit.RestartFrame(frameMirror); |
+ } |
+ |
+ function setVariableValue(scopeNumber, variableName, newValue) |
+ { |
+ return DebuggerScript._setScopeVariableValue(frameMirror, scopeNumber, variableName, newValue); |
+ } |
+ |
+ function stepInPositions() |
+ { |
+ var stepInPositionsV8 = frameMirror.stepInPositions(); |
+ var stepInPositionsProtocol; |
+ if (stepInPositionsV8) { |
+ stepInPositionsProtocol = []; |
+ var script = ensureFuncMirror().script(); |
+ if (script) { |
+ var scriptId = String(script.id()); |
+ for (var i = 0; i < stepInPositionsV8.length; i++) { |
+ var item = { |
+ scriptId: scriptId, |
+ lineNumber: stepInPositionsV8[i].position.line, |
+ columnNumber: stepInPositionsV8[i].position.column |
+ }; |
+ stepInPositionsProtocol.push(item); |
+ } |
+ } |
+ } |
+ return JSON.stringify(stepInPositionsProtocol); |
+ } |
+ |
+ return { |
+ "sourceID": sourceID, |
+ "line": line, |
+ "column": column, |
+ "scriptName": scriptName, |
+ "functionName": functionName, |
+ "thisObject": thisObject, |
+ "scopeChain": lazyScopeChain, |
+ "scopeType": scopeTypes, |
+ "evaluate": evaluate, |
+ "caller": callerFrame, |
+ "restart": restart, |
+ "setVariableValue": setVariableValue, |
+ "stepInPositions": stepInPositions, |
+ "isAtReturn": isAtReturn, |
+ "returnValue": returnValue |
+ }; |
+} |
+ |
+DebuggerScript._buildScopeObject = function(scopeType, scopeObject) |
+{ |
+ var result; |
+ switch (scopeType) { |
+ case ScopeType.Local: |
+ case ScopeType.Closure: |
+ case ScopeType.Catch: |
+ // For transient objects we create a "persistent" copy that contains |
+ // the same properties. |
+ // Reset scope object prototype to null so that the proto properties |
+ // don't appear in the local scope section. |
+ result = { __proto__: null }; |
+ var properties = MakeMirror(scopeObject, true /* transient */).properties(); |
+ for (var j = 0; j < properties.length; j++) { |
+ var name = properties[j].name(); |
+ if (name.charAt(0) === ".") |
+ continue; // Skip internal variables like ".arguments" |
+ result[name] = properties[j].value_; |
+ } |
+ break; |
+ case ScopeType.Global: |
+ case ScopeType.With: |
+ result = scopeObject; |
+ break; |
+ case ScopeType.Block: |
+ // Unsupported yet. Mustn't be reachable. |
+ break; |
+ } |
+ return result; |
+} |
+ |
+DebuggerScript.getPromiseDetails = function(eventData) |
+{ |
+ return { |
+ "promise": eventData.promise().value(), |
+ "parentPromise": eventData.parentPromise().value(), |
+ "status": eventData.status() |
+ }; |
+} |
+ |
+// We never resolve Mirror by its handle so to avoid memory leaks caused by Mirrors in the cache we disable it. |
+ToggleMirrorCache(false); |
+ |
+return DebuggerScript; |
+})(); |