diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebInspectorUI/UserInterface/Models/CallFrame.js | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebInspectorUI/UserInterface/Models/CallFrame.js')
-rw-r--r-- | Source/WebInspectorUI/UserInterface/Models/CallFrame.js | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/Source/WebInspectorUI/UserInterface/Models/CallFrame.js b/Source/WebInspectorUI/UserInterface/Models/CallFrame.js new file mode 100644 index 000000000..67778a6bc --- /dev/null +++ b/Source/WebInspectorUI/UserInterface/Models/CallFrame.js @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2013 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +WebInspector.CallFrame = class CallFrame extends WebInspector.Object +{ + constructor(target, id, sourceCodeLocation, functionName, thisObject, scopeChain, nativeCode, programCode, isTailDeleted) + { + super(); + + console.assert(target instanceof WebInspector.Target); + console.assert(!sourceCodeLocation || sourceCodeLocation instanceof WebInspector.SourceCodeLocation); + console.assert(!thisObject || thisObject instanceof WebInspector.RemoteObject); + console.assert(!scopeChain || scopeChain instanceof Array); + + this._target = target; + this._id = id || null; + this._sourceCodeLocation = sourceCodeLocation || null; + this._functionName = functionName || ""; + this._thisObject = thisObject || null; + this._scopeChain = scopeChain || []; + this._nativeCode = nativeCode || false; + this._programCode = programCode || false; + this._isTailDeleted = isTailDeleted || false; + } + + // Public + + get target() { return this._target; } + get id() { return this._id; } + get sourceCodeLocation() { return this._sourceCodeLocation; } + get functionName() { return this._functionName; } + get nativeCode() { return this._nativeCode; } + get programCode() { return this._programCode; } + get thisObject() { return this._thisObject; } + get scopeChain() { return this._scopeChain; } + get isTailDeleted() { return this._isTailDeleted; } + + saveIdentityToCookie() + { + // Do nothing. The call frame is torn down when the inspector closes, and + // we shouldn't restore call frame content views across debugger pauses. + } + + collectScopeChainVariableNames(callback) + { + var result = {this: true, __proto__: null}; + + var pendingRequests = this._scopeChain.length; + + function propertiesCollected(properties) + { + for (var i = 0; properties && i < properties.length; ++i) + result[properties[i].name] = true; + + if (--pendingRequests) + return; + + callback(result); + } + + for (var i = 0; i < this._scopeChain.length; ++i) + this._scopeChain[i].objects[0].deprecatedGetAllProperties(propertiesCollected); + } + + mergedScopeChain() + { + let mergedScopes = []; + + // Scopes list goes from top/local (1) to bottom/global (5) + // [scope1, scope2, scope3, scope4, scope5] + let scopes = this._scopeChain.slice(); + + // Merge similiar scopes. Some function call frames may have multiple + // top level closure scopes (one for `var`s one for `let`s) that can be + // combined to a single scope of variables. Go in reverse order so we + // merge the first two closure scopes with the same name. Also mark + // the first time we see a new name, so we know the base for the name. + // [scope1&2, scope3, scope4, scope5] + // foo bar GLE global + let lastMarkedHash = null; + function markAsBaseIfNeeded(scope) { + if (!scope.hash) + return false; + if (scope.type !== WebInspector.ScopeChainNode.Type.Closure) + return false; + if (scope.hash === lastMarkedHash) + return false; + lastMarkedHash = scope.hash; + scope.__baseClosureScope = true; + return true; + } + + function shouldMergeClosureScopes(youngScope, oldScope, lastMerge) { + if (!youngScope || !oldScope) + return false; + + // Don't merge unknown locations. + if (!youngScope.hash || !oldScope.hash) + return false; + + // Only merge closure scopes. + if (youngScope.type !== WebInspector.ScopeChainNode.Type.Closure) + return false; + if (oldScope.type !== WebInspector.ScopeChainNode.Type.Closure) + return false; + + // Don't merge if they are not the same. + if (youngScope.hash !== oldScope.hash) + return false; + + // Don't merge if there was already a merge. + if (lastMerge && youngScope.hash === lastMerge.hash) + return false; + + return true; + } + + let lastScope = null; + let lastMerge = null; + for (let i = scopes.length - 1; i >= 0; --i) { + let scope = scopes[i]; + markAsBaseIfNeeded(scope); + if (shouldMergeClosureScopes(scope, lastScope, lastMerge)) { + console.assert(lastScope.__baseClosureScope); + let type = WebInspector.ScopeChainNode.Type.Closure; + let objects = lastScope.objects.concat(scope.objects); + let merged = new WebInspector.ScopeChainNode(type, objects, scope.name, scope.location); + merged.__baseClosureScope = true; + console.assert(objects.length === 2); + + mergedScopes.pop(); // Remove the last. + mergedScopes.push(merged); // Add the merged scope. + + lastMerge = merged; + lastScope = null; + } else { + mergedScopes.push(scope); + + lastMerge = null; + lastScope = scope; + } + } + + mergedScopes = mergedScopes.reverse(); + + // Mark the first Closure as Local if the name matches this call frame. + for (let scope of mergedScopes) { + if (scope.type === WebInspector.ScopeChainNode.Type.Closure) { + if (scope.name === this._functionName) + scope.convertToLocalScope(); + break; + } + } + + return mergedScopes; + } + + // Static + + static functionNameFromPayload(payload) + { + let functionName = payload.functionName; + if (functionName === "global code") + return WebInspector.UIString("Global Code"); + if (functionName === "eval code") + return WebInspector.UIString("Eval Code"); + if (functionName === "module code") + return WebInspector.UIString("Module Code"); + return functionName; + } + + static programCodeFromPayload(payload) + { + return payload.functionName.endsWith(" code"); + } + + static fromDebuggerPayload(target, payload, scopeChain, sourceCodeLocation) + { + let id = payload.callFrameId; + let thisObject = WebInspector.RemoteObject.fromPayload(payload.this, target); + let functionName = WebInspector.CallFrame.functionNameFromPayload(payload); + let nativeCode = false; + let programCode = WebInspector.CallFrame.programCodeFromPayload(payload); + let isTailDeleted = payload.isTailDeleted; + + if (sourceCodeLocation && isWebInspectorConsoleEvaluationScript(sourceCodeLocation.sourceCode.sourceURL)) { + functionName = WebInspector.UIString("Console Evaluation"); + programCode = true; + } + + return new WebInspector.CallFrame(target, id, sourceCodeLocation, functionName, thisObject, scopeChain, nativeCode, programCode, isTailDeleted); + } + + static fromPayload(target, payload) + { + console.assert(payload); + + let {url, scriptId} = payload; + let nativeCode = false; + let sourceCodeLocation = null; + let functionName = WebInspector.CallFrame.functionNameFromPayload(payload); + let programCode = WebInspector.CallFrame.programCodeFromPayload(payload); + + if (url === "[native code]") { + nativeCode = true; + url = null; + } else if (url || scriptId) { + let sourceCode = null; + if (scriptId) { + sourceCode = WebInspector.debuggerManager.scriptForIdentifier(scriptId, target); + if (sourceCode && sourceCode.resource) + sourceCode = sourceCode.resource; + } + if (!sourceCode) + sourceCode = WebInspector.frameResourceManager.resourceForURL(url); + if (!sourceCode) + sourceCode = WebInspector.debuggerManager.scriptsForURL(url, target)[0]; + + if (sourceCode) { + // The lineNumber is 1-based, but we expect 0-based. + let lineNumber = payload.lineNumber - 1; + sourceCodeLocation = sourceCode.createLazySourceCodeLocation(lineNumber, payload.columnNumber); + } else { + // Treat this as native code if we were unable to find a source. + console.assert(!url, "We should have detected source code for something with a url"); + nativeCode = true; + url = null; + } + } + + if (sourceCodeLocation && isWebInspectorConsoleEvaluationScript(sourceCodeLocation.sourceCode.sourceURL)) { + functionName = WebInspector.UIString("Console Evaluation"); + programCode = true; + } + + return new WebInspector.CallFrame(target, null, sourceCodeLocation, functionName, null, null, nativeCode, programCode); + } +}; |