Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(273)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/bindings/SourceMapNamesResolver.js

Issue 1653053002: Devtools: parse variables scopes and sourcemap them (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6 * @constructor
7 */
8 WebInspector.SourceMapNamesResolver = function()
9 {
10 this._resolvingPromises = new Map();
11 }
12
13 WebInspector.SourceMapNamesResolver._cacheSymbol = Symbol("cache");
14
15 WebInspector.SourceMapNamesResolver.prototype = {
16 /**
17 * @param {!WebInspector.DebuggerModel.Scope} scope
18 * @return {!Promise.<!Map<string, string>>}
19 */
20 _resolveScope: function(scope)
21 {
22 var cachedMap = scope[WebInspector.SourceMapNamesResolver._cacheSymbol];
23 if (cachedMap)
24 return Promise.resolve(cachedMap);
25
26 var startLocation = scope.startLocation();
27 var endLocation = scope.endLocation();
28 var script = startLocation.script();
29
30 if (scope.type() === DebuggerAgent.ScopeType.Global || !startLocation || !endLocation || !script.sourceMapURL || script !== endLocation.script())
31 return Promise.resolve(new Map());
32
33 var cachedPromise = this._resolvingPromises.get(scope);
dgozman 2016/02/22 17:24:56 Move this check before |var startLocation = ...|
sergeyv 2016/02/22 21:42:54 Done.
34 if (cachedPromise)
35 return cachedPromise;
36
37 var sourceMap = WebInspector.debuggerWorkspaceBinding.sourceMapForScript (script);
38 if (!sourceMap)
39 return Promise.resolve(new Map());
40
41 var promise = script.requestContent().then(onContent.bind(this));
42 this._resolvingPromises.set(scope, promise);
43 return promise;
44
45 /**
46 * @param {?string} content
47 * @return {!Map<string, string>}
48 * @this {WebInspector.SourceMapNamesResolver}
49 */
50 function onContent(content)
51 {
52 if (!content)
53 return new Map();
54
55 var startLocation = scope.startLocation();
56 var endLocation = scope.endLocation();
57 var textRange = new WebInspector.TextRange(startLocation.lineNumber, startLocation.columnNumber, endLocation.lineNumber, endLocation.columnNumber);
58
59 var scopeText = textRange.extract(content);
60 var scopeStart = textRange.toSourceRange(content).offset;
61 var prefix = "function fui";
dgozman 2016/02/22 17:24:56 Maybe add () after function name for perfectly val
sergeyv 2016/02/22 21:42:54 usually scopes start with (); so we shouldn't add
62 var root = acorn.parse(prefix + scopeText, {});
63 var declarators = [];
64 var functionDeclarationCounter = 0;
65 var walker = new WebInspector.ESTreeWalker(beforeVisit, afterVisit);
66
67 /**
68 * @param {!ESTree.Node} node
69 */
70 function beforeVisit(node)
71 {
72 if (node.type === "FunctionDeclaration" || node.type === "Functi onExpression")
dgozman 2016/02/22 17:24:56 Does it support arrow functions?
sergeyv 2016/02/22 21:42:54 I will check this && support it in follow-ups
73 functionDeclarationCounter++;
74
75 if (node.type === "VariableDeclarator" && functionDeclarationCou nter === 1)
76 declarators.push(node)
dgozman 2016/02/22 17:24:56 missing semicolon
sergeyv 2016/02/22 21:42:54 Done.
77 }
78
79 /**
80 * @param {!ESTree.Node} node
81 */
82 function afterVisit(node)
83 {
84 if (node.type === "FunctionDeclaration" || node.type === "Functi onExpression")
85 functionDeclarationCounter--;
86 }
87
88 walker.walk(root);
89
90 var namesMapping = new Map();
91
92 for (var i = 0; i < declarators.length; ++i) {
93 var id = declarators[i].id;
94 var start = scopeStart + id.start - prefix.length;
95
96 var lineEndings = content.lineEndings();
dgozman 2016/02/22 17:24:56 Move this call out of the loop.
sergeyv 2016/02/22 21:42:54 Done. But there was no real necessity in this: lin
97 var lineNumber = lineEndings.lowerBound(start);
98 var columnNumber = start - (lineNumber === 0 ? 0 : (lineEndings[ lineNumber - 1] + 1));
99 var entry = sourceMap.findEntry(lineNumber, columnNumber);
100 if (entry)
101 namesMapping.set(id.name, entry.name);
102 }
103
104 this._resolvingPromises.remove(scope);
105 scope[WebInspector.SourceMapNamesResolver._cacheSymbol] = namesMappi ng;
106 return namesMapping;
107 }
108 },
109
110 /**
111 * @param {!WebInspector.DebuggerModel.Scope} scope
112 * @return {!WebInspector.RemoteObject}
113 */
114 resolveScopeInObject: function(scope)
115 {
116 if (!Runtime.experiments.isEnabled("resolveVariableNames"))
117 return scope.object();
118
119 var startLocation = scope.startLocation();
120 var endLocation = scope.endLocation();
121
122 if (scope.type() === DebuggerAgent.ScopeType.Global || !startLocation || !endLocation || !startLocation.script().sourceMapURL || startLocation.script() !== endLocation.script())
123 return scope.object();
124
125 return new WebInspector.SourceMapNamesResolver.RemoteObject(scope);
126 }
127 }
128
129 /**
130 * @constructor
131 * @extends {WebInspector.RemoteObject}
132 * @param {!WebInspector.DebuggerModel.Scope} scope
133 */
134 WebInspector.SourceMapNamesResolver.RemoteObject = function(scope)
135 {
136 WebInspector.RemoteObject.call(this);
137 this._scope = scope;
138 this._object = scope.object();
139 };
140
141 WebInspector.SourceMapNamesResolver.RemoteObject.prototype = {
142 /**
143 * @override
144 * @return {?RuntimeAgent.CustomPreview}
145 */
146 customPreview: function()
147 {
148 return this._object.customPreview();
149 },
150
151 /**
152 * @override
153 * @return {string}
154 */
155 get type()
156 {
157 return this._object.type;
158 },
159
160 /**
161 * @override
162 * @return {string|undefined}
163 */
164 get subtype()
165 {
166 return this._object.subtype;
167 },
168
169 /**
170 * @override
171 * @return {string|undefined}
172 */
173 get description()
174 {
175 return this._object.description;
176 },
177
178 /**
179 * @override
180 * @return {boolean}
181 */
182 get hasChildren()
183 {
184 return this._object.hasChildren;
185 },
186
187 /**
188 * @override
189 * @return {number}
190 */
191 arrayLength: function()
192 {
193 return this._object.arrayLength();
194 },
195
196 /**
197 * @override
198 * @param {function(?Array.<!WebInspector.RemoteObjectProperty>, ?Array.<!We bInspector.RemoteObjectProperty>)} callback
199 */
200 getOwnProperties: function(callback)
201 {
202 this._object.getOwnProperties(callback);
203 },
204
205 /**
206 * @override
207 * @param {boolean} accessorPropertiesOnly
208 * @param {function(?Array<!WebInspector.RemoteObjectProperty>, ?Array<!WebI nspector.RemoteObjectProperty>)} callback
209 */
210 getAllProperties: function(accessorPropertiesOnly, callback)
211 {
212 /**
213 * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties
214 * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperti es
215 * @this {WebInspector.SourceMapNamesResolver.RemoteObject}
216 */
217 function wrappedCallback(properties, internalProperties)
218 {
219 WebInspector.sourceMapNamesResolver._resolveScope(this._scope).then( resolveNames.bind(null, properties, internalProperties))
220 }
221
222 /**
223 * @param {?Array.<!WebInspector.RemoteObjectProperty>} properties
224 * @param {?Array.<!WebInspector.RemoteObjectProperty>} internalProperti es
225 * @param {!Map<string, string>} namesMapping
226 */
227 function resolveNames(properties, internalProperties, namesMapping)
228 {
229 var newProperties = [];
230 if (properties) {
231 for (var i = 0; i < properties.length; ++i) {
232 var property = properties[i];
233 var name = namesMapping.get(property.name) || properties[i]. name;
234 newProperties.push(new WebInspector.RemoteObjectProperty(nam e, property.value, property.enumerable, property.writable, property.isOwn, prope rty.wasThrown, property.symbol, property.synthetic));
235 }
236 }
237
238 callback(newProperties, internalProperties);
239 }
240
241 this._object.getAllProperties(accessorPropertiesOnly, wrappedCallback.bi nd(this));
242 },
243
244 /**
245 * @override
246 * @param {string|!RuntimeAgent.CallArgument} argumentName
247 * @param {string} value
248 * @param {function(string=)} callback
249 */
250 setPropertyValue: function(argumentName, value, callback)
251 {
252 WebInspector.sourceMapNamesResolver._resolveScope(this._scope).then(reso lveName.bind(this));
253
254 /**
255 * @param {!Map<string, string>} namesMapping
256 * @this {WebInspector.SourceMapNamesResolver.RemoteObject}
257 */
258 function resolveName(namesMapping)
259 {
260 var name;
261 if (typeof argumentName === "string")
262 name = argumentName;
263 else
264 name = /** @type {string} */ (argumentName.value);
265
266 var actualName = name;
267 for (var compiledName of namesMapping.keys()) {
dgozman 2016/02/22 17:24:56 Is this operation common enough to justify reverse
sergeyv 2016/02/22 21:42:54 It is used only when user changes a property value
268 if (namesMapping.get(compiledName) === name) {
269 actualName = compiledName;
270 break;
271 }
272 }
273 this._object.setPropertyValue(actualName, value, callback);
274 }
275 },
276
277 /**
278 * @override
279 * @return {!Promise<?Array<!WebInspector.EventListener>>}
280 */
281 eventListeners: function()
282 {
283 return this._object.eventListeners();
284 },
285
286 /**
287 * @override
288 * @param {!RuntimeAgent.CallArgument} name
289 * @param {function(string=)} callback
290 */
291 deleteProperty: function(name, callback)
292 {
293 this._object.deleteProperty(name, callback);
294 },
295
296 /**
297 * @override
298 * @param {function(this:Object, ...)} functionDeclaration
299 * @param {!Array<!RuntimeAgent.CallArgument>=} args
300 * @param {function(?WebInspector.RemoteObject, boolean=)=} callback
301 */
302 callFunction: function(functionDeclaration, args, callback)
303 {
304 this._object.callFunction(functionDeclaration, args, callback);
305 },
306
307 /**
308 * @override
309 * @param {function(this:Object, ...)} functionDeclaration
310 * @param {!Array<!RuntimeAgent.CallArgument>|undefined} args
311 * @param {function(*)} callback
312 */
313 callFunctionJSON: function(functionDeclaration, args, callback)
314 {
315 this._object.callFunctionJSON(functionDeclaration, args, callback);
316 },
317
318 /**
319 * @override
320 * @return {!WebInspector.Target}
321 */
322 target: function()
323 {
324 return this._object.target();
325 },
326
327 /**
328 * @override
329 * @return {?WebInspector.DebuggerModel}
330 */
331 debuggerModel: function()
332 {
333 return this._object.debuggerModel();
334 },
335
336 /**
337 * @override
338 * @return {boolean}
339 */
340 isNode: function()
341 {
342 return this._object.isNode();
343 },
344
345 /**
346 * @override
347 * @param {function(?WebInspector.DebuggerModel.FunctionDetails)} callback
348 */
349 functionDetails: function(callback)
350 {
351 this._object.functionDetails(callback);
352 },
353
354 /**
355 * @override
356 * @param {function(?WebInspector.DebuggerModel.GeneratorObjectDetails)} cal lback
357 */
358 generatorObjectDetails: function(callback)
359 {
360 this._object.generatorObjectDetails(callback);
361 },
362
363 /**
364 * @override
365 * @param {function(?Array<!DebuggerAgent.CollectionEntry>)} callback
366 */
367 collectionEntries: function(callback)
368 {
369 this._object.collectionEntries(callback);
370 },
371
372 __proto__: WebInspector.RemoteObject.prototype
373 }
374
375 WebInspector.sourceMapNamesResolver = new WebInspector.SourceMapNamesResolver();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698