| /* | 
 |  * 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. | 
 |  */ | 
 | "use strict"; | 
 |  | 
 | (function () { | 
 |  | 
 | var DebuggerScript = {}; | 
 |  | 
 | /** @type {!Map<!ScopeType, string>} */ | 
 | DebuggerScript._scopeTypeNames = new Map(); | 
 | DebuggerScript._scopeTypeNames.set(ScopeType.Global, "global"); | 
 | DebuggerScript._scopeTypeNames.set(ScopeType.Local, "local"); | 
 | DebuggerScript._scopeTypeNames.set(ScopeType.With, "with"); | 
 | DebuggerScript._scopeTypeNames.set(ScopeType.Closure, "closure"); | 
 | DebuggerScript._scopeTypeNames.set(ScopeType.Catch, "catch"); | 
 | DebuggerScript._scopeTypeNames.set(ScopeType.Block, "block"); | 
 | DebuggerScript._scopeTypeNames.set(ScopeType.Script, "script"); | 
 | DebuggerScript._scopeTypeNames.set(ScopeType.Eval, "eval"); | 
 | DebuggerScript._scopeTypeNames.set(ScopeType.Module, "module"); | 
 |  | 
 | /** | 
 |  * @param {function()} fun | 
 |  * @return {?Array<!Scope>} | 
 |  */ | 
 | DebuggerScript.getFunctionScopes = function(fun) | 
 | { | 
 |     var mirror = MakeMirror(fun); | 
 |     if (!mirror.isFunction()) | 
 |         return null; | 
 |     var functionMirror = /** @type {!FunctionMirror} */(mirror); | 
 |     var count = functionMirror.scopeCount(); | 
 |     if (count == 0) | 
 |         return null; | 
 |     var result = []; | 
 |     for (var i = 0; i < count; i++) { | 
 |         var scopeDetails = functionMirror.scope(i).details(); | 
 |         var scopeObject = DebuggerScript._buildScopeObject(scopeDetails.type(), scopeDetails.object()); | 
 |         if (!scopeObject) | 
 |             continue; | 
 |         result.push({ | 
 |             type: /** @type {string} */(DebuggerScript._scopeTypeNames.get(scopeDetails.type())), | 
 |             object: scopeObject, | 
 |             name: scopeDetails.name() || "" | 
 |         }); | 
 |     } | 
 |     return result; | 
 | } | 
 |  | 
 | /** | 
 |  * @param {Object} gen | 
 |  * @return {?Array<!Scope>} | 
 |  */ | 
 | DebuggerScript.getGeneratorScopes = function(gen) | 
 | { | 
 |     var mirror = MakeMirror(gen); | 
 |     if (!mirror.isGenerator()) | 
 |         return null; | 
 |     var generatorMirror = /** @type {!GeneratorMirror} */(mirror); | 
 |     var count = generatorMirror.scopeCount(); | 
 |     if (count == 0) | 
 |         return null; | 
 |     var result = []; | 
 |     for (var i = 0; i < count; i++) { | 
 |         var scopeDetails = generatorMirror.scope(i).details(); | 
 |         var scopeObject = DebuggerScript._buildScopeObject(scopeDetails.type(), scopeDetails.object()); | 
 |         if (!scopeObject) | 
 |             continue; | 
 |         result.push({ | 
 |             type: /** @type {string} */(DebuggerScript._scopeTypeNames.get(scopeDetails.type())), | 
 |             object: scopeObject, | 
 |             name: scopeDetails.name() || "" | 
 |         }); | 
 |     } | 
 |     return result; | 
 | } | 
 |  | 
 | /** | 
 |  * @param {!ExecutionState} execState | 
 |  * @param {!BreakpointInfo} info | 
 |  * @return {string|undefined} | 
 |  */ | 
 | DebuggerScript.setBreakpoint = function(execState, info) | 
 | { | 
 |     var breakId = Debug.setScriptBreakPointById(info.sourceID, info.lineNumber, info.columnNumber, info.condition, undefined, Debug.BreakPositionAlignment.BreakPosition); | 
 |     var locations = Debug.findBreakPointActualLocations(breakId); | 
 |     if (!locations.length) | 
 |         return undefined; | 
 |     info.lineNumber = locations[0].line; | 
 |     info.columnNumber = locations[0].column; | 
 |     return breakId.toString(); | 
 | } | 
 |  | 
 | /** | 
 |  * @param {!ExecutionState} execState | 
 |  * @param {!{breakpointId: number}} info | 
 |  */ | 
 | DebuggerScript.removeBreakpoint = function(execState, info) | 
 | { | 
 |     Debug.findBreakPoint(info.breakpointId, true); | 
 | } | 
 |  | 
 | /** | 
 |  * @param {!ExecutionState} execState | 
 |  * @param {number} limit | 
 |  * @return {!Array<!JavaScriptCallFrame>} | 
 |  */ | 
 | DebuggerScript.currentCallFrames = function(execState, limit) | 
 | { | 
 |     var frames = []; | 
 |     for (var i = 0; i < execState.frameCount() && (!limit || i < limit); ++i) | 
 |         frames.push(DebuggerScript._frameMirrorToJSCallFrame(execState.frame(i))); | 
 |     return frames; | 
 | } | 
 |  | 
 | // 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. | 
 | /** | 
 |  * @param {number} scriptId | 
 |  * @param {string} newSource | 
 |  * @param {boolean} preview | 
 |  * @return {!Array<*>} | 
 |  */ | 
 | 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.stack_modified]; | 
 |     } catch (e) { | 
 |         if (e instanceof Debug.LiveEdit.Failure && "details" in e) { | 
 |             var details = /** @type {!LiveEditErrorDetails} */(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; | 
 |     } | 
 | } | 
 |  | 
 | /** | 
 |  * @param {!ExecutionState} execState | 
 |  */ | 
 | DebuggerScript.clearBreakpoints = function(execState) | 
 | { | 
 |     Debug.clearAllBreakPoints(); | 
 | } | 
 |  | 
 | /** | 
 |  * @param {!Array<!BreakPoint>|undefined} breakpoints | 
 |  */ | 
 | DebuggerScript.getBreakpointNumbers = function(breakpoints) | 
 | { | 
 |     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; | 
 | } | 
 |  | 
 | // 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. | 
 | /** | 
 |  * @param {!FrameMirror} frameMirror | 
 |  * @return {!JavaScriptCallFrame} | 
 |  */ | 
 | DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror) | 
 | { | 
 |     // 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 scriptObject = frameDetails.script(); | 
 |     var sourcePosition = frameDetails.sourcePosition(); | 
 |     var thisObject = frameDetails.receiver(); | 
 |  | 
 |     var isAtReturn = !!frameDetails.isAtReturn(); | 
 |     var returnValue = isAtReturn ? frameDetails.returnValue() : undefined; | 
 |  | 
 |     var scopeMirrors = frameMirror.allScopes(false); | 
 |     /** @type {!Array<number>} */ | 
 |     var scopeTypes = new Array(scopeMirrors.length); | 
 |     /** @type {?Array<!Object>} */ | 
 |     var scopeObjects = new Array(scopeMirrors.length); | 
 |     /** @type {!Array<string|undefined>} */ | 
 |     var scopeNames = new Array(scopeMirrors.length); | 
 |     /** @type {?Array<number>} */ | 
 |     var scopeStartPositions = new Array(scopeMirrors.length); | 
 |     /** @type {?Array<number>} */ | 
 |     var scopeEndPositions = new Array(scopeMirrors.length); | 
 |     /** @type {?Array<function()|null>} */ | 
 |     var scopeFunctions = 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(); | 
 |         scopeNames[i] = scopeDetails.name(); | 
 |         scopeStartPositions[i] = scopeDetails.startPosition ? scopeDetails.startPosition() : 0; | 
 |         scopeEndPositions[i] = scopeDetails.endPosition ? scopeDetails.endPosition() : 0; | 
 |         scopeFunctions[i] = scopeDetails.func ? scopeDetails.func() : null; | 
 |     } | 
 |  | 
 |     // Calculated lazily. | 
 |     var scopeChain; | 
 |     var funcMirror; | 
 |     var scriptMirror; | 
 |     var location; | 
 |     /** @type {!Array<?RawLocation>} */ | 
 |     var scopeStartLocations; | 
 |     /** @type {!Array<?RawLocation>} */ | 
 |     var scopeEndLocations; | 
 |     var details; | 
 |  | 
 |     /** | 
 |      * @param {!ScriptMirror|undefined} script | 
 |      * @param {number} pos | 
 |      * @return {?RawLocation} | 
 |      */ | 
 |     function createLocation(script, pos) | 
 |     { | 
 |         if (!script) | 
 |             return null; | 
 |  | 
 |         var location = script.locationFromPosition(pos, true); | 
 |         return { | 
 |             "lineNumber": location.line, | 
 |             "columnNumber": location.column, | 
 |             "scriptId": String(script.id()) | 
 |         } | 
 |     } | 
 |  | 
 |     /** | 
 |      * @return {!Array<!Object>} | 
 |      */ | 
 |     function ensureScopeChain() | 
 |     { | 
 |         if (!scopeChain) { | 
 |             scopeChain = []; | 
 |             scopeStartLocations = []; | 
 |             scopeEndLocations = []; | 
 |             for (var i = 0, j = 0; i < scopeObjects.length; ++i) { | 
 |                 var scopeObject = DebuggerScript._buildScopeObject(scopeTypes[i], scopeObjects[i]); | 
 |                 if (scopeObject) { | 
 |                     scopeTypes[j] = scopeTypes[i]; | 
 |                     scopeNames[j] = scopeNames[i]; | 
 |                     scopeChain[j] = scopeObject; | 
 |  | 
 |                     var funcMirror = scopeFunctions ? MakeMirror(scopeFunctions[i]) : null; | 
 |                     if (!funcMirror || !funcMirror.isFunction()) | 
 |                         funcMirror = new UnresolvedFunctionMirror(funcObject); | 
 |  | 
 |                     var script = /** @type {!FunctionMirror} */(funcMirror).script(); | 
 |                     scopeStartLocations[j] = createLocation(script, scopeStartPositions[i]); | 
 |                     scopeEndLocations[j] = createLocation(script, scopeEndPositions[i]); | 
 |                     ++j; | 
 |                 } | 
 |             } | 
 |             scopeTypes.length = scopeChain.length; | 
 |             scopeNames.length = scopeChain.length; | 
 |             scopeObjects = null; // Free for GC. | 
 |             scopeFunctions = null; | 
 |             scopeStartPositions = null; | 
 |             scopeEndPositions = null; | 
 |         } | 
 |         return scopeChain; | 
 |     } | 
 |  | 
 |     /** | 
 |      * @return {!JavaScriptCallFrameDetails} | 
 |      */ | 
 |     function lazyDetails() | 
 |     { | 
 |         if (!details) { | 
 |             var scopeObjects = ensureScopeChain(); | 
 |             var script = ensureScriptMirror(); | 
 |             /** @type {!Array<Scope>} */ | 
 |             var scopes = []; | 
 |             for (var i = 0; i < scopeObjects.length; ++i) { | 
 |                 var scope = { | 
 |                     "type": /** @type {string} */(DebuggerScript._scopeTypeNames.get(scopeTypes[i])), | 
 |                     "object": scopeObjects[i], | 
 |                 }; | 
 |                 if (scopeNames[i]) | 
 |                     scope.name = scopeNames[i]; | 
 |                 if (scopeStartLocations[i]) | 
 |                     scope.startLocation = /** @type {!RawLocation} */(scopeStartLocations[i]); | 
 |                 if (scopeEndLocations[i]) | 
 |                     scope.endLocation = /** @type {!RawLocation} */(scopeEndLocations[i]); | 
 |                 scopes.push(scope); | 
 |             } | 
 |             details = { | 
 |                 "functionName": ensureFuncMirror().debugName(), | 
 |                 "location": { | 
 |                     "lineNumber": ensureLocation().line, | 
 |                     "columnNumber": ensureLocation().column, | 
 |                     "scriptId": String(script.id()) | 
 |                 }, | 
 |                 "this": thisObject, | 
 |                 "scopeChain": scopes | 
 |             }; | 
 |             var functionLocation = ensureFuncMirror().sourceLocation(); | 
 |             if (functionLocation) { | 
 |                 details.functionLocation = { | 
 |                     "lineNumber": functionLocation.line, | 
 |                     "columnNumber": functionLocation.column, | 
 |                     "scriptId": String(script.id()) | 
 |                 }; | 
 |             } | 
 |             if (isAtReturn) | 
 |                 details.returnValue = returnValue; | 
 |         } | 
 |         return details; | 
 |     } | 
 |  | 
 |     /** | 
 |      * @return {!FunctionMirror} | 
 |      */ | 
 |     function ensureFuncMirror() | 
 |     { | 
 |         if (!funcMirror) { | 
 |             funcMirror = MakeMirror(funcObject); | 
 |             if (!funcMirror.isFunction()) | 
 |                 funcMirror = new UnresolvedFunctionMirror(funcObject); | 
 |         } | 
 |         return /** @type {!FunctionMirror} */(funcMirror); | 
 |     } | 
 |  | 
 |     /** | 
 |      * @return {!ScriptMirror} | 
 |      */ | 
 |     function ensureScriptMirror() | 
 |     { | 
 |         if (!scriptMirror) { | 
 |             scriptMirror = MakeMirror(scriptObject); | 
 |         } | 
 |         return /** @type {!ScriptMirror} */(scriptMirror); | 
 |     } | 
 |  | 
 |     /** | 
 |      * @return {!{line: number, column: number}} | 
 |      */ | 
 |     function ensureLocation() | 
 |     { | 
 |         if (!location) { | 
 |             var script = ensureScriptMirror(); | 
 |             location = script.locationFromPosition(sourcePosition, true); | 
 |             if (!location) | 
 |                 location = { line: 0, column: 0 }; | 
 |         } | 
 |         return location; | 
 |     } | 
 |  | 
 |     /** | 
 |      * @return {number} | 
 |      */ | 
 |     function contextId() | 
 |     { | 
 |         var context = | 
 |             ensureFuncMirror().context() || ensureScriptMirror().context(); | 
 |         if (context && context.data()) return Number(context.data()); | 
 |         return 0; | 
 |     } | 
 |  | 
 |     /** | 
 |      * @param {string} expression | 
 |      * @param {boolean} throwOnSideEffect | 
 |      * @return {*} | 
 |      */ | 
 |     function evaluate(expression, throwOnSideEffect) | 
 |     { | 
 |         return frameMirror.evaluate(expression, throwOnSideEffect).value(); | 
 |     } | 
 |  | 
 |     /** @return {undefined} */ | 
 |     function restart() | 
 |     { | 
 |         return frameMirror.restart(); | 
 |     } | 
 |  | 
 |     /** | 
 |      * @param {number} scopeNumber | 
 |      * @param {string} variableName | 
 |      * @param {*} newValue | 
 |      */ | 
 |     function setVariableValue(scopeNumber, variableName, newValue) | 
 |     { | 
 |         var scopeMirror = frameMirror.scope(scopeNumber); | 
 |         if (!scopeMirror) | 
 |             throw new Error("Incorrect scope index"); | 
 |         scopeMirror.setVariableValue(variableName, newValue); | 
 |     } | 
 |  | 
 |     return { | 
 |         "contextId": contextId, | 
 |         "thisObject": thisObject, | 
 |         "evaluate": evaluate, | 
 |         "restart": restart, | 
 |         "setVariableValue": setVariableValue, | 
 |         "isAtReturn": isAtReturn, | 
 |         "details": lazyDetails | 
 |     }; | 
 | } | 
 |  | 
 | /** | 
 |  * @param {number} scopeType | 
 |  * @param {!Object} scopeObject | 
 |  * @return {!Object|undefined} | 
 |  */ | 
 | DebuggerScript._buildScopeObject = function(scopeType, scopeObject) | 
 | { | 
 |     var result; | 
 |     switch (scopeType) { | 
 |     case ScopeType.Local: | 
 |     case ScopeType.Closure: | 
 |     case ScopeType.Catch: | 
 |     case ScopeType.Block: | 
 |     case ScopeType.Script: | 
 |     case ScopeType.Eval: | 
 |     case ScopeType.Module: | 
 |         // 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. | 
 |         var properties = /** @type {!ObjectMirror} */(MakeMirror(scopeObject)).properties(); | 
 |         // Almost always Script scope will be empty, so just filter out that noise. | 
 |         // Also drop empty Block, Eval and Script scopes, should we get any. | 
 |         if (!properties.length && (scopeType === ScopeType.Script || | 
 |                                    scopeType === ScopeType.Block || | 
 |                                    scopeType === ScopeType.Eval || | 
 |                                    scopeType === ScopeType.Module)) { | 
 |             break; | 
 |         } | 
 |         result = { __proto__: null }; | 
 |         for (var j = 0; j < properties.length; j++) { | 
 |             var name = properties[j].name(); | 
 |             if (name.length === 0 || name.charAt(0) === ".") | 
 |                 continue; // Skip internal variables like ".arguments" and variables with empty name | 
 |             result[name] = properties[j].value_; | 
 |         } | 
 |         break; | 
 |     case ScopeType.Global: | 
 |     case ScopeType.With: | 
 |         result = scopeObject; | 
 |         break; | 
 |     } | 
 |     return result; | 
 | } | 
 |  | 
 | return DebuggerScript; | 
 | })(); |