Home | History | Annotate | Download | only in v8
      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