Upstream version 9.37.197.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / bindings / v8 / DebuggerScript.js
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.ScopeInfoDetails = {
42     AllScopes: 0,
43     FastAsyncScopes: 1,
44     NoScopes: 2
45 };
46
47 DebuggerScript._pauseOnExceptionsState = DebuggerScript.PauseOnExceptionsState.DontPauseOnExceptions;
48 Debug.clearBreakOnException();
49 Debug.clearBreakOnUncaughtException();
50
51 DebuggerScript.getAfterCompileScript = function(eventData)
52 {
53     return DebuggerScript._formatScript(eventData.script_.script_);
54 }
55
56 DebuggerScript.getWorkerScripts = function()
57 {
58     var result = [];
59     var scripts = Debug.scripts();
60     for (var i = 0; i < scripts.length; ++i) {
61         var script = scripts[i];
62         // Workers don't share same V8 heap now so there is no need to complicate stuff with
63         // the context id like we do to discriminate between scripts from different pages.
64         // However we need to filter out v8 native scripts.
65         if (script.context_data && script.context_data === "worker")
66             result.push(DebuggerScript._formatScript(script));
67     }
68     return result;
69 }
70
71 DebuggerScript.getFunctionScopes = function(fun)
72 {
73     var mirror = MakeMirror(fun);
74     var count = mirror.scopeCount();
75     if (count == 0)
76         return null;
77     var result = [];
78     for (var i = 0; i < count; i++) {
79         var scopeDetails = mirror.scope(i).details();
80         result[i] = {
81             type: scopeDetails.type(),
82             object: DebuggerScript._buildScopeObject(scopeDetails.type(), scopeDetails.object())
83         };
84     }
85     return result;
86 }
87
88 DebuggerScript.getInternalProperties = function(value)
89 {
90     var properties = ObjectMirror.GetInternalProperties(value);
91     var result = [];
92     for (var i = 0; i < properties.length; i++) {
93         var mirror = properties[i];
94         result.push({
95             name: mirror.name(),
96             value: mirror.value().value()
97         });
98     }
99     return result;
100 }
101
102 DebuggerScript.setFunctionVariableValue = function(functionValue, scopeIndex, variableName, newValue)
103 {
104     var mirror = MakeMirror(functionValue);
105     if (!mirror.isFunction())
106         throw new Error("Function value has incorrect type");
107     return DebuggerScript._setScopeVariableValue(mirror, scopeIndex, variableName, newValue);
108 }
109
110 DebuggerScript._setScopeVariableValue = function(scopeHolder, scopeIndex, variableName, newValue)
111 {
112     var scopeMirror = scopeHolder.scope(scopeIndex);
113     if (!scopeMirror)
114         throw new Error("Incorrect scope index");
115     scopeMirror.setVariableValue(variableName, newValue);
116     return undefined;
117 }
118
119 DebuggerScript.getScripts = function(contextData)
120 {
121     var result = [];
122
123     if (!contextData)
124         return result;
125     var comma = contextData.indexOf(",");
126     if (comma === -1)
127         return result;
128     // Context data is a string in the following format:
129     // ("page"|"injected")","<page id>
130     var idSuffix = contextData.substring(comma); // including the comma
131
132     var scripts = Debug.scripts();
133     for (var i = 0; i < scripts.length; ++i) {
134         var script = scripts[i];
135         if (script.context_data && script.context_data.lastIndexOf(idSuffix) != -1)
136             result.push(DebuggerScript._formatScript(script));
137     }
138     return result;
139 }
140
141 DebuggerScript._formatScript = function(script)
142 {
143     var lineEnds = script.line_ends;
144     var lineCount = lineEnds.length;
145     var endLine = script.line_offset + lineCount - 1;
146     var endColumn;
147     // V8 will not count last line if script source ends with \n.
148     if (script.source[script.source.length - 1] === '\n') {
149         endLine += 1;
150         endColumn = 0;
151     } else {
152         if (lineCount === 1)
153             endColumn = script.source.length + script.column_offset;
154         else
155             endColumn = script.source.length - (lineEnds[lineCount - 2] + 1);
156     }
157
158     return {
159         id: script.id,
160         name: script.nameOrSourceURL(),
161         source: script.source,
162         startLine: script.line_offset,
163         startColumn: script.column_offset,
164         endLine: endLine,
165         endColumn: endColumn,
166         isContentScript: !!script.context_data && script.context_data.indexOf("injected") == 0
167     };
168 }
169
170 DebuggerScript.setBreakpoint = function(execState, info)
171 {
172     var positionAlignment = info.interstatementLocation ? Debug.BreakPositionAlignment.BreakPosition : Debug.BreakPositionAlignment.Statement;
173     var breakId = Debug.setScriptBreakPointById(info.sourceID, info.lineNumber, info.columnNumber, info.condition, undefined, positionAlignment);
174
175     var locations = Debug.findBreakPointActualLocations(breakId);
176     if (!locations.length)
177         return undefined;
178     info.lineNumber = locations[0].line;
179     info.columnNumber = locations[0].column;
180     return breakId.toString();
181 }
182
183 DebuggerScript.removeBreakpoint = function(execState, info)
184 {
185     Debug.findBreakPoint(info.breakpointId, true);
186 }
187
188 DebuggerScript.pauseOnExceptionsState = function()
189 {
190     return DebuggerScript._pauseOnExceptionsState;
191 }
192
193 DebuggerScript.setPauseOnExceptionsState = function(newState)
194 {
195     DebuggerScript._pauseOnExceptionsState = newState;
196
197     if (DebuggerScript.PauseOnExceptionsState.PauseOnAllExceptions === newState)
198         Debug.setBreakOnException();
199     else
200         Debug.clearBreakOnException();
201
202     if (DebuggerScript.PauseOnExceptionsState.PauseOnUncaughtExceptions === newState)
203         Debug.setBreakOnUncaughtException();
204     else
205         Debug.clearBreakOnUncaughtException();
206 }
207
208 DebuggerScript.frameCount = function(execState)
209 {
210     return execState.frameCount();
211 }
212
213 DebuggerScript.currentCallFrame = function(execState, data)
214 {
215     var maximumLimit = data >> 2;
216     var scopeDetailsLevel = data & 3;
217
218     var frameCount = execState.frameCount();
219     if (maximumLimit && maximumLimit < frameCount)
220         frameCount = maximumLimit;
221     var topFrame = undefined;
222     for (var i = frameCount - 1; i >= 0; i--) {
223         var frameMirror = execState.frame(i);
224         topFrame = DebuggerScript._frameMirrorToJSCallFrame(frameMirror, topFrame, scopeDetailsLevel);
225     }
226     return topFrame;
227 }
228
229 DebuggerScript.stepIntoStatement = function(execState)
230 {
231     execState.prepareStep(Debug.StepAction.StepIn, 1);
232 }
233
234 DebuggerScript.stepOverStatement = function(execState, callFrame)
235 {
236     execState.prepareStep(Debug.StepAction.StepNext, 1);
237 }
238
239 DebuggerScript.stepOutOfFunction = function(execState, callFrame)
240 {
241     execState.prepareStep(Debug.StepAction.StepOut, 1);
242 }
243
244 // Returns array in form:
245 //      [ 0, <v8_result_report> ] in case of success
246 //   or [ 1, <general_error_message>, <compiler_message>, <line_number>, <column_number> ] in case of compile error, numbers are 1-based.
247 // or throws exception with message.
248 DebuggerScript.liveEditScriptSource = function(scriptId, newSource, preview)
249 {
250     var scripts = Debug.scripts();
251     var scriptToEdit = null;
252     for (var i = 0; i < scripts.length; i++) {
253         if (scripts[i].id == scriptId) {
254             scriptToEdit = scripts[i];
255             break;
256         }
257     }
258     if (!scriptToEdit)
259         throw("Script not found");
260
261     var changeLog = [];
262     try {
263         var result = Debug.LiveEdit.SetScriptSource(scriptToEdit, newSource, preview, changeLog);
264         return [0, result];
265     } catch (e) {
266         if (e instanceof Debug.LiveEdit.Failure && "details" in e) {
267             var details = e.details;
268             if (details.type === "liveedit_compile_error") {
269                 var startPosition = details.position.start;
270                 return [1, String(e), String(details.syntaxErrorMessage), Number(startPosition.line), Number(startPosition.column)];
271             }
272         }
273         throw e;
274     }
275 }
276
277 DebuggerScript.clearBreakpoints = function(execState, info)
278 {
279     Debug.clearAllBreakPoints();
280 }
281
282 DebuggerScript.setBreakpointsActivated = function(execState, info)
283 {
284     Debug.debuggerFlags().breakPointsActive.setValue(info.enabled);
285 }
286
287 DebuggerScript.getScriptSource = function(eventData)
288 {
289     return eventData.script().source();
290 }
291
292 DebuggerScript.setScriptSource = function(eventData, source)
293 {
294     if (eventData.script().data() === "injected-script")
295         return;
296     eventData.script().setSource(source);
297 }
298
299 DebuggerScript.getScriptName = function(eventData)
300 {
301     return eventData.script().script_.nameOrSourceURL();
302 }
303
304 DebuggerScript.getBreakpointNumbers = function(eventData)
305 {
306     var breakpoints = eventData.breakPointsHit();
307     var numbers = [];
308     if (!breakpoints)
309         return numbers;
310
311     for (var i = 0; i < breakpoints.length; i++) {
312         var breakpoint = breakpoints[i];
313         var scriptBreakPoint = breakpoint.script_break_point();
314         numbers.push(scriptBreakPoint ? scriptBreakPoint.number() : breakpoint.number());
315     }
316     return numbers;
317 }
318
319 DebuggerScript.isEvalCompilation = function(eventData)
320 {
321     var script = eventData.script();
322     return (script.compilationType() === Debug.ScriptCompilationType.Eval);
323 }
324
325 // NOTE: This function is performance critical, as it can be run on every
326 // statement that generates an async event (like addEventListener) to support
327 // asynchronous call stacks. Thus, when possible, initialize the data lazily.
328 DebuggerScript._frameMirrorToJSCallFrame = function(frameMirror, callerFrame, scopeDetailsLevel)
329 {
330     // Stuff that can not be initialized lazily (i.e. valid while paused with a valid break_id).
331     // The frameMirror and scopeMirror can be accessed only while paused on the debugger.
332     var frameDetails = frameMirror.details();
333
334     var funcObject = frameDetails.func();
335     var sourcePosition = frameDetails.sourcePosition();
336     var thisObject = frameDetails.receiver();
337
338     var isAtReturn = !!frameDetails.isAtReturn();
339     var returnValue = isAtReturn ? frameDetails.returnValue() : undefined;
340
341     var scopeMirrors = (scopeDetailsLevel === DebuggerScript.ScopeInfoDetails.NoScopes ? [] : frameMirror.allScopes(scopeDetailsLevel === DebuggerScript.ScopeInfoDetails.FastAsyncScopes));
342     var scopeTypes = new Array(scopeMirrors.length);
343     var scopeObjects = new Array(scopeMirrors.length);
344     for (var i = 0; i < scopeMirrors.length; ++i) {
345         var scopeDetails = scopeMirrors[i].details();
346         scopeTypes[i] = scopeDetails.type();
347         scopeObjects[i] = scopeDetails.object();
348     }
349
350     // Calculated lazily.
351     var scopeChain;
352     var funcMirror;
353     var location;
354
355     function lazyScopeChain()
356     {
357         if (!scopeChain) {
358             scopeChain = [];
359             for (var i = 0; i < scopeObjects.length; ++i)
360                 scopeChain.push(DebuggerScript._buildScopeObject(scopeTypes[i], scopeObjects[i]));
361             scopeObjects = null; // Free for GC.
362         }
363         return scopeChain;
364     }
365
366     function ensureFuncMirror()
367     {
368         if (!funcMirror) {
369             funcMirror = MakeMirror(funcObject);
370             if (!funcMirror.isFunction())
371                 funcMirror = new UnresolvedFunctionMirror(funcObject);
372         }
373         return funcMirror;
374     }
375
376     function ensureLocation()
377     {
378         if (!location) {
379             var script = ensureFuncMirror().script();
380             if (script)
381                 location = script.locationFromPosition(sourcePosition, true);
382             if (!location)
383                 location = { line: 0, column: 0 };
384         }
385         return location;
386     }
387
388     function line()
389     {
390         return ensureLocation().line;
391     }
392
393     function column()
394     {
395         return ensureLocation().column;
396     }
397
398     function sourceID()
399     {
400         var script = ensureFuncMirror().script();
401         return script && script.id();
402     }
403
404     function functionName()
405     {
406         var func = ensureFuncMirror();
407         if (!func.resolved())
408             return undefined;
409         var displayName;
410         var valueMirror = func.property("displayName").value();
411         if (valueMirror && valueMirror.isString())
412             displayName = valueMirror.value();
413         return displayName || func.name() || func.inferredName();
414     }
415
416     function evaluate(expression)
417     {
418         return frameMirror.evaluate(expression, false).value();
419     }
420
421     function restart()
422     {
423         return Debug.LiveEdit.RestartFrame(frameMirror);
424     }
425
426     function setVariableValue(scopeNumber, variableName, newValue)
427     {
428         return DebuggerScript._setScopeVariableValue(frameMirror, scopeNumber, variableName, newValue);
429     }
430
431     function stepInPositions()
432     {
433         var stepInPositionsV8 = frameMirror.stepInPositions();
434         var stepInPositionsProtocol;
435         if (stepInPositionsV8) {
436             stepInPositionsProtocol = [];
437             var script = ensureFuncMirror().script();
438             if (script) {
439                 var scriptId = String(script.id());
440                 for (var i = 0; i < stepInPositionsV8.length; i++) {
441                     var item = {
442                         scriptId: scriptId,
443                         lineNumber: stepInPositionsV8[i].position.line,
444                         columnNumber: stepInPositionsV8[i].position.column
445                     };
446                     stepInPositionsProtocol.push(item);
447                 }
448             }
449         }
450         return JSON.stringify(stepInPositionsProtocol);
451     }
452
453     return {
454         "sourceID": sourceID,
455         "line": line,
456         "column": column,
457         "functionName": functionName,
458         "thisObject": thisObject,
459         "scopeChain": lazyScopeChain,
460         "scopeType": scopeTypes,
461         "evaluate": evaluate,
462         "caller": callerFrame,
463         "restart": restart,
464         "setVariableValue": setVariableValue,
465         "stepInPositions": stepInPositions,
466         "isAtReturn": isAtReturn,
467         "returnValue": returnValue
468     };
469 }
470
471 DebuggerScript._buildScopeObject = function(scopeType, scopeObject)
472 {
473     var result;
474     switch (scopeType) {
475     case ScopeType.Local:
476     case ScopeType.Closure:
477     case ScopeType.Catch:
478         // For transient objects we create a "persistent" copy that contains
479         // the same properties.
480         // Reset scope object prototype to null so that the proto properties
481         // don't appear in the local scope section.
482         result = { __proto__: null };
483         var properties = MakeMirror(scopeObject, true /* transient */).properties();
484         for (var j = 0; j < properties.length; j++) {
485             var name = properties[j].name();
486             if (name.charAt(0) === ".")
487                 continue; // Skip internal variables like ".arguments"
488             result[name] = properties[j].value_;
489         }
490         break;
491     case ScopeType.Global:
492     case ScopeType.With:
493         result = scopeObject;
494         break;
495     case ScopeType.Block:
496         // Unsupported yet. Mustn't be reachable.
497         break;
498     }
499     return result;
500 }
501
502 // We never resolve Mirror by its handle so to avoid memory leaks caused by Mirrors in the cache we disable it.
503 ToggleMirrorCache(false);
504
505 return DebuggerScript;
506 })();