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

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/sdk/SourceMap.js

Issue 2371133003: [Devtools] Lazily build reverse mappings (Closed)
Patch Set: Addressing comments Created 4 years, 2 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
1 /* 1 /*
2 * Copyright (C) 2012 Google Inc. All rights reserved. 2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
73 { 73 {
74 this.lineNumber = lineNumber; 74 this.lineNumber = lineNumber;
75 this.columnNumber = columnNumber; 75 this.columnNumber = columnNumber;
76 this.sourceURL = sourceURL; 76 this.sourceURL = sourceURL;
77 this.sourceLineNumber = sourceLineNumber; 77 this.sourceLineNumber = sourceLineNumber;
78 this.sourceColumnNumber = sourceColumnNumber; 78 this.sourceColumnNumber = sourceColumnNumber;
79 this.name = name; 79 this.name = name;
80 } 80 }
81 81
82 /** 82 /**
83 * @constructor
84 * @param {?string} content
85 * @param {?Array<!WebInspector.SourceMapEntry>} reverseMappings
86 */
87 WebInspector.SourceURLInfo = function(content, reverseMappings) {
lushnikov 2016/09/29 16:29:52 WebInspector.SourceMap.SourceInfo?
eostroukhov 2016/09/30 19:14:41 Done, made it TextSourceMap implementation detail.
88 this.content = content;
89 this.reverseMappings = reverseMappings;
90 }
91
92 /**
83 * @interface 93 * @interface
84 */ 94 */
85 WebInspector.SourceMap = function() { } 95 WebInspector.SourceMap = function() { }
86 96
87 WebInspector.SourceMap.prototype = { 97 WebInspector.SourceMap.prototype = {
88 /** 98 /**
89 * @return {string} 99 * @return {string}
90 */ 100 */
91 compiledURL: function() { }, 101 compiledURL: function() { },
92 102
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
165 */ 175 */
166 WebInspector.TextSourceMap = function(compiledURL, sourceMappingURL, payload) 176 WebInspector.TextSourceMap = function(compiledURL, sourceMappingURL, payload)
167 { 177 {
168 if (!WebInspector.TextSourceMap.prototype._base64Map) { 178 if (!WebInspector.TextSourceMap.prototype._base64Map) {
169 const base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx yz0123456789+/"; 179 const base64Digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx yz0123456789+/";
170 WebInspector.TextSourceMap.prototype._base64Map = {}; 180 WebInspector.TextSourceMap.prototype._base64Map = {};
171 for (var i = 0; i < base64Digits.length; ++i) 181 for (var i = 0; i < base64Digits.length; ++i)
172 WebInspector.TextSourceMap.prototype._base64Map[base64Digits.charAt( i)] = i; 182 WebInspector.TextSourceMap.prototype._base64Map[base64Digits.charAt( i)] = i;
173 } 183 }
174 184
185 this._json = payload;
175 this._compiledURL = compiledURL; 186 this._compiledURL = compiledURL;
176 this._sourceMappingURL = sourceMappingURL; 187 this._sourceMappingURL = sourceMappingURL;
177 this._reverseMappingsBySourceURL = new Map(); 188 /** @type {?Array<!WebInspector.SourceMapEntry>} */
178 this._mappings = []; 189 this._mappings = null;
179 this._sources = {}; 190 /** @type {!Map<string, !WebInspector.SourceURLInfo>} */
180 this._sourceContentByURL = {}; 191 this._sourceURLInfos = new Map();
181 this._parseMappingPayload(payload); 192 this._eachSection(this._parseSources.bind(this));
182 } 193 }
183 194
184 /** 195 /**
185 * @param {string} sourceMapURL 196 * @param {string} sourceMapURL
186 * @param {string} compiledURL 197 * @param {string} compiledURL
187 * @return {!Promise<?WebInspector.TextSourceMap>} 198 * @return {!Promise<?WebInspector.TextSourceMap>}
188 * @this {WebInspector.TextSourceMap} 199 * @this {WebInspector.TextSourceMap}
189 */ 200 */
190 WebInspector.TextSourceMap.load = function(sourceMapURL, compiledURL) 201 WebInspector.TextSourceMap.load = function(sourceMapURL, compiledURL)
191 { 202 {
(...skipping 12 matching lines...) Expand all
204 if (!content || statusCode >= 400) { 215 if (!content || statusCode >= 400) {
205 callback(null); 216 callback(null);
206 return; 217 return;
207 } 218 }
208 219
209 if (content.slice(0, 3) === ")]}") 220 if (content.slice(0, 3) === ")]}")
210 content = content.substring(content.indexOf("\n")); 221 content = content.substring(content.indexOf("\n"));
211 try { 222 try {
212 var payload = /** @type {!SourceMapV3} */ (JSON.parse(content)); 223 var payload = /** @type {!SourceMapV3} */ (JSON.parse(content));
213 var baseURL = sourceMapURL.startsWith("data:") ? compiledURL : sourc eMapURL; 224 var baseURL = sourceMapURL.startsWith("data:") ? compiledURL : sourc eMapURL;
214
215 callback(new WebInspector.TextSourceMap(compiledURL, baseURL, payloa d)); 225 callback(new WebInspector.TextSourceMap(compiledURL, baseURL, payloa d));
216 } catch (e) { 226 } catch (e) {
217 console.error(e); 227 console.error(e);
218 WebInspector.console.warn("DevTools failed to parse SourceMap: " + s ourceMapURL); 228 WebInspector.console.warn("DevTools failed to parse SourceMap: " + s ourceMapURL);
219 callback(null); 229 callback(null);
220 } 230 }
221 } 231 }
222 } 232 }
223 233
224 WebInspector.TextSourceMap.prototype = { 234 WebInspector.TextSourceMap.prototype = {
(...skipping 14 matching lines...) Expand all
239 { 249 {
240 return this._sourceMappingURL; 250 return this._sourceMappingURL;
241 }, 251 },
242 252
243 /** 253 /**
244 * @override 254 * @override
245 * @return {!Array.<string>} 255 * @return {!Array.<string>}
246 */ 256 */
247 sourceURLs: function() 257 sourceURLs: function()
248 { 258 {
249 return Object.keys(this._sources); 259 return this._sourceURLInfos.keysArray();
250 }, 260 },
251 261
252 /** 262 /**
253 * @override 263 * @override
254 * @param {string} sourceURL 264 * @param {string} sourceURL
255 * @param {!WebInspector.ResourceType} contentType 265 * @param {!WebInspector.ResourceType} contentType
256 * @return {!WebInspector.ContentProvider} 266 * @return {!WebInspector.ContentProvider}
257 */ 267 */
258 sourceContentProvider: function(sourceURL, contentType) 268 sourceContentProvider: function(sourceURL, contentType)
259 { 269 {
260 var sourceContent = this._sourceContentByURL[sourceURL]; 270 var info = this._sourceURLInfos.get(sourceURL);
261 if (sourceContent) 271 if (info.content)
262 return WebInspector.StaticContentProvider.fromString(sourceURL, cont entType, sourceContent); 272 return WebInspector.StaticContentProvider.fromString(sourceURL, cont entType, info.content);
263 return new WebInspector.CompilerSourceMappingContentProvider(sourceURL, contentType); 273 return new WebInspector.CompilerSourceMappingContentProvider(sourceURL, contentType);
264 }, 274 },
265 275
266 /** 276 /**
267 * @override 277 * @override
268 * @return {boolean} 278 * @return {boolean}
269 */ 279 */
270 editable: function() 280 editable: function()
271 { 281 {
272 return false; 282 return false;
273 }, 283 },
274 284
275 /** 285 /**
276 * @override 286 * @override
277 * @param {!Array<!WebInspector.TextRange>} ranges 287 * @param {!Array<!WebInspector.TextRange>} ranges
278 * @param {!Array<string>} texts 288 * @param {!Array<string>} texts
279 * @return {!Promise<?WebInspector.SourceMap.EditResult>} 289 * @return {!Promise<?WebInspector.SourceMap.EditResult>}
280 */ 290 */
281 editCompiled: function(ranges, texts) 291 editCompiled: function(ranges, texts)
282 { 292 {
283 return Promise.resolve(/** @type {?WebInspector.SourceMap.EditResult} */ (null)); 293 return Promise.resolve(/** @type {?WebInspector.SourceMap.EditResult} */ (null));
284 }, 294 },
285 295
286 /** 296 /**
287 * @param {!SourceMapV3} mappingPayload
288 */
289 _parseMappingPayload: function(mappingPayload)
290 {
291 if (mappingPayload.sections)
292 this._parseSections(mappingPayload.sections);
293 else
294 this._parseMap(mappingPayload, 0, 0);
295 },
296
297 /**
298 * @param {!Array.<!SourceMapV3.Section>} sections
299 */
300 _parseSections: function(sections)
301 {
302 for (var i = 0; i < sections.length; ++i) {
303 var section = sections[i];
304 this._parseMap(section.map, section.offset.line, section.offset.colu mn);
305 }
306 },
307
308 /**
309 * @override 297 * @override
310 * @param {number} lineNumber in compiled resource 298 * @param {number} lineNumber in compiled resource
311 * @param {number} columnNumber in compiled resource 299 * @param {number} columnNumber in compiled resource
312 * @return {?WebInspector.SourceMapEntry} 300 * @return {?WebInspector.SourceMapEntry}
313 */ 301 */
314 findEntry: function(lineNumber, columnNumber) 302 findEntry: function(lineNumber, columnNumber)
315 { 303 {
316 var first = 0; 304 var first = 0;
317 var count = this._mappings.length; 305 var mappings = this.mappings();
306 var count = mappings.length;
318 while (count > 1) { 307 while (count > 1) {
319 var step = count >> 1; 308 var step = count >> 1;
320 var middle = first + step; 309 var middle = first + step;
321 var mapping = this._mappings[middle]; 310 var mapping = mappings[middle];
322 if (lineNumber < mapping.lineNumber || (lineNumber === mapping.lineN umber && columnNumber < mapping.columnNumber)) { 311 if (lineNumber < mapping.lineNumber || (lineNumber === mapping.lineN umber && columnNumber < mapping.columnNumber)) {
323 count = step; 312 count = step;
324 } else { 313 } else {
325 first = middle; 314 first = middle;
326 count -= step; 315 count -= step;
327 } 316 }
328 } 317 }
329 var entry = this._mappings[first]; 318 var entry = mappings[first];
330 if (!first && entry && (lineNumber < entry.lineNumber || (lineNumber === entry.lineNumber && columnNumber < entry.columnNumber))) 319 if (!first && entry && (lineNumber < entry.lineNumber || (lineNumber === entry.lineNumber && columnNumber < entry.columnNumber)))
331 return null; 320 return null;
332 return entry; 321 return entry;
333 }, 322 },
334 323
335 /** 324 /**
336 * @param {string} sourceURL 325 * @param {string} sourceURL
337 * @param {number} lineNumber 326 * @param {number} lineNumber
338 * @return {?WebInspector.SourceMapEntry} 327 * @return {?WebInspector.SourceMapEntry}
339 */ 328 */
(...skipping 14 matching lines...) Expand all
354 { 343 {
355 return lineNumber - mapping.sourceLineNumber; 344 return lineNumber - mapping.sourceLineNumber;
356 } 345 }
357 }, 346 },
358 347
359 /** 348 /**
360 * @return {!Array<!WebInspector.SourceMapEntry>} 349 * @return {!Array<!WebInspector.SourceMapEntry>}
361 */ 350 */
362 mappings: function() 351 mappings: function()
363 { 352 {
364 return this._mappings; 353 if (this._mappings === null) {
354 this._mappings = [];
355 this._eachSection(this._parseMap.bind(this));
356 this._json = null;
357 }
358 return /** @type {!Array<!WebInspector.SourceMapEntry>} */ (this._mappin gs);
365 }, 359 },
366 360
367 /** 361 /**
368 * @param {string} sourceURL 362 * @param {string} sourceURL
369 * @return {!Array.<!WebInspector.SourceMapEntry>} 363 * @return {!Array.<!WebInspector.SourceMapEntry>}
370 */ 364 */
371 _reversedMappings: function(sourceURL) 365 _reversedMappings: function(sourceURL)
372 { 366 {
373 var mappings = this._reverseMappingsBySourceURL.get(sourceURL); 367 if (this._sourceURLInfos && !this._sourceURLInfos.has(sourceURL))
lushnikov 2016/09/29 16:29:52 how could this._sourceURLInfos be null?
eostroukhov 2016/09/30 19:14:41 It can't. Thanks for pointing out.
374 if (!mappings)
375 return []; 368 return [];
376 if (!mappings._sorted) { 369 var mappings = this.mappings();
377 mappings.sort(sourceMappingComparator); 370 var info = this._sourceURLInfos.get(sourceURL);
378 mappings._sorted = true; 371 if (info.reverseMappings === null)
379 } 372 info.reverseMappings = mappings.filter((mapping) => mapping.sourceUR L === sourceURL).sort(sourceMappingComparator);
380 return mappings; 373 return info.reverseMappings;
381 374
382 /** 375 /**
383 * @param {!WebInspector.SourceMapEntry} a 376 * @param {!WebInspector.SourceMapEntry} a
384 * @param {!WebInspector.SourceMapEntry} b 377 * @param {!WebInspector.SourceMapEntry} b
385 * @return {number} 378 * @return {number}
386 */ 379 */
387 function sourceMappingComparator(a, b) 380 function sourceMappingComparator(a, b)
388 { 381 {
389 if (a.sourceLineNumber !== b.sourceLineNumber) 382 if (a.sourceLineNumber !== b.sourceLineNumber)
390 return a.sourceLineNumber - b.sourceLineNumber; 383 return a.sourceLineNumber - b.sourceLineNumber;
391 if (a.sourceColumnNumber !== b.sourceColumnNumber) 384 if (a.sourceColumnNumber !== b.sourceColumnNumber)
392 return a.sourceColumnNumber - b.sourceColumnNumber; 385 return a.sourceColumnNumber - b.sourceColumnNumber;
393 386
394 if (a.lineNumber !== b.lineNumber) 387 if (a.lineNumber !== b.lineNumber)
395 return a.lineNumber - b.lineNumber; 388 return a.lineNumber - b.lineNumber;
396 389
397 return a.columnNumber - b.columnNumber; 390 return a.columnNumber - b.columnNumber;
398 } 391 }
399 }, 392 },
400 393
401 /** 394 /**
395 * @param {function(!SourceMapV3, number, number)} callback
396 */
397 _eachSection: function(callback) {
398 if (!this._json.sections) {
399 callback(this._json, 0, 0);
400 return;
401 }
402 for (var section of this._json.sections)
403 callback(section.map, section.offset.line, section.offset.column);
404 },
405
406 /**
407 * @param {!SourceMapV3} sourceMap
408 * @param {function(string, ?string)} callback
409 */
410 _visitSourceURLs: function(sourceMap, callback) {
411 var sourceRoot = sourceMap.sourceRoot || "";
412 if (sourceRoot && !sourceRoot.endsWith("/"))
413 sourceRoot += "/";
414 for (var i = 0; i < sourceMap.sources.length; ++i) {
415 var href = sourceRoot + sourceMap.sources[i];
416 var url = WebInspector.ParsedURL.completeURL(this._sourceMappingURL, href) || href;
417 var source = sourceMap.sourcesContent && sourceMap.sourcesContent[i] ;
418 if (url === this._compiledURL && source)
419 url += WebInspector.UIString(" [sm]");
420 callback(url, source);
421 }
422 },
423
424 /**
425 * @param {!SourceMapV3} sourceMap
426 */
427 _parseSources: function(sourceMap) {
lushnikov 2016/09/29 16:29:53 yeah, callback is unfortunate Let's do the follow
eostroukhov 2016/09/30 19:14:41 Done.
428 this._visitSourceURLs(sourceMap,
429 (url, source) => this._sourceURLInfos.set(url, new WebInspector.Sour ceURLInfo(source, null)));
430 },
431
432 /**
402 * @param {!SourceMapV3} map 433 * @param {!SourceMapV3} map
403 * @param {number} lineNumber 434 * @param {number} lineNumber
404 * @param {number} columnNumber 435 * @param {number} columnNumber
405 */ 436 */
406 _parseMap: function(map, lineNumber, columnNumber) 437 _parseMap: function(map, lineNumber, columnNumber)
407 { 438 {
408 var sourceIndex = 0; 439 var sourceIndex = 0;
409 var sourceLineNumber = 0; 440 var sourceLineNumber = 0;
410 var sourceColumnNumber = 0; 441 var sourceColumnNumber = 0;
411 var nameIndex = 0; 442 var nameIndex = 0;
412
413 var sources = []; 443 var sources = [];
414 var names = map.names || []; 444 var names = map.names || [];
415 var sourceRoot = map.sourceRoot || "";
416 if (sourceRoot && !sourceRoot.endsWith("/"))
417 sourceRoot += "/";
418 for (var i = 0; i < map.sources.length; ++i) {
419 var href = sourceRoot + map.sources[i];
420 var url = WebInspector.ParsedURL.completeURL(this._sourceMappingURL, href) || href;
421 var hasSource = map.sourcesContent && map.sourcesContent[i];
422 if (url === this._compiledURL && hasSource)
423 url += WebInspector.UIString(" [sm]");
424 sources.push(url);
425 this._sources[url] = true;
426
427 if (hasSource)
428 this._sourceContentByURL[url] = map.sourcesContent[i];
429 }
430
431 var stringCharIterator = new WebInspector.TextSourceMap.StringCharIterat or(map.mappings); 445 var stringCharIterator = new WebInspector.TextSourceMap.StringCharIterat or(map.mappings);
446 this._visitSourceURLs(map, (url) => sources.push(url));
432 var sourceURL = sources[sourceIndex]; 447 var sourceURL = sources[sourceIndex];
433 448
434 while (true) { 449 while (true) {
435 if (stringCharIterator.peek() === ",") 450 if (stringCharIterator.peek() === ",")
436 stringCharIterator.next(); 451 stringCharIterator.next();
437 else { 452 else {
438 while (stringCharIterator.peek() === ";") { 453 while (stringCharIterator.peek() === ";") {
439 lineNumber += 1; 454 lineNumber += 1;
440 columnNumber = 0; 455 columnNumber = 0;
441 stringCharIterator.next(); 456 stringCharIterator.next();
(...skipping 17 matching lines...) Expand all
459 sourceColumnNumber += this._decodeVLQ(stringCharIterator); 474 sourceColumnNumber += this._decodeVLQ(stringCharIterator);
460 475
461 if (!stringCharIterator.hasNext() || this._isSeparator(stringCharIte rator.peek())) { 476 if (!stringCharIterator.hasNext() || this._isSeparator(stringCharIte rator.peek())) {
462 this._mappings.push(new WebInspector.SourceMapEntry(lineNumber, columnNumber, sourceURL, sourceLineNumber, sourceColumnNumber)); 477 this._mappings.push(new WebInspector.SourceMapEntry(lineNumber, columnNumber, sourceURL, sourceLineNumber, sourceColumnNumber));
463 continue; 478 continue;
464 } 479 }
465 480
466 nameIndex += this._decodeVLQ(stringCharIterator); 481 nameIndex += this._decodeVLQ(stringCharIterator);
467 this._mappings.push(new WebInspector.SourceMapEntry(lineNumber, colu mnNumber, sourceURL, sourceLineNumber, sourceColumnNumber, names[nameIndex])); 482 this._mappings.push(new WebInspector.SourceMapEntry(lineNumber, colu mnNumber, sourceURL, sourceLineNumber, sourceColumnNumber, names[nameIndex]));
468 } 483 }
469
470 for (var i = 0; i < this._mappings.length; ++i) {
471 var mapping = this._mappings[i];
472 var url = mapping.sourceURL;
473 if (!url)
474 continue;
475 if (!this._reverseMappingsBySourceURL.has(url))
476 this._reverseMappingsBySourceURL.set(url, []);
477 var reverseMappings = this._reverseMappingsBySourceURL.get(url);
478 reverseMappings.push(mapping);
479 }
480 }, 484 },
481 485
482 /** 486 /**
483 * @param {string} char 487 * @param {string} char
484 * @return {boolean} 488 * @return {boolean}
485 */ 489 */
486 _isSeparator: function(char) 490 _isSeparator: function(char)
487 { 491 {
488 return char === "," || char === ";"; 492 return char === "," || char === ";";
489 }, 493 },
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
571 }, 575 },
572 576
573 /** 577 /**
574 * @return {boolean} 578 * @return {boolean}
575 */ 579 */
576 hasNext: function() 580 hasNext: function()
577 { 581 {
578 return this._position < this._string.length; 582 return this._position < this._string.length;
579 } 583 }
580 } 584 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698