diff --git a/packages/metro-source-map/src/Consumer/MappingsConsumer.js b/packages/metro-source-map/src/Consumer/MappingsConsumer.js index f9c9b7cc08..de04febce6 100644 --- a/packages/metro-source-map/src/Consumer/MappingsConsumer.js +++ b/packages/metro-source-map/src/Consumer/MappingsConsumer.js @@ -27,9 +27,8 @@ import { lookupBiasToString, } from './constants'; import normalizeSourcePath from './normalizeSourcePath'; -import {greatestLowerBound} from './search'; import invariant from 'invariant'; -import {add, add0, get0, inc, sub} from 'ob1'; +import {add, add0, get0, get1, inc} from 'ob1'; /* eslint-disable no-bitwise */ @@ -58,12 +57,16 @@ export default class MappingsConsumer { _sourceMap: BasicSourceMap; _decodedMappings: ?ReadonlyArray; + // The index in `_decodedMappings` of the first mapping on each generated + // line (0-based), so a lookup only searches the mappings on its line. + _lineStarts: ?ReadonlyArray; _normalizedSources: ?ReadonlyArray; constructor(sourceMap: BasicSourceMap) { super(sourceMap); this._sourceMap = sourceMap; this._decodedMappings = null; + this._lineStarts = null; this._normalizedSources = null; } @@ -84,21 +87,27 @@ export default class MappingsConsumer ); } const mappings = this._decodeAndCacheMappings(); - const index = greatestLowerBound( - mappings, - {line, column}, - (position, mapping) => { - if (position.line === mapping.generatedLine) { - return get0(sub(position.column, mapping.generatedColumn)); - } - return get0(sub(position.line, mapping.generatedLine)); - }, - ); - if ( - index != null && - mappings[index].generatedLine === generatedPosition.line - ) { - const mapping = mappings[index]; + const lineStarts = this._lineStarts; + invariant(lineStarts != null, 'Expected line starts to be decoded'); + const line0 = get1(line) - 1; + if (line0 < 0 || line0 >= lineStarts.length) { + return {...EMPTY_POSITION}; + } + // The last mapping on the line at or before `column`. + let low = lineStarts[line0]; + let high = + line0 + 1 < lineStarts.length ? lineStarts[line0 + 1] : mappings.length; + const lineStart = low; + while (low < high) { + const mid = (low + high) >>> 1; + if (get0(mappings[mid].generatedColumn) <= get0(column)) { + low = mid + 1; + } else { + high = mid; + } + } + if (low > lineStart) { + const mapping = mappings[low - 1]; return { source: mapping.source, name: mapping.name, @@ -113,6 +122,7 @@ export default class MappingsConsumer const normalizedSources = this._normalizeAndCacheSources(); const {mappings: mappingsRaw, names} = this._sourceMap; const result: Array = []; + const lineStarts = [0]; let generatedLine = FIRST_LINE; let generatedColumn = FIRST_COLUMN; @@ -170,6 +180,7 @@ export default class MappingsConsumer fieldCount = 0; } if (charCode === SEMICOLON && i < length) { + lineStarts.push(result.length); generatedLine = inc(generatedLine); generatedColumn = FIRST_COLUMN; } @@ -191,6 +202,7 @@ export default class MappingsConsumer shift = 0; } } + this._lineStarts = lineStarts; return result; } diff --git a/packages/metro-source-map/src/__tests__/Consumer-test.js b/packages/metro-source-map/src/__tests__/Consumer-test.js index bb9aa64b78..cbd235e2d6 100644 --- a/packages/metro-source-map/src/__tests__/Consumer-test.js +++ b/packages/metro-source-map/src/__tests__/Consumer-test.js @@ -133,6 +133,33 @@ describe('basic maps', () => { } `); }); + + test('a column before the first mapping on its line is unmapped', () => { + const consumer = new Consumer({ + version: 3, + mappings: 'AAAA;KAAC', + names: [], + sources: ['source0'], + }); + expect( + consumer.originalPositionFor({line: add1(1), column: add0(4)}), + ).toEqual({source: null, name: null, line: null, column: null}); + expect( + consumer.originalPositionFor({line: add1(1), column: add0(5)}), + ).toEqual({source: 'source0', name: null, line: 1, column: 1}); + }); + + test('mappings at the same generated column resolve to the last', () => { + const consumer = new Consumer({ + version: 3, + mappings: 'AAAA,AACA', + names: [], + sources: ['source0'], + }); + expect( + consumer.originalPositionFor({line: add1(0), column: add0(0)}), + ).toEqual({source: 'source0', name: null, line: 2, column: 0}); + }); }); describe('generatedMappings()', () => {