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, args)
    165 {
    166     var positionAlignment = args.interstatementLocation ? Debug.BreakPositionAlignment.BreakPosition : Debug.BreakPositionAlignment.Statement;
    167     var breakId = Debug.setScriptBreakPointById(args.sourceID, args.lineNumber, args.columnNumber, args.condition, undefined, positionAlignment);
    168 
    169     var locations = Debug.findBreakPointActualLocations(breakId);
    170     if (!locations.length)
    171         return undefined;
    172     args.lineNumber = locations[0].line;
    173     args.columnNumber = locations[0].column;
    174     return breakId.toString();
    175 }
    176 
    177 DebuggerScript.removeBreakpoint = function(execState, args)
    178 {
    179     Debug.findBreakPoint(args.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)
    221 {
    222     execState.prepareStep(Debug.StepAction.StepNext, 1);
    223 }
    224 
    225 DebuggerScript.stepOutOfFunction = function(execState)
    226 {
    227     execState.prepareStep(Debug.StepAction.StepOut, 1);
    228 }
    229 
    230 // Returns array in form:
    231 //      [ 0, <v8_result_report> ] in case of success
    232 //   or [ 1, <general_error_message>, <compiler_message>, <line_number>, <column_number> ] in case of compile error, numbers are 1-based.
    233 // or throws exception with message.
    234 DebuggerScript.liveEditScriptSource = function(scriptId, newSource, preview)
    235 {
    236     var scripts = Debug.scripts();
    237     var scriptToEdit = null;
    238     for (var i = 0; i < scripts.length; i++) {
    239         if (scripts[i].id == scriptId) {
    240             scriptToEdit = scripts[i];
    241             break;
    242         }
    243     }
    244     if (!scriptToEdit)
    245         throw("Script not found");
    246 
    247     var changeLog = [];
    248     try {
    249         var result = Debug.LiveEdit.SetScriptSource(scriptToEdit, newSource, preview, changeLog);
    250         return [0, result];
    251     } catch (e) {
    252         if (e instanceof Debug.LiveEdit.Failure && "details" in e) {
    253             var details = e.details;
    254             if (details.type === "liveedit_compile_error") {
    255                 var startPosition = details.position.start;
    256                 return [1, String(e), String(details.syntaxErrorMessage), Number(startPosition.line), Number(startPosition.column)];
    257             }
    258         }
    259         throw e;
    260     }
    261 }
    262 
    263 DebuggerScript.clearBreakpoints = function(execState, args)
    264 {
    265     Debug.clearAllBreakPoints();
    266 }
    267 
    268 DebuggerScript.setBreakpointsActivated = function(execState, args)
    269 {
    270     Debug.debuggerFlags().breakPointsActive.setValue(args.enabled);
    271 }
    272 
    273 DebuggerScript.getScriptSource = function(eventData)
    274 {
    275     return eventData.script().source();
    276 }
    277 
    278 DebuggerScript.setScriptSource = function(eventData, source)
    279 {
    280     if (eventData.script().data() === "injected-script")
    281         return;
    282     eventData.script().setSource(source);
    283 }
    284 
    285 DebuggerScript.getScriptName = function(eventData)
    286 {
    287     return eventData.script().script_.nameOrSourceURL();
    288 }
    289 
    290 DebuggerScript.getBreakpointNumbers = function(eventData)
    291 {
    292     var breakpoints = eventData.breakPointsHit();
    293     var numbers = [];
    294     if (!breakpoints)
    295         return numbers;
    296 
    297     for (var i = 0; i < breakpoints.length; i++) {
    298         var breakpoint = breakpoints[i];
    299         var scriptBreakPoint = breakpoint.script_break_point();
    300         numbers.push(scriptBreakPoint ? scriptBreakPoint.number() : breakpoint.number());
    301     }
    302     return numbers;
    303 }
    304 
    305 DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror, callerFrame)
    306 {
    307     // Get function name.
    308     var func;
    309     try {
    310         func = frameMirror.func();
    311     } catch(e) {
    312     }
    313     var functionName;
    314     if (func)
    315         functionName = func.name() || func.inferredName();
    316 
    317     // Get script ID.
    318     var script = func.script();
    319     var sourceID = script && script.id();
    320 
    321     // Get location.
    322     var location  = frameMirror.sourceLocation();
    323 
    324     // Get this object.
    325     var thisObject = frameMirror.details_.receiver();
    326 
    327     var scopeChain = [];
    328     var scopeType = [];
    329     for (var i = 0; i < frameMirror.scopeCount(); i++) {
    330         var scopeMirror = frameMirror.scope(i);
    331         scopeType.push(scopeMirror.scopeType());
    332         scopeChain.push(DebuggerScript._buildScopeObject(scopeMirror));
    333     }
    334 
    335     function evaluate(expression)
    336     {
    337         return frameMirror.evaluate(expression, false).value();
    338     }
    339 
    340     function restart()
    341     {
    342         return Debug.LiveEdit.RestartFrame(frameMirror);
    343     }
    344 
    345     function setVariableValue(scopeNumber, variableName, newValue)
    346     {
    347         return DebuggerScript._setScopeVariableValue(frameMirror, scopeNumber, variableName, newValue);
    348     }
    349 
    350     function stepInPositions()
    351     {
    352         var stepInPositionsV8 = frameMirror.stepInPositions();
    353         var stepInPositionsProtocol;
    354         if (stepInPositionsV8) {
    355             stepInPositionsProtocol = [];
    356             var script = frameMirror.func().script();
    357             if (script) {
    358                 var scriptId = String(script.id());
    359                 for (var i = 0; i < stepInPositionsV8.length; i++) {
    360                     var item = {
    361                         scriptId: scriptId,
    362                         lineNumber: stepInPositionsV8[i].position.line,
    363                         columnNumber: stepInPositionsV8[i].position.column
    364                     };
    365                     stepInPositionsProtocol.push(item);
    366                 }
    367             }
    368         }
    369         return JSON.stringify(stepInPositionsProtocol);
    370     }
    371 
    372     return {
    373         "sourceID": sourceID,
    374         "line": location ? location.line : 0,
    375         "column": location ? location.column : 0,
    376         "functionName": functionName,
    377         "thisObject": thisObject,
    378         "scopeChain": scopeChain,
    379         "scopeType": scopeType,
    380         "evaluate": evaluate,
    381         "caller": callerFrame,
    382         "restart": restart,
    383         "setVariableValue": setVariableValue,
    384         "stepInPositions": stepInPositions
    385     };
    386 }
    387 
    388 DebuggerScript._buildScopeObject = function(scopeMirror) {
    389     var scopeObject;
    390     switch (scopeMirror.scopeType()) {
    391     case ScopeType.Local:
    392     case ScopeType.Closure:
    393     case ScopeType.Catch:
    394         // For transient objects we create a "persistent" copy that contains
    395         // the same properties.
    396         scopeObject = {};
    397         // Reset scope object prototype to null so that the proto properties
    398         // don't appear in the local scope section.
    399         scopeObject.__proto__ = null;
    400         var scopeObjectMirror = scopeMirror.scopeObject();
    401         var properties = scopeObjectMirror.properties();
    402         for (var j = 0; j < properties.length; j++) {
    403             var name = properties[j].name();
    404             if (name.charAt(0) === ".")
    405                 continue; // Skip internal variables like ".arguments"
    406             scopeObject[name] = properties[j].value_;
    407         }
    408         break;
    409     case ScopeType.Global:
    410     case ScopeType.With:
    411         scopeObject = scopeMirror.details_.object();
    412         break;
    413     case ScopeType.Block:
    414         // Unsupported yet. Mustn't be reachable.
    415         break;
    416     }
    417     return scopeObject;
    418 }
    419 
    420 return DebuggerScript;
    421 })();
    422