diff --git a/docs-developer/CHANGELOG-formats.md b/docs-developer/CHANGELOG-formats.md index 50da5c96d4..f715f96ab2 100644 --- a/docs-developer/CHANGELOG-formats.md +++ b/docs-developer/CHANGELOG-formats.md @@ -6,6 +6,21 @@ Note that this is not an exhaustive list. Processed profile format upgraders can ## Processed profile format +### Version 63 + +A new `SourceLocationTable` has been added to `profile.shared.originalLocation`. It holds the original (pre-compilation) source positions produced by source map symbolication, paired with the generated `line`/`column` already on `FrameTable`. + +- `source: IndexIntoSourceTable[]`: source file index. Set independently for func entries (the function's definition file) and frame entries (the execution point's file). +- `line: number[]`: 1-based line number +- `column: number[]`: 1-based column number + +Two new columns were added that index into this table: + +- `FrameTable.originalLocation: Array`: the original execution point for the frame +- `FuncTable.originalLocation: Array`: the original definition site for the function + +A new `content: Array` column was added to `SourceTable`. + ### Version 62 A new `display` field of type `CounterDisplayConfig` was added to `RawCounter`. diff --git a/jest.config.js b/jest.config.js index 3b89ea2c4e..573fd8338e 100644 --- a/jest.config.js +++ b/jest.config.js @@ -30,6 +30,7 @@ const browserEnvConfig = { globals: { AVAILABLE_STAGING_LOCALES: null, + SOURCE_MAP_WORKER_PATH: 'src/test/fixtures/source-map.worker.stub.js', }, snapshotFormat: { diff --git a/package.json b/package.json index 3f3aab20bb..489fa2b1ca 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "@fluent/langneg": "^0.7.0", "@fluent/react": "^0.15.2", "@lezer/highlight": "^1.2.3", + "@lezer/javascript": "^1.5.4", "@streamparser/json": "^0.0.22", "@tgwf/co2": "^0.18.0", "array-move": "^3.0.1", @@ -109,6 +110,8 @@ "redux-logger": "^3.0.6", "redux-thunk": "^3.1.0", "reselect": "^4.1.8", + "source-map": "^0.7.6", + "url": "^0.11.4", "valibot": "^1.4.0", "workbox-window": "^7.4.1" }, diff --git a/scripts/build-profiler-cli.mjs b/scripts/build-profiler-cli.mjs index 4cff0ef1d9..9e90b69c28 100644 --- a/scripts/build-profiler-cli.mjs +++ b/scripts/build-profiler-cli.mjs @@ -23,6 +23,9 @@ const profilerCliConfig = { define: { __BUILD_HASH__: JSON.stringify(BUILD_HASH), __VERSION__: JSON.stringify(version), + // SOURCE_MAP_WORKER_PATH is injected by the browser build. The CLI doesn't + // use source map workers but the shared code references this constant. + SOURCE_MAP_WORKER_PATH: JSON.stringify('/source-map.worker.js'), }, external: [...nodeBaseConfig.external, 'gecko-profiler-demangle'], }; diff --git a/scripts/build.mjs b/scripts/build.mjs index 13334a63cf..01ebca600d 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -3,14 +3,32 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import esbuild from 'esbuild'; -import { mainBundleConfig } from './lib/esbuild-configs.mjs'; +import { + mainBundleConfig, + sourceMapWorkerConfig, + getSourceMapWorkerPath, +} from './lib/esbuild-configs.mjs'; import { cleanDist, saveMetafile } from './lib/build-utils.mjs'; async function build() { cleanDist(); - const buildResult = await esbuild.build(mainBundleConfig); + + // Build the worker first so we can read its output path from the metafile + // and inject it into the main bundle via SOURCE_MAP_WORKER_PATH. + const workerResult = await esbuild.build(sourceMapWorkerConfig); + + const buildResult = await esbuild.build({ + ...mainBundleConfig, + define: { + ...mainBundleConfig.define, + SOURCE_MAP_WORKER_PATH: JSON.stringify( + getSourceMapWorkerPath(workerResult.metafile) + ), + }, + }); + saveMetafile(buildResult); - console.log('✅ Main browser build completed'); + console.log('✅ Main browser build and source map worker completed'); } build().catch(console.error); diff --git a/scripts/lib/dev-server.mjs b/scripts/lib/dev-server.mjs index 471924d58e..aae460741e 100644 --- a/scripts/lib/dev-server.mjs +++ b/scripts/lib/dev-server.mjs @@ -56,6 +56,7 @@ export async function startDevServer(buildConfig, options = {}) { fallback = 'index.html', onServerStart, cleanDist = true, + extraWatchConfigs = [], } = options; // Clean dist directory first @@ -77,6 +78,12 @@ export async function startDevServer(buildConfig, options = {}) { // Start watching for changes await buildContext.watch(); + // Watch extra configs (no serving needed, just watch for rebuilds) + const extraContexts = await Promise.all( + extraWatchConfigs.map((config) => esbuild.context(config)) + ); + await Promise.all(extraContexts.map((ctx) => ctx.watch())); + // Create HTTP server const server = http.createServer((req, res) => { // Validate Host header @@ -135,7 +142,9 @@ export async function startDevServer(buildConfig, options = {}) { isShuttingDown = true; console.log('\nShutting down...'); - await buildContext.dispose(); + await Promise.all( + [buildContext, ...extraContexts].map((ctx) => ctx.dispose()) + ); server.close(); process.exit(0); }); diff --git a/scripts/lib/esbuild-configs.mjs b/scripts/lib/esbuild-configs.mjs index 42f97c868b..df7a2cf3b6 100644 --- a/scripts/lib/esbuild-configs.mjs +++ b/scripts/lib/esbuild-configs.mjs @@ -81,6 +81,9 @@ export const mainBundleConfig = { : 'undefined', // no need to define NODE_ENV: // esbuild automatically defines NODE_ENV based on the value for "minify" + // In dev, the worker is not hashed so the path is predictable. + // In production, build.mjs overrides this after building the worker first. + SOURCE_MAP_WORKER_PATH: JSON.stringify('/source-map.worker.js'), }, external: ['zlib'], plugins: [ @@ -98,6 +101,10 @@ export const mainBundleConfig = { { from: ['res/img/favicon.png'], to: ['dist/res/img'] }, { from: ['docs-user/**/*'], to: ['dist/docs'] }, { from: ['locales/**/*'], to: ['dist/locales'] }, + { + from: ['node_modules/source-map/lib/mappings.wasm'], + to: ['dist'], + }, ], }), generateHtmlPlugin({ @@ -108,6 +115,35 @@ export const mainBundleConfig = { ], }; +// Source map worker bundle configuration. +// Built as a standalone IIFE so that npm dependencies (lezer, source-map) are +// bundled into a single file that can be loaded as a Web Worker without needing +// ES module support. In production the output filename includes a content hash +// (e.g. source-map-ABCD1234.worker.js). The path is then injected into the main +// bundle via the SOURCE_MAP_WORKER_PATH define. In dev there is no hash since the +// dev server always serves fresh content and the define can't be updated mid-watch. +export const sourceMapWorkerConfig = { + ...baseConfig, + entryPoints: ['src/profile-logic/source-map.worker.ts'], + outdir: 'dist', + format: 'iife', + platform: 'browser', + target: browserslistToEsbuild(), + sourcemap: true, + splitting: false, + entryNames: isProduction ? '[name]-[hash]' : '[name]', + metafile: true, + plugins: [wasmLoader()], +}; + +export function getSourceMapWorkerPath(metafile) { + const [entryPoint] = sourceMapWorkerConfig.entryPoints; + const [outputPath] = Object.entries(metafile.outputs).find( + ([, output]) => output.entryPoint === entryPoint + ); + return '/' + path.basename(outputPath); +} + // Photon styling build configuration const photonTemplateHTML = fs.readFileSync( path.join(projectRoot, 'res', 'photon', 'index.html'), diff --git a/scripts/run-dev-server.mjs b/scripts/run-dev-server.mjs index 0edc44612a..71d87fef02 100644 --- a/scripts/run-dev-server.mjs +++ b/scripts/run-dev-server.mjs @@ -2,7 +2,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import path from 'path'; -import { mainBundleConfig } from './lib/esbuild-configs.mjs'; +import { + mainBundleConfig, + sourceMapWorkerConfig, +} from './lib/esbuild-configs.mjs'; import { startDevServer } from './lib/dev-server.mjs'; import { serveAndOpenProfile } from './lib/profile-server.mjs'; import yargs from 'yargs'; @@ -22,6 +25,7 @@ startDevServer(mainBundleConfig, { host, distDir: 'dist', cleanDist: true, + extraWatchConfigs: [sourceMapWorkerConfig], onServerStart: (profilerUrl) => { const barAscii = '------------------------------------------------------------------------------------------'; diff --git a/src/actions/receive-profile.ts b/src/actions/receive-profile.ts index 7eb4182cdd..b8c980f030 100644 --- a/src/actions/receive-profile.ts +++ b/src/actions/receive-profile.ts @@ -76,7 +76,9 @@ import { determineTimelineType, hasUsefulSamples, } from 'firefox-profiler/profile-logic/profile-data'; +import { doSourceMapSymbolication } from './source-map-symbolication'; +import type { RawSourceMap } from 'source-map'; import type { RequestedLib, ImplementationFilter, @@ -89,6 +91,7 @@ import type { TabID, PageList, MixedObject, + IndexIntoSourceTable, } from 'firefox-profiler/types'; import type { SymbolicationStepInfo } from '../profile-logic/symbolication'; @@ -245,7 +248,28 @@ export function finalizeProfileView( } } - await Promise.all([faviconsPromise, symbolicationPromise]); + // Fetch source maps for all JS sources with a sourceMapURL, then run the + // source-map worker. Runs fully in parallel with native symbolication: + // native only touches funcs/frames belonging to library resources (JS + // funcs aren't in those sets), and the JS apply step reads current + // shared state at dispatch time so it composes with whatever native + // has committed by then. Requires WebChannel version 7+. + let sourceMapSymbolicationPromise: Promise | null = null; + if (browserConnection !== null && browserConnection.supportsGetSourceMap) { + sourceMapSymbolicationPromise = doResolveSourceMaps( + profile, + browserConnection, + dispatch + ).then(({ resolvedSourceMaps, compiledSources }) => + dispatch(doSourceMapSymbolication(resolvedSourceMaps, compiledSources)) + ); + } + + await Promise.all([ + faviconsPromise, + symbolicationPromise, + sourceMapSymbolicationPromise, + ]); }; } @@ -811,6 +835,85 @@ export async function doSymbolicateProfile( dispatch(doneSymbolicating()); } +/** + * Resolve JS source maps for every source in the profile that has both a + * sourceMapURL and a UUID. Fetches source maps via the browser WebChannel. + * + * Also fetches the compiled source text which is required by the scope-tree + * name resolution in symbolicateWithSourceMaps. + */ +async function doResolveSourceMaps( + profile: Profile, + browserConnection: BrowserConnection, + dispatch: Dispatch +): Promise<{ + resolvedSourceMaps: Map; + compiledSources: Map; +}> { + const { sources, stringArray } = profile.shared; + + // Collect every source with a sourceMapURL and a UUID. Only UUID-bearing + // sources can be fetched via the browser WebChannel. + const sourceIndexesWithSourceMaps = new Set(); + for (let sourceIndex = 0; sourceIndex < sources.length; sourceIndex++) { + if ( + sources.sourceMapURL[sourceIndex] !== null && + sources.sourceMapURL[sourceIndex] !== undefined && + typeof sources.id[sourceIndex] === 'string' + ) { + sourceIndexesWithSourceMaps.add(sourceIndex); + } + } + + if (sourceIndexesWithSourceMaps.size === 0) { + return { resolvedSourceMaps: new Map(), compiledSources: new Map() }; + } + + // Fetch source maps and compiled sources in parallel, ignoring individual failures. + const resolvedSourceMaps: Map = new Map(); + const compiledSources: Map = new Map(); + + dispatch({ type: 'START_SOURCE_MAP_FETCHING' }); + try { + await Promise.all( + Array.from(sourceIndexesWithSourceMaps).map(async (sourceIndex) => { + const filename = stringArray[sources.filename[sourceIndex]]; + // sourceId is guaranteed non-null by the filter above. + const sourceId = sources.id[sourceIndex] as string; + + await Promise.all([ + browserConnection + .getSourceMap(sourceId) + .then((result) => { + resolvedSourceMaps.set(sourceIndex, result); + }) + .catch((e) => { + console.warn( + `Failed to fetch source map for "${filename}" (id=${sourceId}):`, + e + ); + }), + browserConnection + .getJSSource(sourceId) + .then((text) => { + compiledSources.set(sourceIndex, text); + }) + .catch((e) => { + console.warn( + `Failed to fetch compiled source for "${filename}" (id=${sourceId}):`, + e + ); + }), + ]); + }) + ); + } finally { + dispatch({ type: 'DONE_SOURCE_MAP_FETCHING' }); + } + + return { resolvedSourceMaps, compiledSources }; +} + export async function retrievePageFaviconsFromBrowser( dispatch: Dispatch, pages: PageList, diff --git a/src/actions/source-map-symbolication.ts b/src/actions/source-map-symbolication.ts new file mode 100644 index 0000000000..7bb309a77a --- /dev/null +++ b/src/actions/source-map-symbolication.ts @@ -0,0 +1,141 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import { getRawProfileSharedData } from 'firefox-profiler/selectors'; +import { applySourceMapSymbolicationResponse } from 'firefox-profiler/profile-logic/source-map-symbolication'; + +import type { + WorkerInput, + WorkerOutput, +} from 'firefox-profiler/profile-logic/source-map-worker-types'; +import type { IndexIntoSourceTable, ThunkAction } from 'firefox-profiler/types'; +import type { RawSourceMap } from 'source-map'; +import { assertExhaustiveCheck } from 'firefox-profiler/utils/types'; + +/** + * Run source map symbolication using previously-fetched source maps from Redux + * state. Offloads source parsing and source-map lookups to a dedicated Web + * Worker so the main thread stays responsive. + * + * Reads the current profile from Redux state at dispatch time. Callers must + * ensure native symbolication has already committed its changes before + * dispatching this action, so that JS symbolication builds on the final state. + * + * `compiledSources` maps bundle IndexIntoSourceTable values to compiled source + * text (fetched alongside the source maps). Used for scope-tree-based function + * name resolution. + */ +export function doSourceMapSymbolication( + resolvedSourceMaps: Map, + compiledSources: Map +): ThunkAction> { + return async (dispatch, getState) => { + if (resolvedSourceMaps.size === 0) { + return; + } + + const shared = getRawProfileSharedData(getState()); + + const input: WorkerInput = { + resolvedSourceMaps, + compiledSources, + funcTable: shared.funcTable, + frameTable: shared.frameTable, + originalLocation: shared.originalLocation, + sources: shared.sources, + stringArray: shared.stringArray, + }; + + dispatch({ type: 'START_SOURCE_MAP_SYMBOLICATION' }); + const result = await _runSourceMapWorker(input); + switch (result.type) { + case 'success': { + // Apply against the current shared state (not the snapshot the worker + // received), so concurrent worker runs compose instead of stomping + // each other's results. + const currentShared = getRawProfileSharedData(getState()); + const applied = applySourceMapSymbolicationResponse( + currentShared, + result.response + ); + if (applied === null) { + dispatch({ type: 'SOURCE_MAP_SYMBOLICATION_FAILED' }); + break; + } + dispatch({ + type: 'BULK_SOURCE_MAP_SYMBOLICATION', + newFuncTable: applied.newFuncTable, + newFrameTable: applied.newFrameTable, + newOriginalLocation: applied.newOriginalLocation, + newSources: applied.newSources, + newStringArray: applied.newStringArray, + }); + break; + } + case 'error': + console.warn('Source map worker error:', result.message); + dispatch({ type: 'SOURCE_MAP_SYMBOLICATION_FAILED' }); + break; + case 'no-op': + dispatch({ type: 'SOURCE_MAP_SYMBOLICATION_FAILED' }); + break; + default: + throw assertExhaustiveCheck(result); + } + }; +} + +/** + * Spawn a one-shot source map worker, send it the input, and return the + * output. The worker is terminated once a response is received or an error + * occurs. Uses the same on-demand spawn pattern as gz.browser.ts. + * + * Debugging tip: to step through symbolication from the page DevTools, paste + * the snippet below over this function body. It runs the same core on the + * main thread (blocking, debug only). The worker no longer mutates its + * input, so no defensive cloning is needed here. + * + * const { runSourceMapSymbolicationCore } = await import( + * 'firefox-profiler/profile-logic/source-map-symbolication' + * ); + * const wasmUrl = new URL('/mappings.wasm', window.location.href).href; + * return runSourceMapSymbolicationCore(input, wasmUrl); + */ +function _runSourceMapWorker(input: WorkerInput): Promise { + return new Promise((resolve) => { + const reportError = (err: unknown): void => { + resolve({ + type: 'error', + message: err instanceof Error ? err.message : String(err), + }); + }; + + let worker: Worker; + try { + worker = new Worker(SOURCE_MAP_WORKER_PATH); + } catch (err) { + reportError(err); + return; + } + + worker.onmessage = (e: MessageEvent) => { + resolve(e.data); + worker.terminate(); + }; + worker.onerror = (e: ErrorEvent) => { + resolve({ + type: 'error', + message: e.error?.message ?? e.message ?? 'Source map worker failed', + }); + worker.terminate(); + }; + + try { + worker.postMessage(input); + } catch (err) { + reportError(err); + worker.terminate(); + } + }); +} diff --git a/src/app-logic/browser-connection.ts b/src/app-logic/browser-connection.ts index 8a40db9c14..1ef37bede9 100644 --- a/src/app-logic/browser-connection.ts +++ b/src/app-logic/browser-connection.ts @@ -12,8 +12,11 @@ import { querySymbolicationApiViaWebChannel, getPageFaviconsViaWebChannel, showFunctionInDevtoolsViaWebChannel, - getJSSourcesViaWebChannel, + getJSSourcesViaWebChannelV6, + getJSSourcesViaWebChannelV7, + getSourceMapViaWebChannel, } from './web-channel'; +import type { RawSourceMap } from 'source-map'; import type { Milliseconds, FaviconData, @@ -86,6 +89,14 @@ export interface BrowserConnection { ): Promise; getJSSource(sourceUuid: string): Promise; + + // Get source map of the given source directly from the browser. + // Requires WebChannel version 7+. + getSourceMap(sourceId: string): Promise; + + // True when the browser exposes GET_SOURCE_MAP (WebChannel version 7+). + // Callers use this to gate source-map-based features. + readonly supportsGetSourceMap: boolean; } /** @@ -96,21 +107,25 @@ export interface BrowserConnection { * the profile or symbols. So this class also supports the frame script. */ class BrowserConnectionImpl implements BrowserConnection { + _webChannelVersion: number; _webChannelSupportsGetProfileAndSymbolication: boolean; _webChannelSupportsGetExternalPowerTracks: boolean; _webChannelSupportsGetExternalMarkers: boolean; _webChannelSupportsGetPageFavicons: boolean; _webChannelSupportsOpenDebuggerInTab: boolean; _webChannelSupportsGetJSSource: boolean; + readonly supportsGetSourceMap: boolean; _geckoProfiler: $GeckoProfiler | undefined; constructor(webChannelVersion: number) { + this._webChannelVersion = webChannelVersion; this._webChannelSupportsGetProfileAndSymbolication = webChannelVersion >= 1; this._webChannelSupportsGetExternalPowerTracks = webChannelVersion >= 2; this._webChannelSupportsGetExternalMarkers = webChannelVersion >= 3; this._webChannelSupportsGetPageFavicons = webChannelVersion >= 4; this._webChannelSupportsOpenDebuggerInTab = webChannelVersion >= 5; this._webChannelSupportsGetJSSource = webChannelVersion >= 6; + this.supportsGetSourceMap = webChannelVersion >= 7; } // Only called when we must obtain the profile from the browser, i.e. if we @@ -232,6 +247,15 @@ class BrowserConnectionImpl implements BrowserConnection { return []; } + async getSourceMap(sourceId: string): Promise { + if (!this.supportsGetSourceMap) { + throw new Error( + "Can't use getSourceMap in Firefox versions with the old WebChannel." + ); + } + return getSourceMapViaWebChannel(sourceId); + } + /** * Fetches JavaScript source code from the browser using the source UUID. * This method requires WebChannel version 6 or higher (Firefox 145+). @@ -247,7 +271,11 @@ class BrowserConnectionImpl implements BrowserConnection { // fetching multiple sources, we only fetch one at a time currently. // TODO: Change this to fetch multiple JS sources at the load time or while // we share the profile. - return getJSSourcesViaWebChannel([sourceUuid]).then((sources) => { + const sourcesPromise = + this._webChannelVersion >= 7 + ? getJSSourcesViaWebChannelV7([sourceUuid]) + : getJSSourcesViaWebChannelV6([sourceUuid]); + return sourcesPromise.then((sources) => { const source = sources[0]; if ('error' in source) { throw new Error(source.error); diff --git a/src/app-logic/constants.ts b/src/app-logic/constants.ts index a8bdb0e23c..ef712c02b8 100644 --- a/src/app-logic/constants.ts +++ b/src/app-logic/constants.ts @@ -12,7 +12,7 @@ export const GECKO_PROFILE_VERSION = 34; // The current version of the "processed" profile format. // Please don't forget to update the processed profile format changelog in // `docs-developer/CHANGELOG-formats.md`. -export const PROCESSED_PROFILE_VERSION = 62; +export const PROCESSED_PROFILE_VERSION = 63; // The following are the margin sizes for the left and right of the timeline. Independent // components need to share these values. diff --git a/src/app-logic/web-channel.ts b/src/app-logic/web-channel.ts index 4db2cf6825..3316c6e822 100644 --- a/src/app-logic/web-channel.ts +++ b/src/app-logic/web-channel.ts @@ -8,6 +8,7 @@ import type { ExternalMarkersData, FaviconData, } from 'firefox-profiler/types'; +import type { RawSourceMap } from 'source-map'; /** * This file is in charge of handling the message managing between profiler.firefox.com @@ -29,7 +30,8 @@ export type Request = | QuerySymbolicationApiRequest | GetPageFaviconsRequest | OpenScriptInTabDebuggerRequest - | GetJSSourcesRequest; + | GetJSSourcesRequest + | GetSourceMapRequest; type StatusQueryRequest = { type: 'STATUS_QUERY' }; type EnableMenuButtonRequest = { type: 'ENABLE_MENU_BUTTON' }; @@ -65,9 +67,14 @@ type OpenScriptInTabDebuggerRequest = { line: number | null; column: number | null; }; -type GetJSSourcesRequest = { - type: 'GET_JS_SOURCES'; - sourceUuids: Array; +type GetJSSourcesRequest = + // Version 7+ uses sourceIds + | { type: 'GET_JS_SOURCES'; sourceIds: Array } + // Version 6 uses sourceUuids + | { type: 'GET_JS_SOURCES'; sourceUuids: Array }; +type GetSourceMapRequest = { + type: 'GET_SOURCE_MAP'; + sourceId: string; }; export type MessageFromBrowser = @@ -138,6 +145,11 @@ type StatusQueryResponse = { // Shipped in Firefox 145. // Adds support for fetching JS sources. // - GET_JS_SOURCES + // Version 7: + // Adds support for fetching source maps via the browser, which can access + // URLs that the frontend cannot. + // - GET_SOURCE_MAP + // Also renames the GET_JS_SOURCES request field from `sourceUuids` to `sourceIds`. version?: number; }; type EnableMenuButtonResponse = void; @@ -150,6 +162,7 @@ type GetPageFaviconsResponse = Array; type OpenScriptInTabDebuggerResponse = void; type GetJSSourceReponseItem = { sourceText: string } | { error: string }; type GetJSSourcesResponse = Array; +type GetSourceMapResponse = RawSourceMap; // TypeScript function overloads for request/response pairs. function _sendMessageWithResponse( @@ -182,6 +195,9 @@ function _sendMessageWithResponse( function _sendMessageWithResponse( request: GetJSSourcesRequest ): Promise; +function _sendMessageWithResponse( + request: GetSourceMapRequest +): Promise; function _sendMessageWithResponse(request: Request): Promise { const requestId = _requestId++; @@ -387,7 +403,7 @@ export async function showFunctionInDevtoolsViaWebChannel( }); } -export async function getJSSourcesViaWebChannel( +export async function getJSSourcesViaWebChannelV6( sourceUuids: Array ): Promise> { return _sendMessageWithResponse({ @@ -396,6 +412,24 @@ export async function getJSSourcesViaWebChannel( }); } +export async function getJSSourcesViaWebChannelV7( + sourceIds: Array +): Promise> { + return _sendMessageWithResponse({ + type: 'GET_JS_SOURCES', + sourceIds, + }); +} + +export async function getSourceMapViaWebChannel( + sourceId: string +): Promise { + return _sendMessageWithResponse({ + type: 'GET_SOURCE_MAP', + sourceId, + }); +} + /** * ----------------------------------------------------------------------------- * diff --git a/src/components/app/SymbolicationStatusOverlay.tsx b/src/components/app/SymbolicationStatusOverlay.tsx index a4157a2e3e..ab96aba373 100644 --- a/src/components/app/SymbolicationStatusOverlay.tsx +++ b/src/components/app/SymbolicationStatusOverlay.tsx @@ -6,10 +6,15 @@ import { PureComponent } from 'react'; import { getProfileViewOptions, getSymbolicationStatus, + getSourceMapSymbolicationStatus, } from 'firefox-profiler/selectors/profile'; import explicitConnect from 'firefox-profiler/utils/connect'; -import type { RequestedLib, State } from 'firefox-profiler/types'; +import type { + RequestedLib, + SourceMapSymbolicationStatus, + State, +} from 'firefox-profiler/types'; import type { ConnectedProps } from 'firefox-profiler/utils/connect'; import './SymbolicationStatusOverlay.css'; @@ -33,6 +38,7 @@ function englishListJoin(list: string[]) { type StateProps = { readonly symbolicationStatus: string; + readonly sourceMapSymbolicationStatus: SourceMapSymbolicationStatus; readonly waitingForLibs: Set; }; @@ -40,7 +46,11 @@ type Props = ConnectedProps<{}, StateProps, {}>; class SymbolicationStatusOverlayImpl extends PureComponent { override render() { - const { symbolicationStatus, waitingForLibs } = this.props; + const { + symbolicationStatus, + sourceMapSymbolicationStatus, + waitingForLibs, + } = this.props; if (symbolicationStatus === 'SYMBOLICATING') { if (waitingForLibs.size > 0) { const libNames = Array.from(waitingForLibs.values()).map( @@ -62,6 +72,23 @@ class SymbolicationStatusOverlayImpl extends PureComponent { ); } + // Native takes priority: only show source-map status when native isn't active. + if (sourceMapSymbolicationStatus === 'FETCHING') { + return ( +
+ + Fetching source maps... +
+ ); + } + if (sourceMapSymbolicationStatus === 'SYMBOLICATING') { + return ( +
+ + Symbolicating JS source maps... +
+ ); + } return
; } } @@ -69,6 +96,7 @@ class SymbolicationStatusOverlayImpl extends PureComponent { export const SymbolicationStatusOverlay = explicitConnect<{}, StateProps, {}>({ mapStateToProps: (state: State) => ({ symbolicationStatus: getSymbolicationStatus(state), + sourceMapSymbolicationStatus: getSourceMapSymbolicationStatus(state), waitingForLibs: getProfileViewOptions(state).waitingForLibs, }), component: SymbolicationStatusOverlayImpl, diff --git a/src/components/shared/CallNodeContextMenu.tsx b/src/components/shared/CallNodeContextMenu.tsx index 1a360f01c6..cfb655cf5c 100644 --- a/src/components/shared/CallNodeContextMenu.tsx +++ b/src/components/shared/CallNodeContextMenu.tsx @@ -253,7 +253,14 @@ class CallNodeContextMenuImpl extends React.PureComponent { const { callNodeIndex, - thread: { funcTable, resourceTable, stringTable, sources }, + thread: { + frameTable, + funcTable, + resourceTable, + stringTable, + sources, + originalLocation, + }, callNodeInfo, } = rightClickedCallNodeInfo; @@ -271,10 +278,13 @@ class CallNodeContextMenuImpl extends React.PureComponent { const funcName = stringTable.getString(funcNameIndex); const originAnnotation = getOriginAnnotationForFunc( funcIndex, + null, + frameTable, funcTable, resourceTable, stringTable, - sources + sources, + originalLocation ); return funcName + (originAnnotation ? ` [${originAnnotation}]` : ''); }) diff --git a/src/components/tooltip/CallNode.tsx b/src/components/tooltip/CallNode.tsx index 220fa723f2..e84107d2f0 100644 --- a/src/components/tooltip/CallNode.tsx +++ b/src/components/tooltip/CallNode.tsx @@ -8,7 +8,10 @@ import { getStackType } from 'firefox-profiler/profile-logic/transforms'; import { parseFileNameFromSymbolication } from 'firefox-profiler/utils/special-paths'; import { formatCallNodeNumberWithUnit } from 'firefox-profiler/utils/format-numbers'; import { Icon } from 'firefox-profiler/components/shared/Icon'; -import { getCategoryPairLabel } from 'firefox-profiler/profile-logic/profile-data'; +import { + getCategoryPairLabel, + getOriginalPositionForFrame, +} from 'firefox-profiler/profile-logic/profile-data'; import { countPositiveValues } from 'firefox-profiler/utils'; import type { @@ -379,9 +382,19 @@ export class TooltipCallNode extends React.PureComponent { let fileName = null; - const sourceIndex = thread.funcTable.source[funcIndex]; - const { sources } = thread; + const { + source: sourceIndex, + line: lineNumber, + column: columnNumber, + } = getOriginalPositionForFrame( + null, + funcIndex, + thread.frameTable, + thread.funcTable, + thread.originalLocation + ); + if (sourceIndex !== null) { const fileNameIndex = sources.filename[sourceIndex]; let fileNameURL = thread.stringTable.getString(fileNameIndex); @@ -390,12 +403,8 @@ export class TooltipCallNode extends React.PureComponent { // If it's a path from symbolication, strip it down to just the actual path. fileNameURL = parseFileNameFromSymbolication(fileNameURL).path; - // JS functions have information about where the function starts. - // Add :: to the URL, if known. - const lineNumber = thread.funcTable.lineNumber[funcIndex]; if (lineNumber !== null) { fileNameURL += ':' + lineNumber; - const columnNumber = thread.funcTable.columnNumber[funcIndex]; if (columnNumber !== null) { fileNameURL += ':' + columnNumber; } diff --git a/src/profile-logic/bottom-box.ts b/src/profile-logic/bottom-box.ts index 70ff159167..8de8ada2a9 100644 --- a/src/profile-logic/bottom-box.ts +++ b/src/profile-logic/bottom-box.ts @@ -16,6 +16,7 @@ import { getCallNodeFramePerStack, getNativeSymbolInfo, getNativeSymbolsForCallNode, + getOriginalPositionForFrame, getTotalNativeSymbolTimingsForCallNode, } from './profile-data'; import { mapGetKeyWithMaxValue } from 'firefox-profiler/utils'; @@ -46,7 +47,13 @@ export function getBottomBoxInfoForCallNode( } = thread; const funcIndex = callNodeInfo.funcForNode(callNodeIndex); - const sourceIndex = funcTable.source[funcIndex]; + const { source: sourceIndex, line: funcLine } = getOriginalPositionForFrame( + null, + funcIndex, + frameTable, + funcTable, + thread.originalLocation + ); const resource = funcTable.resource[funcIndex]; const libIndex = resource !== -1 && resourceTable.type[resource] === ResourceType.Library @@ -98,13 +105,16 @@ export function getBottomBoxInfoForCallNode( ); // Compute the hottest line and instruction address, so we can ask the - // source and assembly view to scroll those into view. - const funcLine = funcTable.lineNumber[funcIndex]; + // source and assembly view to scroll those into view. funcLine and the per-sample + // frame lines come from getOriginalPositionForFrame, so the scroll target lines + // up with the (original) source view's line numbering when symbolicated. const lineTimings = getTotalLineTimingsForCallNode( samples, callNodeFramePerStack, frameTable, - funcLine + funcTable, + funcLine, + thread.originalLocation ); const hottestLine = mapGetKeyWithMaxValue(lineTimings); const addressTimings = getTotalAddressTimingsForCallNode( @@ -149,7 +159,13 @@ export function getBottomBoxInfoForStackFrame( const frameIndex = stackTable.frame[stackIndex]; const funcIndex = frameTable.func[frameIndex]; - const sourceIndex = funcTable.source[funcIndex]; + const { source: sourceIndex, line: lineNumber } = getOriginalPositionForFrame( + frameIndex, + funcIndex, + frameTable, + funcTable, + thread.originalLocation + ); const resource = funcTable.resource[funcIndex]; const libIndex = resource !== -1 && resourceTable.type[resource] === ResourceType.Library @@ -173,9 +189,6 @@ export function getBottomBoxInfoForStackFrame( const instructionAddress = nativeSymbol !== null ? frameTable.address[frameIndex] : -1; - // Extract line number from the frame - const lineNumber = frameTable.line[frameIndex]; - return { libIndex, sourceIndex, diff --git a/src/profile-logic/call-tree.ts b/src/profile-logic/call-tree.ts index fe026267cd..6022e6a953 100644 --- a/src/profile-logic/call-tree.ts +++ b/src/profile-logic/call-tree.ts @@ -608,10 +608,13 @@ export class CallTree { _getOriginAnnotation(funcIndex: IndexIntoFuncTable): string { return getOriginAnnotationForFunc( funcIndex, + null, + this._thread.frameTable, this._thread.funcTable, this._thread.resourceTable, this._thread.stringTable, - this._thread.sources + this._thread.sources, + this._thread.originalLocation ); } diff --git a/src/profile-logic/data-structures.ts b/src/profile-logic/data-structures.ts index 0f25fe3e39..8d8307f1b5 100644 --- a/src/profile-logic/data-structures.ts +++ b/src/profile-logic/data-structures.ts @@ -26,6 +26,7 @@ import type { JsTracerTable, CallNodeTable, SourceTable, + SourceLocationTable, } from 'firefox-profiler/types'; /** @@ -93,6 +94,7 @@ export function getEmptyFrameTable(): FrameTable { innerWindowID: [], line: [], column: [], + originalLocation: [], length: 0, }; } @@ -112,6 +114,7 @@ export function shallowCloneFrameTable(frameTable: FrameTable): FrameTable { innerWindowID: frameTable.innerWindowID.slice(), line: frameTable.line.slice(), column: frameTable.column.slice(), + originalLocation: frameTable.originalLocation.slice(), length: frameTable.length, }; } @@ -129,6 +132,7 @@ export function getEmptyFuncTable(): FuncTable { source: [], lineNumber: [], columnNumber: [], + originalLocation: [], length: 0, }; } @@ -146,10 +150,31 @@ export function shallowCloneFuncTable(funcTable: FuncTable): FuncTable { source: funcTable.source.slice(), lineNumber: funcTable.lineNumber.slice(), columnNumber: funcTable.columnNumber.slice(), + originalLocation: funcTable.originalLocation.slice(), length: funcTable.length, }; } +export function getEmptySourceLocationTable(): SourceLocationTable { + return { + source: [], + line: [], + column: [], + length: 0, + }; +} + +export function shallowCloneSourceLocationTable( + originalLocation: SourceLocationTable +): SourceLocationTable { + return { + source: originalLocation.source.slice(), + line: originalLocation.line.slice(), + column: originalLocation.column.slice(), + length: originalLocation.length, + }; +} + export function shallowCloneNativeSymbolTable( nativeSymbols: NativeSymbolTable ): NativeSymbolTable { @@ -338,6 +363,7 @@ export function getEmptySourceTable(): SourceTable { startLine: [], startColumn: [], sourceMapURL: [], + content: [], length: 0, }; } @@ -374,6 +400,7 @@ export function getEmptySharedData(): RawProfileSharedData { nativeSymbols: getEmptyNativeSymbolTable(), sources: getEmptySourceTable(), stringArray: [], + originalLocation: getEmptySourceLocationTable(), }; } diff --git a/src/profile-logic/global-data-collector.ts b/src/profile-logic/global-data-collector.ts index a928534898..ec0957a162 100644 --- a/src/profile-logic/global-data-collector.ts +++ b/src/profile-logic/global-data-collector.ts @@ -10,6 +10,7 @@ import { getEmptyRawStackTable, getEmptyResourceTable, getEmptySourceTable, + getEmptySourceLocationTable, } from './data-structures'; import type { @@ -108,6 +109,7 @@ export class GlobalDataCollector { this._funcTable.source[funcIndex] = source; this._funcTable.lineNumber[funcIndex] = lineNumber; this._funcTable.columnNumber[funcIndex] = columnNumber; + this._funcTable.originalLocation[funcIndex] = null; this._funcKeyToFuncIndex.set(funcKey, funcIndex); } return funcIndex; @@ -144,6 +146,7 @@ export class GlobalDataCollector { this._sources.startLine[index] = startLine; this._sources.startColumn[index] = startColumn; this._sources.sourceMapURL[index] = sourceMapURLIndex; + this._sources.content[index] = null; this._sources.length++; if (id !== null) { @@ -314,6 +317,7 @@ export class GlobalDataCollector { nativeSymbols: this._nativeSymbols, stringArray: this._stringArray, sources: this._sources, + originalLocation: getEmptySourceLocationTable(), }; return { libs: this._libs, shared }; diff --git a/src/profile-logic/import/chrome.ts b/src/profile-logic/import/chrome.ts index b49c570ffc..96add0a137 100644 --- a/src/profile-logic/import/chrome.ts +++ b/src/profile-logic/import/chrome.ts @@ -646,6 +646,7 @@ async function processTracingEvents( lineNumber === undefined ? null : lineNumber; frameTable.column[frameIndex] = columnNumber === undefined ? null : columnNumber; + frameTable.originalLocation[frameIndex] = null; frameTable.length = Math.max(frameTable.length, frameIndex + 1); stackTable.frame.push(frameIndex); diff --git a/src/profile-logic/import/dhat.ts b/src/profile-logic/import/dhat.ts index f5f58d4fed..dc56c06f17 100644 --- a/src/profile-logic/import/dhat.ts +++ b/src/profile-logic/import/dhat.ts @@ -214,6 +214,7 @@ export function attemptToConvertDhat(json: unknown): Profile | null { frameTable.nativeSymbol.push(null); frameTable.inlineDepth.push(0); frameTable.func.push(rootFuncIndex); + frameTable.originalLocation.push(null); const rootFrameIndex = frameTable.length++; stackTable.frame.push(rootFrameIndex); @@ -280,6 +281,7 @@ export function attemptToConvertDhat(json: unknown): Profile | null { frameTable.nativeSymbol.push(null); frameTable.inlineDepth.push(0); frameTable.func.push(funcIndex); + frameTable.originalLocation.push(null); frameTable.length++; } diff --git a/src/profile-logic/import/flame-graph.ts b/src/profile-logic/import/flame-graph.ts index c941f2137b..148a777bd2 100644 --- a/src/profile-logic/import/flame-graph.ts +++ b/src/profile-logic/import/flame-graph.ts @@ -122,6 +122,7 @@ export function convertFlameGraphProfile(profileText: string): Profile { frameTable.innerWindowID.push(null); frameTable.line.push(null); frameTable.column.push(null); + frameTable.originalLocation.push(null); frameTable.length++; frameMap.set(frameString, frameIndex); diff --git a/src/profile-logic/import/simpleperf.ts b/src/profile-logic/import/simpleperf.ts index 152830e49c..ce933b9a7c 100644 --- a/src/profile-logic/import/simpleperf.ts +++ b/src/profile-logic/import/simpleperf.ts @@ -29,6 +29,7 @@ import { getEmptyRawMarkerTable, getEmptyNativeSymbolTable, getEmptySourceTable, + getEmptySourceLocationTable, } from 'firefox-profiler/profile-logic/data-structures'; import { StringTable } from 'firefox-profiler/utils/string-table'; import { ensureExists } from 'firefox-profiler/utils/types'; @@ -134,6 +135,7 @@ class FirefoxFuncTable { this.funcTable.source.push(null); this.funcTable.lineNumber.push(null); this.funcTable.columnNumber.push(null); + this.funcTable.originalLocation.push(null); funcIndex = this.funcTable.length++; this.funcMap.set(mapKey, funcIndex); @@ -174,6 +176,7 @@ class FirefoxFrameTable { this.frameTable.innerWindowID.push(null); this.frameTable.line.push(null); this.frameTable.column.push(null); + this.frameTable.originalLocation.push(null); frameIndex = this.frameTable.length++; this.frameMap.set(mapKey, frameIndex); @@ -233,6 +236,7 @@ class FirefoxSharedData { nativeSymbols: getEmptyNativeSymbolTable(), sources: getEmptySourceTable(), stringArray: this.stringArray, + originalLocation: getEmptySourceLocationTable(), }; } } diff --git a/src/profile-logic/js-tracer.ts b/src/profile-logic/js-tracer.ts index 17af033347..8993ddfbca 100644 --- a/src/profile-logic/js-tracer.ts +++ b/src/profile-logic/js-tracer.ts @@ -571,6 +571,7 @@ export function convertJsTracerToThreadWithoutSamples( funcTable.source.push(null); funcTable.lineNumber.push(null); funcTable.columnNumber.push(null); + funcTable.originalLocation.push(null); funcMap.set(eventStringIndex, funcIndex); } else { @@ -601,6 +602,7 @@ export function convertJsTracerToThreadWithoutSamples( frameTable.innerWindowID.push(0); frameTable.line.push(line); frameTable.column.push(column); + frameTable.originalLocation.push(null); // Each event gets a stack table entry. const stackIndex = stackTable.length++; diff --git a/src/profile-logic/line-timings.ts b/src/profile-logic/line-timings.ts index 8001082d84..aa9993b3ea 100644 --- a/src/profile-logic/line-timings.ts +++ b/src/profile-logic/line-timings.ts @@ -12,6 +12,7 @@ import type { LineNumber, IndexIntoSourceTable, IndexIntoLineSetTable, + SourceLocationTable, } from 'firefox-profiler/types'; import { SetCollectionBuilder } from 'firefox-profiler/utils/set-collection'; @@ -47,7 +48,8 @@ export function getStackLineInfo( stackTable: StackTable, frameTable: FrameTable, funcTable: FuncTable, - sourceViewSourceIndex: IndexIntoSourceTable + sourceViewSourceIndex: IndexIntoSourceTable, + originalLocation: SourceLocationTable ): StackLineInfo { const builder = new SetCollectionBuilder(); const stackIndexToLineSetIndex = new Int32Array(stackTable.length); @@ -59,14 +61,37 @@ export function getStackLineInfo( const frame = stackTable.frame[stackIndex]; const func = frameTable.func[frame]; - const sourceIndexOfThisStack = funcTable.source[func]; + + // Inlined source resolution from getOriginalPositionForFrame. We avoid + // its per-stack {source, line, column} allocation, skip resolving the + // column entirely, and defer the line lookup until we know the source + // matches. + const frameOriginalLocationIdx = frameTable.originalLocation[frame]; + const funcOriginalLocationIdx = funcTable.originalLocation[func]; + let sourceIndexOfThisStack; + if (frameOriginalLocationIdx !== null) { + sourceIndexOfThisStack = + originalLocation.source[frameOriginalLocationIdx]; + } else if (funcOriginalLocationIdx !== null) { + sourceIndexOfThisStack = originalLocation.source[funcOriginalLocationIdx]; + } else { + sourceIndexOfThisStack = funcTable.source[func]; + } + const matchesSource = sourceIndexOfThisStack === sourceViewSourceIndex; if (prefixLineSet === -1 && !matchesSource) { stackIndexToLineSetIndex[stackIndex] = -1; } else { - const selfLineOrNull = matchesSource - ? (frameTable.line[frame] ?? funcTable.lineNumber[func]) - : null; + let selfLineOrNull: number | null = null; + if (matchesSource) { + if (frameOriginalLocationIdx !== null) { + selfLineOrNull = originalLocation.line[frameOriginalLocationIdx]; + } else if (funcOriginalLocationIdx !== null) { + selfLineOrNull = originalLocation.line[funcOriginalLocationIdx]; + } else { + selfLineOrNull = frameTable.line[frame] ?? funcTable.lineNumber[func]; + } + } stackIndexToLineSetIndex[stackIndex] = builder.extend( prefixLineSet !== -1 ? prefixLineSet : null, @@ -169,7 +194,9 @@ export function getTotalLineTimingsForCallNode( samples: SamplesLikeTable, callNodeFramePerStack: Int32Array, frameTable: FrameTable, - funcLine: LineNumber | null + funcTable: FuncTable, + funcLine: LineNumber | null, + originalLocation: SourceLocationTable ): Map { const totalPerLine = new Map(); for (let sampleIndex = 0; sampleIndex < samples.length; sampleIndex++) { @@ -179,11 +206,27 @@ export function getTotalLineTimingsForCallNode( } const callNodeFrame = callNodeFramePerStack[stack]; if (callNodeFrame === -1) { - // This sample does not contribute to the call node's total. Ignore. continue; } - const frameLine = frameTable.line[callNodeFrame]; + // Resolve in the same coordinate space as the source view: prefer the + // source-mapped line, otherwise the frame's compiled line, otherwise the + // func's compiled line. Inlined from getOriginalPositionForFrame to avoid + // the per-sample object allocation. + const funcIndex = frameTable.func[callNodeFrame]; + const frameOriginalLocationIdx = frameTable.originalLocation[callNodeFrame]; + let frameLine: number | null; + if (frameOriginalLocationIdx !== null) { + frameLine = originalLocation.line[frameOriginalLocationIdx]; + } else { + const funcOriginalLocationIdx = funcTable.originalLocation[funcIndex]; + if (funcOriginalLocationIdx !== null) { + frameLine = originalLocation.line[funcOriginalLocationIdx]; + } else { + frameLine = + frameTable.line[callNodeFrame] ?? funcTable.lineNumber[funcIndex]; + } + } const line = frameLine !== null ? frameLine : funcLine; if (line === null) { continue; diff --git a/src/profile-logic/merge-compare.ts b/src/profile-logic/merge-compare.ts index 269fc4fc46..e8a6e9558d 100644 --- a/src/profile-logic/merge-compare.ts +++ b/src/profile-logic/merge-compare.ts @@ -48,6 +48,7 @@ import type { IndexIntoStackTable, IndexIntoStringTable, IndexIntoSourceTable, + IndexIntoSourceLocationTable, FuncTable, FrameTable, Lib, @@ -56,6 +57,7 @@ import type { RawSamplesTable, RawStackTable, SourceTable, + SourceLocationTable, UrlState, ImplementationFilter, TransformStacksPerThread, @@ -374,6 +376,7 @@ type TranslationMapForStacks = Int32Array; type TranslationMapForLibs = Int32Array; type TranslationMapForStrings = Int32Array; type TranslationMapForSources = Int32Array; +type TranslationMapForOriginalLocation = Int32Array; /** * Merges several categories lists into one, resolving duplicates if necessary. @@ -442,6 +445,10 @@ export function mergeSharedData(profiles: Profile[]): { profiles.map((profile) => profile.shared.sources ?? null), translationMapsForStrings ); + const { + originalLocation: newOriginalLocation, + translationMaps: translationMapsForOriginalLocation, + } = mergeSourceLocationTables(profiles, translationMapsForSources); const { resourceTable: newResourceTable, translationMaps: translationMapsForResources, @@ -463,6 +470,7 @@ export function mergeSharedData(profiles: Profile[]): { profiles, translationMapsForResources, translationMapsForSources, + translationMapsForOriginalLocation, translationMapsForStrings ); const { @@ -472,6 +480,7 @@ export function mergeSharedData(profiles: Profile[]): { profiles, translationMapsForFuncs, translationMapsForNativeSymbols, + translationMapsForOriginalLocation, translationMapsForCategories ); const { @@ -487,6 +496,7 @@ export function mergeSharedData(profiles: Profile[]): { resourceTable: newResourceTable, stringArray: newStringArray, sources: newSources, + originalLocation: newOriginalLocation, }; const translationMapsPerProfile = profiles.map((profile, i) => { @@ -581,6 +591,7 @@ function mergeSources( newSources.startLine[insertedSourceIndex] = sources.startLine[i]; newSources.startColumn[insertedSourceIndex] = sources.startColumn[i]; newSources.sourceMapURL[insertedSourceIndex] = newSourceMapURLIndex; + newSources.content[insertedSourceIndex] = sources.content[i]; newSources.length++; mapOfInsertedSources.set(sourceKey, insertedSourceIndex); } @@ -597,6 +608,52 @@ function mergeSources( }; } +/** + * Combines the originalLocation tables from multiple profiles. Each row is copied + * verbatim except `source`, which is translated through the source + * translation map. Rows are not deduplicated across profiles. This is leaf + * data with no downstream constraint that would benefit from a stable key. + */ +function mergeSourceLocationTables( + profiles: ReadonlyArray, + translationMapsForSources: TranslationMapForSources[] +): { + originalLocation: SourceLocationTable; + translationMaps: TranslationMapForOriginalLocation[]; +} { + const newOriginalLocation: SourceLocationTable = { + source: [], + line: [], + column: [], + length: 0, + }; + + const translationMaps = profiles.map((profile, profileIndex) => { + const originalLocation = profile.shared.originalLocation; + const sourceTranslation = translationMapsForSources[profileIndex]; + const oldOriginalLocationToNewPlusOne = new Int32Array( + originalLocation.length + ); + + for (let i = 0; i < originalLocation.length; i++) { + const source = originalLocation.source[i]; + const newOriginalSource = sourceTranslation[source] - 1; + + const newIndex = newOriginalLocation.length; + newOriginalLocation.source.push(newOriginalSource); + newOriginalLocation.line.push(originalLocation.line[i]); + newOriginalLocation.column.push(originalLocation.column[i]); + newOriginalLocation.length++; + + oldOriginalLocationToNewPlusOne[i] = newIndex + 1; + } + + return oldOriginalLocationToNewPlusOne; + }); + + return { originalLocation: newOriginalLocation, translationMaps }; +} + function adjustStringIndexes( stringIndexes: ReadonlyArray, oldStringToNewStringPlusOne: TranslationMapForStrings @@ -713,6 +770,15 @@ function _mapNullableSource( : null; } +function _mapNullableOriginalLocation( + originalLocationIndex: IndexIntoSourceLocationTable | null, + oldOriginalLocationToNewPlusOne: TranslationMapForOriginalLocation +): IndexIntoSourceLocationTable | null { + return originalLocationIndex !== null + ? oldOriginalLocationToNewPlusOne[originalLocationIndex] - 1 + : null; +} + function _mapFuncResource( resourceIndex: IndexIntoResourceTable | -1, oldResourceToNewResourcePlusOne: TranslationMapForResources @@ -900,6 +966,7 @@ function mergeFuncTables( profiles: ReadonlyArray, translationMapsForResources: TranslationMapForResources[], translationMapsForSources: TranslationMapForSources[], + translationMapsForOriginalLocation: TranslationMapForOriginalLocation[], translationMapsForStrings: TranslationMapForStrings[] ): { funcTable: FuncTable; translationMaps: TranslationMapForFuncs[] } { const mapOfInsertedFuncs = new Map(); @@ -912,6 +979,8 @@ function mergeFuncTables( translationMapsForResources[profileIndex]; const oldStringToNewStringPlusOne = translationMapsForStrings[profileIndex]; const oldSourceToNewSourcePlusOne = translationMapsForSources[profileIndex]; + const oldOriginalLocationToNewPlusOne = + translationMapsForOriginalLocation[profileIndex]; const oldFuncToNewFuncPlusOne = new Int32Array(funcTable.length); for (let i = 0; i < funcTable.length; i++) { @@ -953,6 +1022,12 @@ function mergeFuncTables( newFuncTable.source.push(sourceIndex); newFuncTable.lineNumber.push(lineNumber); newFuncTable.columnNumber.push(funcTable.columnNumber[i]); + newFuncTable.originalLocation.push( + _mapNullableOriginalLocation( + funcTable.originalLocation[i], + oldOriginalLocationToNewPlusOne + ) + ); newFuncTable.length++; } @@ -974,6 +1049,7 @@ function mergeFrameTables( profiles: ReadonlyArray, translationMapsForFuncs: TranslationMapForFuncs[], translationMapsForNativeSymbols: TranslationMapForNativeSymbols[], + translationMapsForOriginalLocation: TranslationMapForOriginalLocation[], translationMapsForCategories: TranslationMapForCategories[] ): { frameTable: FrameTable; translationMaps: TranslationMapForFrames[] } { const translationMaps: TranslationMapForFrames[] = []; @@ -984,6 +1060,8 @@ function mergeFrameTables( const oldFuncToNewFuncPlusOne = translationMapsForFuncs[profileIndex]; const oldNativeSymbolToNewNativeSymbolPlusOne = translationMapsForNativeSymbols[profileIndex]; + const oldOriginalLocationToNewPlusOne = + translationMapsForOriginalLocation[profileIndex]; const oldCategoryToNewCategoryPlusOne = translationMapsForCategories[profileIndex]; const oldFrameToNewFramePlusOne = new Int32Array(frameTable.length); @@ -1010,6 +1088,12 @@ function mergeFrameTables( newFrameTable.innerWindowID.push(frameTable.innerWindowID[i]); newFrameTable.line.push(frameTable.line[i]); newFrameTable.column.push(frameTable.column[i]); + newFrameTable.originalLocation.push( + _mapNullableOriginalLocation( + frameTable.originalLocation[i], + oldOriginalLocationToNewPlusOne + ) + ); oldFrameToNewFramePlusOne[i] = newFrameTable.length + 1; newFrameTable.length++; diff --git a/src/profile-logic/nonymous.ts b/src/profile-logic/nonymous.ts new file mode 100644 index 0000000000..9dcec52434 --- /dev/null +++ b/src/profile-logic/nonymous.ts @@ -0,0 +1,64 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Parse and serialize Nonymous function names as produced by SpiderMonkey's + * NameFunctions pass: + * https://searchfox.org/firefox-main/rev/1f7030c8de8f2b349c7d91d7b5a3253c109a1cc1/js/src/frontend/NameFunctions.cpp + * + * Nonymous algorithm: https://johnjbarton.github.io/nonymous/index.html + * + * The format uses '/' as a scope separator and '<' as a "contributes-to" + * marker for anonymous functions. Examples: + * "foo" - named function assigned to `foo` + * "foo<" - function contributing to `foo` (e.g. passed as arg) + * "obj.method" - method on an object + * "outer/inner" - `inner` defined inside `outer` + * "outer/inner<" - anonymous inside `outer`, contributing to `inner` + * "outer/<" - anonymous inside `outer`, no assignment context + * "i2 { + // Add the originalLocation table on profile.shared and the matching + // originalLocation column on funcTable and frameTable. Also add the content + // column on the sources table. + const { funcTable, frameTable, sources } = profile.shared; + funcTable.originalLocation = new Array(funcTable.length).fill(null); + frameTable.originalLocation = new Array(frameTable.length).fill(null); + profile.shared.originalLocation = { + source: [], + line: [], + column: [], + length: 0, + }; + sources.content = new Array(sources.length).fill(null); + }, // If you add a new upgrader here, please document the change in // `docs-developer/CHANGELOG-formats.md`. }; diff --git a/src/profile-logic/profile-compacting.ts b/src/profile-logic/profile-compacting.ts index e575383286..444bfc0f11 100644 --- a/src/profile-logic/profile-compacting.ts +++ b/src/profile-logic/profile-compacting.ts @@ -18,6 +18,7 @@ import type { NativeSymbolTable, Lib, SourceTable, + SourceLocationTable, } from 'firefox-profiler/types'; import { assertExhaustiveCheck, @@ -104,6 +105,7 @@ type TableCompactionStates = { resourceTable: TableCompactionState; nativeSymbols: TableCompactionState; sources: TableCompactionState; + originalLocation: TableCompactionState; stringArray: TableCompactionState; libs: TableCompactionState; }; @@ -135,6 +137,7 @@ export function computeCompactedProfile( resourceTable: new TableCompactionState(shared.resourceTable.length), nativeSymbols: new TableCompactionState(shared.nativeSymbols.length), sources: new TableCompactionState(shared.sources.length), + originalLocation: new TableCompactionState(shared.originalLocation.length), libs: new TableCompactionState(profile.libs.length), stringArray: new TableCompactionState(shared.stringArray.length), }; @@ -153,6 +156,7 @@ export function computeCompactedProfile( innerWindowID: ColDesc.noRef(), line: ColDesc.noRef(), column: ColDesc.noRef(), + originalLocation: ColDesc.indexRefOrNull(tcs.originalLocation), }; const funcTableDesc: TableDescription = { name: ColDesc.indexRef(tcs.stringArray), @@ -162,6 +166,12 @@ export function computeCompactedProfile( source: ColDesc.indexRefOrNull(tcs.sources), lineNumber: ColDesc.noRef(), columnNumber: ColDesc.noRef(), + originalLocation: ColDesc.indexRefOrNull(tcs.originalLocation), + }; + const originalLocationDesc: TableDescription = { + source: ColDesc.indexRef(tcs.sources), + line: ColDesc.noRef(), + column: ColDesc.noRef(), }; const resourceTableDesc: TableDescription = { name: ColDesc.indexRef(tcs.stringArray), @@ -181,6 +191,7 @@ export function computeCompactedProfile( startLine: ColDesc.noRef(), startColumn: ColDesc.noRef(), sourceMapURL: ColDesc.indexRefOrNull(tcs.stringArray), + content: ColDesc.noRef(), }; // Step 1: Gather all references. @@ -215,6 +226,11 @@ export function computeCompactedProfile( tcs.resourceTable, resourceTableDesc ); + _markTableAndComputeTranslation( + shared.originalLocation, + tcs.originalLocation, + originalLocationDesc + ); _markTableAndComputeTranslation( shared.nativeSymbols, tcs.nativeSymbols, @@ -250,6 +266,11 @@ export function computeCompactedProfile( nativeSymbolsDesc ), sources: _compactTable(shared.sources, tcs.sources, sourcesDesc), + originalLocation: _compactTable( + shared.originalLocation, + tcs.originalLocation, + originalLocationDesc + ), stringArray: _createCompactedStringArray(shared.stringArray, tcs), }; diff --git a/src/profile-logic/profile-data.ts b/src/profile-logic/profile-data.ts index 89ed4bd8f6..8c8f963f04 100644 --- a/src/profile-logic/profile-data.ts +++ b/src/profile-logic/profile-data.ts @@ -101,6 +101,7 @@ import type { IndexIntoSourceTable, TransformOutput, SampleCategoriesAndSubcategories, + SourceLocationTable, } from 'firefox-profiler/types'; import { SelectedState, ResourceType } from 'firefox-profiler/types'; import type { CallNodeInfo, SuffixOrderIndex } from './call-node-info'; @@ -2784,7 +2785,8 @@ export function createThreadFromDerivedTables( resourceTable: ResourceTable, stringTable: StringTable, sources: SourceTable, - tracedValuesBuffer: ArrayBuffer | undefined + tracedValuesBuffer: ArrayBuffer | undefined, + originalLocation: SourceLocationTable ): Thread { const { processType, @@ -2844,6 +2846,7 @@ export function createThreadFromDerivedTables( stringTable, sources, tracedValuesBuffer, + originalLocation, }; return thread; } @@ -3329,6 +3332,70 @@ function _shouldShowBothOriginAndFileName( return true; } +/** + * Resolves the original (source-mapped) position for a frame's execution point, + * with a 3-tier fallback that always produces a self-consistent + * (source, line, column) triple where all three come from the same file. + * + * Tier 1: frame's own source-map entry (when present). Handles inlined code + * where the frame's original file may differ from the func's. + * Tier 2: func's source-map entry (same condition). + * Tier 3: compiled position: funcTable.source[funcIndex] plus line/column + * from the frame (if frameIndex !== null) else from the func declaration. + * + * Pass frameIndex = null for call sites that only have a funcIndex (e.g., + * tooltip / call-node-level lookups, where the call node represents the func + * rather than a specific frame). + */ +export function getOriginalPositionForFrame( + frameIndex: IndexIntoFrameTable | null, + funcIndex: IndexIntoFuncTable, + frameTable: FrameTable, + funcTable: FuncTable, + originalLocation: SourceLocationTable | null +): { + source: IndexIntoSourceTable | null; + line: number | null; + column: number | null; +} { + if (originalLocation !== null && frameIndex !== null) { + const frameOriginalLocationIdx = frameTable.originalLocation[frameIndex]; + if (frameOriginalLocationIdx !== null) { + return { + source: originalLocation.source[frameOriginalLocationIdx], + line: originalLocation.line[frameOriginalLocationIdx], + column: originalLocation.column[frameOriginalLocationIdx], + }; + } + } + + if (originalLocation !== null) { + const funcOriginalLocationIdx = funcTable.originalLocation[funcIndex]; + if (funcOriginalLocationIdx !== null) { + return { + source: originalLocation.source[funcOriginalLocationIdx], + line: originalLocation.line[funcOriginalLocationIdx], + column: originalLocation.column[funcOriginalLocationIdx], + }; + } + } + + if (frameIndex !== null) { + return { + source: funcTable.source[funcIndex], + line: frameTable.line[frameIndex] ?? funcTable.lineNumber[funcIndex], + column: + frameTable.column[frameIndex] ?? funcTable.columnNumber[funcIndex], + }; + } + + return { + source: funcTable.source[funcIndex], + line: funcTable.lineNumber[funcIndex], + column: funcTable.columnNumber[funcIndex], + }; +} + /** * This function returns the source origin for a function. This can be: * - a filename (javascript or object file or c++ source file) @@ -3336,12 +3403,13 @@ function _shouldShowBothOriginAndFileName( */ export function getOriginAnnotationForFunc( funcIndex: IndexIntoFuncTable, + frameIndex: IndexIntoFrameTable | null, + frameTable: FrameTable, funcTable: FuncTable, resourceTable: ResourceTable, stringTable: StringTable, sources: SourceTable, - frameLineNumber: number | null = null, - frameColumnNumber: number | null = null + originalLocation: SourceLocationTable | null = null ): string { let resourceType = null; let origin = null; @@ -3352,7 +3420,18 @@ export function getOriginAnnotationForFunc( origin = stringTable.getString(resourceNameIndex); } - const sourceIndex = funcTable.source[funcIndex]; + const { + source: sourceIndex, + line: lineNumber, + column: columnNumber, + } = getOriginalPositionForFrame( + frameIndex, + funcIndex, + frameTable, + funcTable, + originalLocation + ); + let fileName; if (sourceIndex !== null) { const urlIndex = sources.filename[sourceIndex]; @@ -3366,19 +3445,10 @@ export function getOriginAnnotationForFunc( // strip it down to just the actual path. fileName = parseFileNameFromSymbolication(fileName).path; - if (frameLineNumber !== null) { - fileName += ':' + frameLineNumber; - if (frameColumnNumber !== null) { - fileName += ':' + frameColumnNumber; - } - } else { - const lineNumber = funcTable.lineNumber[funcIndex]; - if (lineNumber !== null) { - fileName += ':' + lineNumber; - const columnNumber = funcTable.columnNumber[funcIndex]; - if (columnNumber !== null) { - fileName += ':' + columnNumber; - } + if (lineNumber !== null) { + fileName += ':' + lineNumber; + if (columnNumber !== null) { + fileName += ':' + columnNumber; } } } @@ -3440,6 +3510,7 @@ export function reserveFunctionsForCollapsedResources( funcTable.source.push(null); funcTable.lineNumber.push(null); funcTable.columnNumber.push(null); + funcTable.originalLocation.push(null); funcTable.length++; reservedFunctionsForResources.set(resourceIndex, funcIndex); } @@ -3989,6 +4060,70 @@ export function _gatherSingleThreadStackReferences( } } +/** + * Collect all source table indices referenced by the given threads. + * Walks samples, jsAllocations, and nativeAllocations, following stack prefix + * chains. Includes both compiled sources (funcTable.source) and + * post-symbolication original sources (via originalLocation). + */ +export function collectSourceIndicesFromThreads( + threadIndexes: Iterable, + threads: RawThread[], + shared: RawProfileSharedData +): Set { + const { stackTable, frameTable, funcTable, originalLocation } = shared; + const sourceIndices = new Set(); + const visitedStacks = makeBitSet(stackTable.length); + + const addStackChain = (stackIndex: IndexIntoStackTable | null) => { + let current = stackIndex; + while (current !== null && !checkBit(visitedStacks, current)) { + setBit(visitedStacks, current); + const frameIndex = stackTable.frame[current]; + const funcIndex = frameTable.func[frameIndex]; + + const compiledSource = funcTable.source[funcIndex]; + if (compiledSource !== null) { + sourceIndices.add(compiledSource); + } + + const frameOriginalLocationIdx = frameTable.originalLocation[frameIndex]; + if (frameOriginalLocationIdx !== null) { + sourceIndices.add(originalLocation.source[frameOriginalLocationIdx]); + } + + const funcOriginalLocationIdx = funcTable.originalLocation[funcIndex]; + if (funcOriginalLocationIdx !== null) { + sourceIndices.add(originalLocation.source[funcOriginalLocationIdx]); + } + + current = stackTable.prefix[current]; + } + }; + + for (const threadIndex of threadIndexes) { + if (threadIndex >= threads.length) { + continue; + } + const thread = threads[threadIndex]; + for (const s of thread.samples.stack) { + addStackChain(s); + } + if (thread.jsAllocations !== undefined) { + for (const s of thread.jsAllocations.stack) { + addStackChain(s); + } + } + if (thread.nativeAllocations !== undefined) { + for (const s of thread.nativeAllocations.stack) { + addStackChain(s); + } + } + } + + return sourceIndices; +} + /** * Creates a new thread with modified frame and stack tables for "nudged" return addresses: * All return addresses are moved backwards by one byte, to point into the "call" diff --git a/src/profile-logic/sanitize.ts b/src/profile-logic/sanitize.ts index dec2160f55..a69bec579f 100644 --- a/src/profile-logic/sanitize.ts +++ b/src/profile-logic/sanitize.ts @@ -126,6 +126,14 @@ export function sanitizePII( stringArray, }; + // FIXME: Currently we always sanitize the source contents while publishing. + // But we should have a sharing option in the publish panel to be able to + // include sources. + newShared.sources = { + ...newShared.sources, + content: Array(newShared.sources.content.length).fill(null), + }; + let stackFlags: Uint8Array | null = null; if (windowIdFromPrivateBrowsing.size > 0) { diff --git a/src/profile-logic/source-map-scope-tree.ts b/src/profile-logic/source-map-scope-tree.ts new file mode 100644 index 0000000000..f76d9bd2b0 --- /dev/null +++ b/src/profile-logic/source-map-scope-tree.ts @@ -0,0 +1,730 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Scope tree construction from compiled JavaScript using Lezer. + * + * ## Why we need this + * + * Source maps don't expose function scopes. They're a flat list of + * generated -> original token mappings, some of which carry a `name`. + * There's no "this range belongs to function F" information, and for many + * functions there's no usable name at any single token to begin with: + * + * - For anonymous functions (`() => {}`, `function() {}`), there is no + * identifier in either source for the mapping to attach a name to. + * - For functions inferred from context (`var x = () => {}`, + * `obj.foo = function() {}`, `obj[key] = fn`), the name has to be + * recovered from surrounding AST nodes, not from the function token. + * - Even for self-named functions, the sample's position lands somewhere + * in the body, not on the identifier, so we still need to know where + * the identifier sits in order to probe the source map for it. + * + * So we reconstruct that information by parsing the compiled JS + * ourselves. The shapes we recognize and the fields we record for each + * are catalogued below, and the resolver in source-map-symbolication.ts + * consumes them. + * + * The TC39 source-map scopes proposal (ecma426, stage 3) would encode + * this scope information directly in the source map and let us skip the + * CST reconstruction entirely. Once it ships and toolchains start emitting + * it, this file can become a fallback for source maps that lack a scopes + * section. See https://github.com/tc39/ecma426/blob/main/proposals/scopes.md. + * + * ## What this file produces + * + * Parses the compiled source with @lezer/javascript, walks the resulting + * concrete syntax tree (CST), and builds a tree of function scopes. A CST + * is a syntax tree that preserves every token from the source, including + * punctuation, parentheses, and whitespace positions, unlike an abstract + * syntax tree (AST), which typically drops them. We need the CST because + * the whole point of this pass is to compute character offsets in the + * compiled source (for source-map probes), so the exact positions of + * tokens like the opening `(` or the `=>` arrow matter. + * + * Each scope carries `nameMappingLocations` (character offsets to probe + * via exact source-map lookups) plus, for inferred scopes, extra fields + * the resolver in source-map-symbolication.ts uses to recover the + * original name. + * + * ## Shapes recognized + * + * Self-named (the function/method has its own identifier): + * + * function foo() {} // astName = "foo" + * const x = function bar() {} // bar's astName = "bar" + * class C { foo() {} } // astName = "foo" + * { foo: () => {} } // astName = "foo" + * + * Inferred from a direct assignment target: + * + * var x = () => {} // probe at `x`, lhsText = "x" + * obj.foo = function() {} // probe at `foo`, lhsText = "obj.foo" + * + * Inferred through a transparent wrapper (sets `contributesTo`, which the + * resolver maps to the Nonymous `<` suffix): + * + * var observer = new IntersectionObserver(() => {}) // contributesTo = true + * var x = wrap(() => {}) // contributesTo = true + * + * Computed-member LHS (sets `computedKeyLoc` so the resolver can compose + * `${receiver}[${key}]` from two independent probes): + * + * obj[key] = function() {} + * // identifierLoc at `obj`, computedKeyLoc at `key`, lhsText = "obj[key]" + * + * Every inferred scope also records `lhsText`, the verbatim source slice of + * the assignment-target LHS. The resolver feeds it through the original + * parse of the source (from `sourcesContent`), so the un-minified member + * chain survives even when the minifier rewrote the LHS. + */ + +import { parser as lezerJsParser } from '@lezer/javascript'; + +import { bisectionRightByKey } from '../utils/bisect'; + +// Derive SyntaxNode from the parser to avoid version conflicts with nested +// @lezer/common copies in node_modules (they have incompatible private fields). +type SyntaxNode = ReturnType['topNode']; + +export type FunctionScope = { + // Character offsets in the compiled source, inclusive start / exclusive end. + start: number; + end: number; + // Ordered list of character offsets to probe with an exact source-map lookup. + // The first probe that yields a named entry wins. + nameMappingLocations: number[]; + // Fallback name derived from the AST (the compiled identifier, e.g. `foo`). + // null for anonymous functions and arrow functions. + astName: string | null; + kind: 'function' | 'arrow'; + // True when the function's inferred name came through a wrapping + // `call(...)` / `new C(...)`, i.e. the function "contributes to" the + // assignment target rather than being it. Emits the Nonymous `<` suffix + // (e.g. `outer/observer<` for `var observer = new C(() => {})`). + contributesTo: boolean; + // Set for scopes inferred from a computed-member assignment `obj[key] = fn`: + // the char offset of the bracket-key expression. Used to compose a compound + // `receiver[key]` name from two independent source-map probes, matching + // SpiderMonkey's output where the function's name is the literal source + // text of the LHS (e.g. `obj[key]`). Only consulted as a fallback when the + // original source isn't available in `sourcesContent`. Otherwise `lhsText` + // wins. + computedKeyLoc: number | null; + // Verbatim source text of the assignment-target LHS for inferred scopes. + // For a scope built from this scope-tree's source, that's just the slice + // of `text` between the LHS node's `from`/`to`. The resolver uses it via + // the *original* parse of the source (read from the source map's + // `sourcesContent`), so the name is whatever the developer wrote + // (`Watcher.prototype.run`, `this.eventPool_.createObject`, `obj[key]`, + // etc.), independent of how the minifier rewrote the LHS. + lhsText: string | null; + children: FunctionScope[]; +}; + +// --------------------------------------------------------------------------- +// Mapping-location helpers +// --------------------------------------------------------------------------- + +function _mappingLocationsForFunction( + node: SyntaxNode, + text: string +): number[] { + const locations: number[] = []; + const nameNode = node.getChild('VariableDefinition'); + if (nameNode) { + locations.push(nameNode.from); + } + const paramList = node.getChild('ParamList'); + if (paramList && text[paramList.from] === '(') { + locations.push(paramList.from); + } + return locations; +} + +function _mappingLocationsForArrow(node: SyntaxNode, text: string): number[] { + const locations: number[] = []; + + const paramList = node.getChild('ParamList'); + if (paramList && text[paramList.from] === '(') { + locations.push(paramList.from); + } + + const arrow = node.getChild('Arrow'); + if (arrow) { + locations.push(arrow.from); + } + + return locations; +} + +function _keyMappingLocation(node: SyntaxNode): number[] { + const key = + node.getChild('PropertyDefinition') ?? + node.getChild('PrivatePropertyDefinition'); + return key ? [key.from] : []; +} + +function _astNameForKey(node: SyntaxNode, text: string): string | null { + const key = + node.getChild('PropertyDefinition') ?? + node.getChild('PrivatePropertyDefinition'); + if (!key) { + return null; + } + // PrivatePropertyDefinition spans `#name` including the `#`. + return text.slice(key.from, key.to); +} + +/** + * Describes the assignment target of an AssignmentExpression / VariableDeclarator + * for the purpose of inferring a function name from it. + * + * `identifierLoc` is the char offset of an identifier to probe in the source + * map for the original name (the variable, property name, or, for computed + * member access, the receiver expression). + * + * `computedKeyLoc` is set for `obj[key] = fn`: the offset of the bracket-key + * expression. The resolver probes it separately and composes + * `${receiver}[${key}]` so the assigned-to expression survives symbolication. + * + * `lhsText` is the verbatim source slice of the LHS (e.g. `Foo.prototype.bar`, + * `obj[key]`, `foo`). The resolver prefers it when looking up the function in + * the original source. + */ +type LhsContext = { + identifierLoc: number; + computedKeyLoc: number | null; + lhsText: string | null; +}; + +function _lhsContextFromAssignment( + node: SyntaxNode, + text: string +): LhsContext | null { + const lhs = node.firstChild; + if (!lhs) { + return null; + } + const lhsText = text.slice(lhs.from, lhs.to); + if (lhs.name === 'VariableName') { + return { identifierLoc: lhs.from, computedKeyLoc: null, lhsText }; + } + if (lhs.name === 'MemberExpression') { + const prop = lhs.getChild('PropertyName'); + if (prop) { + return { identifierLoc: prop.from, computedKeyLoc: null, lhsText }; + } + // Computed access `receiver[key]`. The receiver's leading identifier is + // used for the main probe, and the key expression for the compound-name + // probe (so we can compose `${receiver}[${key}]` from two probes). + const keyLoc = _computedMemberKeyLoc(lhs); + if (keyLoc !== null) { + return { identifierLoc: lhs.from, computedKeyLoc: keyLoc, lhsText }; + } + } + return null; +} + +/** + * For a MemberExpression with computed access (`receiver[keyExpr]`), + * return the char offset of the key expression. Returns null for dotted + * accesses or unexpected shapes. + */ +function _computedMemberKeyLoc(memberExpr: SyntaxNode): number | null { + let pastBracket = false; + for (let c = memberExpr.firstChild; c; c = c.nextSibling) { + if (c.name === '[') { + pastBracket = true; + continue; + } + if (c.name === ']') { + return null; + } + if (pastBracket) { + return c.from; + } + } + return null; +} + +/** + * Create a FunctionScope for an anonymous (or arrow) function node whose name + * must be inferred from surrounding AST context (VariableDeclarator, assignment + * target, wrap-pattern call argument). + * + * `lhs.identifierLoc` is appended LAST to `nameMappingLocations` so the source + * map is queried for the original identifier name only after all of the + * function's own probes have failed. Using it last prevents a broad mapping at + * the identifier from overriding a more precise name found at the function's + * paren/arrow position. + * + * `contributesTo` reflects whether the inference passed through a wrapping + * call/`new` (i.e. the function contributes to the target rather than being + * it). It maps directly to the Nonymous `<` suffix. + * + * `astName` is intentionally left null. If the identifier probe also fails we + * have no meaningful fallback name. The compiled variable name is a minified + * identifier and using it would produce incorrect names in the profiler. In + * that case the function keeps its Gecko-assigned name unchanged. + */ +function _pushInferredScope( + funcNode: SyntaxNode, + text: string, + parentChildren: FunctionScope[], + lhs: LhsContext, + contributesTo: boolean +): void { + const funcLocations = + funcNode.name === 'ArrowFunction' + ? _mappingLocationsForArrow(funcNode, text) + : _mappingLocationsForFunction(funcNode, text); + const scope: FunctionScope = { + start: funcNode.from, + end: funcNode.to, + nameMappingLocations: [...funcLocations, lhs.identifierLoc], + astName: null, + kind: funcNode.name === 'ArrowFunction' ? 'arrow' : 'function', + contributesTo, + computedKeyLoc: lhs.computedKeyLoc, + lhsText: lhs.lhsText, + children: [], + }; + parentChildren.push(scope); + _walkChildren(funcNode, text, scope.children); +} + +/** + * Process the RHS of an assignment-like construct (a VariableDeclarator's + * init or an AssignmentExpression's RHS), inferring `identifierLoc` for any + * anonymous function / arrow argument reached through "transparent" wrapping + * expressions. + * + * In the examples below, `() => {}` stands in for any anonymous function or + * arrow function. + * + * Recognised wrappers: + * - ParenthesizedExpression - `(() => {})` + * - CallExpression - `wrap(() => {})` / `wrap(f1, f2)` + * - NewExpression - `new Class(() => {})` + * + * `throughWrapper` tracks whether the traversal has descended through a + * Call/New: that's what distinguishes "fn IS the target" (`var x = () => {}`, + * `obj.foo = () => {}`, `obj[key] = () => {}`) from "fn CONTRIBUTES TO the + * target" (`var x = wrap(() => {})`, `var x = new C(() => {})`). It maps + * directly to the Nonymous `<` suffix on the resulting scope. + * + * Other nested function literals (e.g. inside the wrapper's body or in + * non-argument positions) are processed normally with no inference. + */ +function _processInitExpr( + initNode: SyntaxNode, + text: string, + parentChildren: FunctionScope[], + lhs: LhsContext, + throughWrapper: boolean +): void { + switch (initNode.name) { + case 'ArrowFunction': + _pushInferredScope(initNode, text, parentChildren, lhs, throughWrapper); + return; + case 'FunctionExpression': { + // Only infer a name for truly anonymous functions. Named function + // expressions (`const foo = function bar() {}`) keep their `bar` name. + const isAnon = initNode.getChild('VariableDefinition') === null; + if (isAnon) { + _pushInferredScope(initNode, text, parentChildren, lhs, throughWrapper); + } else { + _processNode(initNode, text, parentChildren); + } + return; + } + case 'ParenthesizedExpression': { + // Parens are pure-syntactic. Preserve throughWrapper as-is. + for (let c = initNode.firstChild; c; c = c.nextSibling) { + if (c.name === '(' || c.name === ')') { + continue; + } + _processInitExpr(c, text, parentChildren, lhs, throughWrapper); + } + return; + } + case 'CallExpression': + case 'NewExpression': { + // For each child of the call: if it's the argument list, recurse into + // each argument with inference. Other children (callee, type args) are + // walked normally so any nested function literals there get plain + // scopes. They don't "contribute to" the assignment target. + // Optional calls (`fn?.(() => {})`) are also CallExpression in @lezer/javascript + // (the optional `?.` is an inner token), so they go through this branch. + for (let c = initNode.firstChild; c; c = c.nextSibling) { + if (c.name === 'ArgList') { + for (let arg = c.firstChild; arg; arg = arg.nextSibling) { + if (arg.name === '(' || arg.name === ')' || arg.name === ',') { + continue; + } + _processInitExpr(arg, text, parentChildren, lhs, true); + } + } else { + _processNode(c, text, parentChildren); + } + } + return; + } + case 'AwaitExpression': { + // `await wrap(() => {})` keeps wrap-pattern inference for the inner call. + // The `await` keyword is itself a child; skip it. + for (let c = initNode.firstChild; c; c = c.nextSibling) { + if (c.name === 'await') { + continue; + } + _processInitExpr(c, text, parentChildren, lhs, throughWrapper); + } + return; + } + case 'TaggedTemplateExpression': { + // `` tag`${() => {}}` `` behaves like `tag(["..."], () => {})`: interpolated + // expressions contribute to the assignment target via the tag call. + for (let c = initNode.firstChild; c; c = c.nextSibling) { + if (c.name !== 'TemplateString') { + // The tag (callee). Walk normally so any nested function literal + // inside it gets a plain scope rather than being inferred. + _processNode(c, text, parentChildren); + continue; + } + for (let part = c.firstChild; part; part = part.nextSibling) { + if (part.name !== 'Interpolation') { + continue; + } + for (let e = part.firstChild; e; e = e.nextSibling) { + if ( + e.name === 'InterpolationStart' || + e.name === 'InterpolationEnd' + ) { + continue; + } + _processInitExpr(e, text, parentChildren, lhs, true); + } + } + } + return; + } + default: + _processNode(initNode, text, parentChildren); + } +} + +// --------------------------------------------------------------------------- +// CST walker +// --------------------------------------------------------------------------- + +function _processNode( + node: SyntaxNode, + text: string, + parentChildren: FunctionScope[] +): void { + switch (node.name) { + case 'MethodDeclaration': { + const keyNode = + node.getChild('PropertyDefinition') ?? + node.getChild('PrivatePropertyDefinition'); + if (!keyNode) { + // Computed key. No stable mapping location. + _walkChildren(node, text, parentChildren); + return; + } + _pushMethodScope(node, text, parentChildren); + return; + } + case 'Property': { + const keyNode = + node.getChild('PropertyDefinition') ?? + node.getChild('PrivatePropertyDefinition'); + + if (!keyNode) { + // Computed key (`{ [expr]() {} }`): no stable mapping location. + _walkChildren(node, text, parentChildren); + return; + } + + const hasColon = node.getChild(':') !== null; + + if (!hasColon) { + const hasParamList = node.getChild('ParamList') !== null; + if (!hasParamList) { + // Shorthand `{ a }`: not a function definition. + return; + } + // Method shorthand: `{ foo() {} }`. + _pushMethodScope(node, text, parentChildren); + return; + } + + // Function-valued property: `{ foo: function() {} }` or `{ foo: () => {} }`. + const funcNode = + node.getChild('FunctionExpression') ?? node.getChild('ArrowFunction'); + if (funcNode) { + _pushMethodScope(node, text, parentChildren); + return; + } + + // Non-function value: walk in case there are nested functions. + _walkChildren(node, text, parentChildren); + return; + } + case 'VariableDeclaration': { + // Lezer flattens multi-declaration: keyword, VarDef, Equals, init, ',', VarDef, Equals, init, ... + // Pair each VariableDefinition with its init expression (the child that + // follows the matching Equals). The init is dispatched to + // _processInitExpr so wrapping calls/new contribute inferred names to + // function arguments. + let pendingId: SyntaxNode | null = null; + let sawEquals = false; + for (let child = node.firstChild; child; child = child.nextSibling) { + if (child.name === 'VariableDefinition') { + pendingId = child; + sawEquals = false; + } else if (child.name === 'Equals') { + sawEquals = true; + } else if (child.name === ',') { + pendingId = null; + sawEquals = false; + } else if (sawEquals && pendingId !== null) { + _processInitExpr( + child, + text, + parentChildren, + { + identifierLoc: pendingId.from, + computedKeyLoc: null, + lhsText: text.slice(pendingId.from, pendingId.to), + }, + /* throughWrapper */ false + ); + pendingId = null; + sawEquals = false; + } else { + // Standalone child (var/let/const keyword, TypeAnnotation, ...): + // walk it for nested functions but don't treat it as an init. + _processNode(child, text, parentChildren); + } + } + return; + } + case 'AssignmentExpression': { + const lhs = _lhsContextFromAssignment(node, text); + // Only infer for plain `=`. Compound assignments (`+=`, `||=`, ...) have + // an UpdateOp instead of an Equals child. Let those fall through to the + // default walk, since the LHS isn't really being assigned a new identity. + if (lhs === null || node.getChild('Equals') === null) { + _walkChildren(node, text, parentChildren); + return; + } + let pastEquals = false; + for (let c = node.firstChild; c; c = c.nextSibling) { + if (c.name === 'Equals') { + pastEquals = true; + continue; + } + if (pastEquals) { + _processInitExpr(c, text, parentChildren, lhs, false); + } else { + // LHS. Walk for nested functions inside computed properties etc. + _processNode(c, text, parentChildren); + } + } + return; + } + case 'FunctionDeclaration': + case 'FunctionExpression': { + const nameNode = node.getChild('VariableDefinition'); + const scope: FunctionScope = { + start: node.from, + end: node.to, + nameMappingLocations: _mappingLocationsForFunction(node, text), + astName: nameNode ? text.slice(nameNode.from, nameNode.to) : null, + kind: 'function', + contributesTo: false, + computedKeyLoc: null, + lhsText: null, + children: [], + }; + parentChildren.push(scope); + _walkChildren(node, text, scope.children); + return; + } + case 'ArrowFunction': { + const scope: FunctionScope = { + start: node.from, + end: node.to, + nameMappingLocations: _mappingLocationsForArrow(node, text), + astName: null, + kind: 'arrow', + contributesTo: false, + computedKeyLoc: null, + lhsText: null, + children: [], + }; + parentChildren.push(scope); + _walkChildren(node, text, scope.children); + return; + } + default: + _walkChildren(node, text, parentChildren); + } +} + +/** + * Create and push a FunctionScope for a MethodDeclaration or a Property with a + * function value. + * + * The scope starts at `node.from` (the method key) so that the function's + * generated position (which Gecko reports at the key) is covered. + * + * For MethodDeclaration (and method-shorthand Property), ParamList/Block are + * direct children of the node. For Property with a function value, funcChild + * is the FunctionExpression/ArrowFunction wrapper. Either way, we walk + * children of funcNode to avoid re-creating a scope for the wrapper itself. + */ +function _pushMethodScope( + node: SyntaxNode, + text: string, + parentChildren: FunctionScope[] +): void { + const funcChild = + node.getChild('FunctionExpression') ?? node.getChild('ArrowFunction'); + const funcNode = funcChild ?? node; + + const funcLocations = + funcNode.name === 'ArrowFunction' + ? _mappingLocationsForArrow(funcNode, text) + : _mappingLocationsForFunction(funcNode, text); + + const scope: FunctionScope = { + start: node.from, + end: node.to, + nameMappingLocations: [..._keyMappingLocation(node), ...funcLocations], + astName: _astNameForKey(node, text), + // Arrow-valued properties (e.g. `{ foo: () => {} }`) must be 'arrow' so + // source-map symbolication skips the funcOffset probe, which resolves to + // the first parameter name rather than the function name. + kind: funcChild?.name === 'ArrowFunction' ? 'arrow' : 'function', + contributesTo: false, + computedKeyLoc: null, + lhsText: null, + children: [], + }; + parentChildren.push(scope); + _walkChildren(funcNode, text, scope.children); +} + +function _walkChildren( + node: SyntaxNode, + text: string, + children: FunctionScope[] +): void { + for (let child = node.firstChild; child; child = child.nextSibling) { + _processNode(child, text, children); + } +} + +// --------------------------------------------------------------------------- +// Public API +// --------------------------------------------------------------------------- + +// @lezer/javascript ships TS and JSX as opt-in dialects. The base parser only +// understands plain JS, so a `.ts` or `.tsx` source feeding back through +// `sourcesContent` produces Error nodes for every type annotation or JSX +// element. Always configure the matching dialect for original sources. +export type JsDialect = '' | 'ts' | 'jsx' | 'ts jsx'; + +const _configuredParsers = new Map(); + +function _parserForDialect(dialect: JsDialect): typeof lezerJsParser { + if (!dialect) { + return lezerJsParser; + } + let parser = _configuredParsers.get(dialect); + if (parser === undefined) { + parser = lezerJsParser.configure({ dialect }); + _configuredParsers.set(dialect, parser); + } + return parser; +} + +/** + * Map a source filename to the Lezer dialect needed to parse it. Looks at the + * file extension only. Accepts URL-style source paths (the typical content of + * a source map's `sources[]`). + */ +export function dialectForFilename(filename: string): JsDialect { + const lower = filename.toLowerCase(); + if (lower.endsWith('.tsx')) { + return 'ts jsx'; + } + if (lower.endsWith('.jsx')) { + return 'jsx'; + } + if ( + lower.endsWith('.ts') || + lower.endsWith('.mts') || + lower.endsWith('.cts') + ) { + return 'ts'; + } + return ''; +} + +/** + * Parse `sourceText` with Lezer and return the top-level function scopes. + * + * `dialect` selects the Lezer dialect: `'ts'` for TypeScript, `'jsx'` for + * JSX, `'ts jsx'` for TSX. Default `''` is plain JS. + * + * Lezer never throws on invalid JS. Error nodes are silently skipped. + */ +export function parseJsScopeTree( + sourceText: string, + dialect: JsDialect = '' +): FunctionScope[] { + const tree = _parserForDialect(dialect).parse(sourceText); + const topLevel: FunctionScope[] = []; + _walkChildren(tree.topNode, sourceText, topLevel); + return topLevel; +} + +/** + * Find the innermost FunctionScope in the tree that contains `offset`. + * + * Returns the path from innermost to outermost as an array: + * result[0]: the innermost scope (the one that directly contains offset) + * result.slice(1): ancestors, nearest first (parent, grandparent, ...) + * + * Returns null if no top-level scope contains the offset. Sibling scopes are + * pushed in source order and are non-overlapping, so a binary search picks the + * candidate at each level in O(log n). + */ +export function findInnermostFunctionScope( + scopes: FunctionScope[], + offset: number +): FunctionScope[] | null { + function search(siblings: FunctionScope[]): FunctionScope[] | null { + const idx = bisectionRightByKey(siblings, offset, (s) => s.start) - 1; + if (idx < 0) { + return null; + } + const candidate = siblings[idx]; + if (offset >= candidate.end) { + return null; + } + const found = search(candidate.children); + if (found !== null) { + found.push(candidate); + return found; + } + return [candidate]; + } + + return search(scopes); +} diff --git a/src/profile-logic/source-map-store.ts b/src/profile-logic/source-map-store.ts new file mode 100644 index 0000000000..068719e3d9 --- /dev/null +++ b/src/profile-logic/source-map-store.ts @@ -0,0 +1,62 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import { SourceMapConsumer as LibSourceMapConsumer } from 'source-map'; +import type { RawSourceMap } from 'source-map'; +import type { IndexIntoSourceTable } from 'firefox-profiler/types'; + +export type SourceMapConsumer = LibSourceMapConsumer; + +/** + * Holds pre-parsed SourceMapConsumer instances keyed by IndexIntoSourceTable. + * Initialized from the resolvedSourceMaps already fetched at profile load time. + */ +export class SourceMapStore { + private _consumers: Map; + + private constructor(consumers: Map) { + this._consumers = consumers; + } + + static async create( + resolvedSourceMaps: Map, + wasmUrl: string + ): Promise { + const consumers = new Map(); + + // `initialize` is a static method at runtime but typed as instance method + // in the .d.ts. https://github.com/mozilla/source-map/pull/520 + // No-op if already initialized. + await (LibSourceMapConsumer as any).initialize({ + 'lib/mappings.wasm': wasmUrl, + }); + + await Promise.all( + Array.from(resolvedSourceMaps).map(async ([sourceIndex, sourceMap]) => { + try { + const consumer = await new LibSourceMapConsumer(sourceMap); + consumers.set(sourceIndex, consumer); + } catch (e) { + console.warn( + `SourceMapStore: failed to parse source map for sourceIndex=${sourceIndex}:`, + e + ); + } + }) + ); + + return new SourceMapStore(consumers); + } + + getConsumer(sourceIndex: IndexIntoSourceTable): SourceMapConsumer | null { + return this._consumers.get(sourceIndex) ?? null; + } + + destroy(): void { + for (const consumer of this._consumers.values()) { + consumer.destroy(); + } + this._consumers.clear(); + } +} diff --git a/src/profile-logic/source-map-symbolication.ts b/src/profile-logic/source-map-symbolication.ts new file mode 100644 index 0000000000..1b5234b42d --- /dev/null +++ b/src/profile-logic/source-map-symbolication.ts @@ -0,0 +1,1060 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * JS source map symbolication. Maps generated (compiled/bundled) JS positions + * back to their original source files, lines, columns, and function names + * using source maps fetched at profile load time. + * + * Storage: the source file lives on funcTable.source (frames only carry + * line/column). The per-row remapping lives in originalLocation. funcTable and + * frameTable index into originalLocation via their `.originalLocation` column. + * + * The shapes the scope tree recognizes (self-named, direct assignment, + * wrap-pattern, computed-member) are catalogued at the top of + * `source-map-scope-tree.ts`. This file is the resolver: it consumes those + * scopes and produces final function names. + * + * ## Function name resolution + * + * Names come from two sources, both optional. The compiled source (the + * bundle text fetched from the browser) enables the full resolution path + * including the Nonymous ancestor chain. The original source is read from + * the source map's `sourcesContent` field, which toolchains may populate + * fully, partially, or not at all. If the compiled source is missing (e.g. + * the profile was captured without the JS Sources feature), we fall back + * to original-source-only resolution without an ancestor chain. + * + * 1. Original source (parsed lazily, cached per file). Look up the innermost + * function scope at the original position. Use `scope.astName` if set, + * else fall back to `scope.lhsText` (the verbatim LHS of an inferred + * scope). This recovers names that minifiers stripped and preserves + * un-minified member chains: + * + * // bundle: const a = function(){}. Original: function mapToPropsProxy(){} + * // -> mapToPropsProxy + * + * Watcher.prototype.run = function() {} // -> Watcher.prototype.run + * this.eventPool_.createObject = ... // -> this.eventPool_.createObject + * obj[key] = fn // -> obj[key] + * + * 2. Compiled source. Walk the scope's `nameMappingLocations` with exact + * source-map probes. On miss, fall back to `scope.astName` (the compiled + * identifier). For computed-member assignments, compose + * `${receiver}[${key}]` from two probes. Used as a fallback when (1) + * didn't yield anything because `sourcesContent` was empty. + * + * ## Ancestor chain + * + * SpiderMonkey's NameFunctions output prefixes a function's name with its + * enclosing chain only when the local name doesn't fully express the + * function's identity on its own. We mirror that: + * + * (Here `() => {}` stands in for any anonymous function or arrow function. + * `wrap(...)` is any call/new expression that the source-map-scope-tree + * treats as a "transparent wrapper". See its docs for the recognized shapes.) + * + * var foo = () => {} // -> foo (bare named, no chain) + * var foo = wrap(() => {}) // -> outer/foo< (contributes-to: chain + `<`) + * obj[key] = () => {} // -> outer/obj[key] (member-style: chain) + * function () { ... } // -> outer/< (anonymous: chain) + * + * When building the chain we keep the immediate parent and any *named* + * ancestors above it. Intermediate anonymous IIFEs are skipped, so bundled + * UMD code reads as `outer/local`, not ` = new Map() +): SourceMapSymbolicationResponse | null { + const { funcsToSymbolicate, framesToSymbolicate } = _identifyToSymbolicate( + shared.frameTable, + shared.funcTable, + shared.sources + ); + + if (funcsToSymbolicate.length === 0 && framesToSymbolicate.length === 0) { + return null; + } + + return _buildSourceMapSymbolicationResponse( + shared, + funcsToSymbolicate, + framesToSymbolicate, + sourceMapStore, + compiledSources + ); +} + +/** + * Walk the frames and funcs to identify which ones need JS symbolication. + * Each JS func with a sourceMapURL is a candidate. Its frames are also + * candidates for line/column remapping. + */ +function _identifyToSymbolicate( + frameTable: FrameTable, + funcTable: FuncTable, + sources: SourceTable +): { + funcsToSymbolicate: IndexIntoFuncTable[]; + framesToSymbolicate: IndexIntoFrameTable[]; +} { + const funcsToSymbolicate: IndexIntoFuncTable[] = []; + const framesToSymbolicate: IndexIntoFrameTable[] = []; + + // Frame tables can have >1M entries, with many frames per func. Cache the + // per-func eligibility so the inner per-frame check is a single array + // lookup instead of three nullability checks against cold columns. + // 0 = unknown, 1 = eligible, 2 = ineligible. + const funcEligibility = new Uint8Array(funcTable.length); + + for (let frameIndex = 0; frameIndex < frameTable.length; frameIndex++) { + const funcIndex = frameTable.func[frameIndex]; + + let eligibility = funcEligibility[funcIndex]; + if (eligibility === 0) { + eligibility = _isFuncSymbolicable(funcIndex, funcTable, sources) ? 1 : 2; + funcEligibility[funcIndex] = eligibility; + + if (eligibility === 1 && funcTable.originalLocation[funcIndex] === null) { + const funcLine = funcTable.lineNumber[funcIndex]; + const funcCol = funcTable.columnNumber[funcIndex]; + if (funcLine !== null && funcCol !== null) { + funcsToSymbolicate.push(funcIndex); + } + } + } + + if ( + eligibility === 1 && + frameTable.originalLocation[frameIndex] === null && + frameTable.line[frameIndex] !== null && + frameTable.column[frameIndex] !== null + ) { + framesToSymbolicate.push(frameIndex); + } + } + + return { funcsToSymbolicate, framesToSymbolicate }; +} + +function _isFuncSymbolicable( + funcIndex: IndexIntoFuncTable, + funcTable: FuncTable, + sources: SourceTable +): boolean { + if (!funcTable.isJS[funcIndex]) { + return false; + } + const sourceIndex = funcTable.source[funcIndex]; + if (sourceIndex === null) { + return false; + } + return sources.sourceMapURL[sourceIndex] !== null; +} + +/** + * Return true if the source-map has an exact entry at `pos` (0-based col). + * + * `source-map`'s `originalPositionFor` does a greatest-lower-bound (GLB) + * lookup: it returns the largest mapping whose generated position is <= the + * query, so most queries round down to a preceding mapping rather than + * requiring an exact match. We approximate "is there an exact entry at pos?" + * by comparing GLB(pos) with GLB(predecessor of pos): if they differ, `pos` + * must be the start of a new mapping (i.e. an exact entry). + * + * For pos.col > 0 the predecessor is (line, col - 1). For pos.col === 0 the + * predecessor is the end of the previous line: GLB will clip to the last + * actual mapping with (line, col) <= (pos.line - 1, anything), so asking with + * a very large column on the previous line suffices. + * + * TODO: This approximation produces false negatives when two consecutive + * source-map mappings resolve to the same original position (GLB(pos) == + * GLB(predecessor) even though pos is an exact entry). If this causes + * incorrect function names in practice, replace with a direct mapping-entry + * lookup once the source-map library exposes that API. + */ +function _isExactSourceMapEntry( + consumer: SourceMapConsumer, + pos: { line: number; col: number }, + glb: { source: string | null; line: number | null; column: number | null } +): boolean { + let prev; + if (pos.col === 0) { + if (pos.line === 0) { + // First char of the file. No predecessor exists. + return true; + } + // Probe the end of the previous line. + prev = consumer.originalPositionFor({ + line: pos.line, + column: INT32_MAX, + }); + } else { + prev = consumer.originalPositionFor({ + line: pos.line + 1, + column: pos.col - 1, + }); + } + return ( + prev.source !== glb.source || + prev.line !== glb.line || + prev.column !== glb.column + ); +} + +/** + * Walk the funcs/frames to symbolicate, run source-map lookups, and collect + * the results into a position-keyed response. The worker doesn't allocate + * source-table or originalLocation indices and doesn't intern strings; that + * bookkeeping happens main-side in applySourceMapSymbolicationResponse. + * + * Returns null if no funcs or frames were resolved (e.g. no consumers were + * available, or every position fell outside its source map's coverage). + */ +function _buildSourceMapSymbolicationResponse( + shared: SourceMapSymbolicationInput, + funcsToSymbolicate: IndexIntoFuncTable[], + framesToSymbolicate: IndexIntoFrameTable[], + sourceMapStore: SourceMapStore, + compiledSources: Map +): SourceMapSymbolicationResponse | null { + const { frameTable, funcTable } = shared; + + const funcResults = new Map(); + const frameResults = new Map(); + + // Per-URL source content fetched from source maps' sourcesContent. Used + // both by original-source name resolution and (post-walk) to populate the + // response's originalSources map. Value null means we probed and the + // source map had no content for this URL. + const originalSourceContents = new Map(); + + const compiledSourceCache = new Map(); + + // Parsed scope tree + line offsets of original sources we've seen, keyed + // by URL. Used to recover function names stripped during minification. + // Value `null` is cached when no content was available so we don't keep + // retrying. + const originalSourceCache = new Map(); + + // Function definitions. These get an original source file. + for (const funcIndex of funcsToSymbolicate) { + const sourceIndex = funcTable.source[funcIndex]; + if (sourceIndex === null) { + continue; + } + const consumer = sourceMapStore.getConsumer(sourceIndex); + if (consumer === null) { + continue; + } + const line = funcTable.lineNumber[funcIndex]; + const column = funcTable.columnNumber[funcIndex]; + if (line === null || column === null) { + continue; + } + const remap = _remapPosition( + consumer, + line, + column, + originalSourceContents + ); + if (remap === null) { + continue; + } + + // Full resolution (compiled + original, with ancestor chain) when the + // compiled bundle is available. Otherwise (e.g. the profile was captured + // without the JS Sources feature) fall back to original-source-only + // resolution, which still recovers names from `sourcesContent`. + const compiledText = compiledSources.get(sourceIndex); + let resolvedName: string | null; + if (compiledText !== undefined) { + let compiled = compiledSourceCache.get(sourceIndex); + if (compiled === undefined) { + compiled = { + tree: parseJsScopeTree(compiledText), + lineOffsets: buildLineOffsets(compiledText), + }; + compiledSourceCache.set(sourceIndex, compiled); + } + + resolvedName = _resolveFunctionName( + consumer, + remap.original, + line, + column, + compiled.lineOffsets, + compiled.tree, + originalSourceContents, + originalSourceCache + ); + } else { + resolvedName = _resolveOriginalName( + remap.original, + originalSourceContents, + originalSourceCache + ); + } + + funcResults.set(funcIndex, { + originalSource: remap.originalSource, + originalLine: remap.originalLine, + originalColumn: remap.originalColumn, + name: resolvedName, + }); + } + + // Frame execution points. Remap line/column and capture the original source + // file. In most cases this matches the func's original source, but for + // inlined code it may differ (e.g. a function from utils.ts inlined into + // app.ts maps frames back to utils.ts). + for (const frameIndex of framesToSymbolicate) { + const sourceIndex = funcTable.source[frameTable.func[frameIndex]]; + if (sourceIndex === null) { + continue; + } + const consumer = sourceMapStore.getConsumer(sourceIndex); + if (consumer === null) { + continue; + } + const line = frameTable.line[frameIndex]; + const column = frameTable.column[frameIndex]; + if (line === null || column === null) { + continue; + } + const remap = _remapPosition( + consumer, + line, + column, + originalSourceContents + ); + if (remap === null) { + continue; + } + frameResults.set(frameIndex, { + originalSource: remap.originalSource, + originalLine: remap.originalLine, + originalColumn: remap.originalColumn, + }); + } + + if (funcResults.size === 0 && frameResults.size === 0) { + return null; + } + + const originalSources = new Map(); + for (const [url, content] of originalSourceContents) { + originalSources.set(url, { content }); + } + + return { funcResults, frameResults, originalSources }; +} + +/** + * Remap a single (line, column) through the source map. Returns the original + * source URL plus 1-based line/column (Gecko convention), or null when the + * source map has no mapping at that position. Also populates + * `originalSourceContents` with the source map's sourcesContent for the URL + * the first time it's seen, used by original-source name resolution and + * later shipped to the main thread in the response's originalSources map. + * + * Gecko line/column are 1-based. source-map expects 0-based columns and + * returns 0-based columns; we convert in both directions here so callers + * stay in Gecko's convention. + */ +function _remapPosition( + consumer: SourceMapConsumer, + line: number, + column: number, + originalSourceContents: Map +): { + originalSource: string; + originalLine: number; + originalColumn: number; + original: OriginalPosition; +} | null { + const original = consumer.originalPositionFor({ line, column: column - 1 }); + if (original.source === null || original.line === null) { + return null; + } + + // Extract original source content from sourcesContent if not yet seen, or + // re-probe when a prior consumer recorded null but this one might have it. + // nullOnMissing=true so we get null instead of a throw when not embedded. + const existingContent = originalSourceContents.get(original.source); + if (existingContent === undefined || existingContent === null) { + const content = consumer.sourceContentFor(original.source, true); + if (content !== null) { + originalSourceContents.set(original.source, content); + } else if (existingContent === undefined) { + originalSourceContents.set(original.source, null); + } + } + + return { + originalSource: original.source, + originalLine: original.line, + originalColumn: (original.column ?? 0) + 1, + original: original as OriginalPosition, + }; +} + +/** + * Resolve the original function name. + * + * Resolution order: + * 1. Look the function up in the **original** source (parsed from the + * source map's sourcesContent). If the innermost scope at that position + * has its own `astName` (i.e. it's a named function declaration / named + * function expression / method / property), that name wins. This is what + * recovers names that the minifier stripped (e.g. esbuild drops + * `function mapToPropsProxy()` to `function()` when the inner name isn't + * self-referenced). + * 2. Otherwise probe the compiled-source scope tree at each + * `nameMappingLocation` with an exact source-map lookup, falling back to + * the compiled scope's `astName`. + * + * The Nonymous-style ancestor chain (`outer/.../local`) is only emitted when + * the local segment doesn't fully express the function's identity on its + * own: either the local is anonymous, "contributes to" the target (passed + * through a wrapping call/`new`, gets `<`), or is a computed-member + * expression (`receiver[key]`). A simple named direct assignment + * (`var x = anonFn`) returns just `x`, matching SpiderMonkey's output. + * + * For computed-member assignments (`obj[key] = fn`), `scope.computedKeyLoc` + * triggers a second source-map probe so the resolved name is composed as + * `${receiver}[${key}]`. + * + * Returns null if no name could be determined. + */ +function _resolveFunctionName( + consumer: SourceMapConsumer, + original: OriginalPosition, + line: number, + column: number, + lineOffsets: number[], + scopeTree: FunctionScope[], + originalSourceContents: Map, + originalSourceCache: Map +): string | null { + // 1. Original-source name lookup. Prefers the function's own declared + // identifier, falling back to the verbatim LHS text of the surrounding + // assignment. When it produces a simple (non-member) name, we return + // immediately. When it's member-style (`Foo.prototype.bar` / `obj[key]`), + // we keep it but drop into the chain branch below so the result carries + // the enclosing function, matching SpiderMonkey's output shape (e.g. + // `outer/Foo.prototype.bar`). + const originalName = _resolveOriginalName( + original, + originalSourceContents, + originalSourceCache + ); + if (originalName !== null && !_isMemberStyleName(originalName)) { + return originalName; + } + + // Gecko uses 1-based line/column. Convert to 0-based for char-offset math. + const funcOffset = lineColToOffset(line - 1, column - 1, lineOffsets); + // scopePath[0] = innermost scope, scopePath[1..] = ancestors (nearest first). + const scopePath = findInnermostFunctionScope(scopeTree, funcOffset); + + if (scopePath === null) { + return originalName; + } + + const scope = scopePath[0]; + const ancestors = scopePath.slice(1); + + // Use the original-source name if available. Otherwise fall back to + // compiled-source resolution. Skipping the compiled path when the original + // source already named the function avoids reapplying computed-member + // composition on top of an `lhsText` of `obj[key]` (would double brackets). + const resolvedName = + originalName ?? + _resolveCompiledName(scope, funcOffset, consumer, original, lineOffsets); + + // Bare named local. Return it without the ancestor chain. Covers + // simple named function declarations (`function foo(){}`), named + // function expressions, and direct variable assignments + // (`var foo = function() {}` / `var foo = () => {}`). Matches SpiderMonkey's + // NameFunctions output, which only prefixes the chain when the local + // segment doesn't fully express the function's identity (anonymous, + // contributes-to, computed-member, or any other member-style name where + // the receiver references an outer-scope identifier). + const needsChain = + resolvedName === null || + scope.contributesTo || + scope.computedKeyLoc !== null || + _isMemberStyleName(resolvedName); + if (!needsChain) { + return resolvedName; + } + + // Build the ancestor chain. Walk innermost-first and keep: + // - the immediate parent (always, even if anonymous: the local being + // directly nested inside an IIFE is useful context). + // - every named ancestor above it (i.e. anything that resolves to a + // real identifier via the source map or AST). + // Skip non-immediate anonymous wrappers. This matches SpiderMonkey's + // output for bundled UMD code where the function we care about lives + // inside a few anonymous IIFEs. Gecko emits e.g. `ewH8/ s.kind === 'named') + ) { + return null; + } + + return serializeNonymousName([...ancestorSegments.reverse(), local]); +} + +/** + * True when the resolved name has property-access structure + * (`obj.foo`, `Foo.prototype.bar`, `obj[key]`, `arr[0]`, ...). The receiver + * in those names references an outer-scope identifier, so the result is + * only fully meaningful with the enclosing scope chain prefixed, + * matching SpiderMonkey's `outer/Foo.prototype.bar` shape. + * + * Plain `foo` / `_foo` / `$foo` stay bare. + */ +function _isMemberStyleName(name: string): boolean { + return name.includes('.') || name.includes('['); +} + +/** + * Probe a single offset in the compiled source: convert to line/col, look it + * up through the source map, and return the mapping's `name` only when the + * mapping is "exact" at that offset. Returns null on any miss. + */ +function _probeExactName( + consumer: SourceMapConsumer, + locOffset: number, + lineOffsets: number[] +): string | null { + const pos = offsetToLineCol(locOffset, lineOffsets); + // source-map's originalPositionFor uses 1-based lines, 0-based columns. + const glb = consumer.originalPositionFor({ + line: pos.line + 1, + column: pos.col, + }); + if ( + glb.source === null || + glb.name === null || + !_isExactSourceMapEntry(consumer, pos, glb) + ) { + return null; + } + return glb.name; +} + +/** + * Compiled-source name resolution: walk the scope's `nameMappingLocations` + * with exact source-map probes, fall back to `scope.astName`, and finally + * compose `${receiver}[${key}]` when the scope came from a computed-member + * assignment. + * + * Probe strategy depends on scope kind: + * + * Regular functions (FunctionDeclaration / FunctionExpression / method): + * Try `funcOffset` first with a relaxed accept rule (the mapping returned by + * the GLB lookup at funcOffset just has to resolve to the same original + * position as the function itself). esbuild often emits a wide mapping there + * so the stricter "exact entry" check (GLB(pos) != GLB(pos - 1), see + * `_isExactSourceMapEntry`) fails even though the mapping IS for this + * function. `nameMappingLocations` then use that stricter exact check. + * + * Arrow functions: + * `funcOffset` sits at the parameter/arrow position, not a function + * identifier. For renamed params (`profile` -> `e`), the GLB lookup there + * returns the parameter name. Skip funcOffset and probe + * `nameMappingLocations` only. + */ +function _resolveCompiledName( + scope: FunctionScope, + funcOffset: number, + consumer: SourceMapConsumer, + original: OriginalPosition, + lineOffsets: number[] +): string | null { + let resolvedName: string | null = null; + + if (scope.kind === 'function') { + const pos = offsetToLineCol(funcOffset, lineOffsets); + const glb = consumer.originalPositionFor({ + line: pos.line + 1, + column: pos.col, + }); + if ( + glb.name !== null && + glb.source === original.source && + glb.line === original.line && + glb.column !== null && + glb.column === original.column + ) { + resolvedName = glb.name; + } + } + + if (resolvedName === null) { + for (const locOffset of scope.nameMappingLocations) { + const name = _probeExactName(consumer, locOffset, lineOffsets); + if (name !== null) { + resolvedName = name; + break; + } + } + } + + // Chrome fallback: if no probe yielded a named entry, use the AST identifier + // (the compiled name). No-op for renamed functions. Resolves unminified + // methods that the source map didn't tag with a name. + if (resolvedName === null) { + resolvedName = scope.astName; + } + + // Computed-member assignment (`obj[key] = fn`): compose `${receiver}[${key}]`. + // Fall back to just the receiver if the key isn't resolvable. + if (resolvedName !== null && scope.computedKeyLoc !== null) { + const keyName = _probeExactName( + consumer, + scope.computedKeyLoc, + lineOffsets + ); + if (keyName !== null) { + resolvedName = `${resolvedName}[${keyName}]`; + } + } + + return resolvedName; +} + +/** + * Look up the function at `original.line` / `original.column` in the original + * source (parsed lazily and cached) and return a name derived from its scope. + * + * Resolution preference, highest to lowest: + * 1. `astName`: the function's own declared identifier (named function + * declaration / named function expression / method / property key). + * Recovers names that minifiers strip, e.g. esbuild dropping + * `function bar()` to `function()` when `bar` isn't self-referenced. + * 2. `lhsText`: the verbatim source text of the assignment-target LHS + * when the function was inferred from one. The original source is + * preserved in the source map's `sourcesContent`, so this carries + * the original (un-minified) member chain: `Watcher.prototype.run`, + * `this.eventPool_.createObject`, `obj[key]`, etc. + * + * Returns null if the original source isn't embedded, the parsed tree has + * no function containing the position, or the innermost scope has neither + * an `astName` nor an `lhsText`. + */ +function _resolveOriginalName( + original: OriginalPosition, + originalSourceContents: Map, + cache: Map +): string | null { + // A cached `null` means we saw no content the last time we looked, but a + // later _remapPosition from a different consumer may have populated + // `originalSourceContents` since. Re-check on every cached miss so the + // upgrade doesn't depend on iteration order across consumers. + let entry = cache.get(original.source); + if (entry === undefined || entry === null) { + const sourceContent = originalSourceContents.get(original.source) ?? null; + if (sourceContent === null) { + cache.set(original.source, null); + return null; + } + entry = { + tree: parseJsScopeTree( + sourceContent, + dialectForFilename(original.source) + ), + lineOffsets: buildLineOffsets(sourceContent), + }; + cache.set(original.source, entry); + } + // source-map's original.line is 1-based, column is 0-based. + const offset = lineColToOffset( + original.line - 1, + original.column ?? 0, + entry.lineOffsets + ); + const path = findInnermostFunctionScope(entry.tree, offset); + if (path === null) { + return null; + } + const scope = path[0]; + return scope.astName ?? scope.lhsText; +} + +/** + * Resolve a NonymousSegment for an ancestor scope: probe its mapping + * locations, then fall back to its AST name. Propagates `contributesTo` + * from the scope itself so wrap-pattern ancestors surface with the `<` + * suffix (e.g. the outer `i2<` in `i2 +): IndexIntoSourceTable { + const filenameStrIndex = stringTable.indexForString(filename); + + const existing = sourceByFilename.get(filenameStrIndex); + if (existing !== undefined) { + return existing; + } + + const index = sources.length; + sources.id.push(null); + sources.filename.push(filenameStrIndex); + // Original source files from source maps don't have start line/column. + sources.startLine.push(1); + sources.startColumn.push(1); + sources.sourceMapURL.push(null); + sources.content.push(null); + sources.length++; + + sourceByFilename.set(filenameStrIndex, index); + return index; +} + +/** + * End-to-end source map symbolication runner: build the SourceMapStore, run + * symbolication, and return a WorkerOutput carrying a position-keyed response. + * + * The worker only reads from `input` — no tables or string arrays are mutated. + * The main thread translates the response into new tables via + * applySourceMapSymbolicationResponse, against the latest shared state. + */ +export async function runSourceMapSymbolicationCore( + input: WorkerInput, + wasmUrl: string +): Promise { + let sourceMapStore: SourceMapStore | null = null; + try { + const { + resolvedSourceMaps, + compiledSources, + funcTable, + frameTable, + originalLocation, + sources, + stringArray, + } = input; + + sourceMapStore = await SourceMapStore.create(resolvedSourceMaps, wasmUrl); + const response = symbolicateWithSourceMaps( + { frameTable, funcTable, originalLocation, sources, stringArray }, + sourceMapStore, + compiledSources + ); + + if (response === null) { + return { type: 'no-op' }; + } + + return { type: 'success', response }; + } catch (err) { + return { + type: 'error', + message: err instanceof Error ? err.message : String(err), + }; + } finally { + if (sourceMapStore !== null) { + sourceMapStore.destroy(); + } + } +} + +/** + * Apply a worker-produced SourceMapSymbolicationResponse against the current + * shared tables and return new ones. This is the main-thread complement to + * the worker: it allocates the source-table and originalLocation indices, + * interns names into the string table, and dedupes original-source URLs + * against current state. + * + * Reading current state at apply time (rather than reusing the snapshot the + * worker started from) makes concurrent worker runs compose correctly: a + * second result landing after a first never undoes the first's writes, + * because each apply step skips funcs/frames that already have an + * originalLocation row. + * + * Idempotency relies on funcIndex/frameIndex being stable across concurrent + * runs. JS source-map symbolication only appends to originalLocation, + * sources, and stringArray — it never adds funcs or frames. If a future + * change adds funcs/frames during symbolication, this contract needs + * revisiting. + * + * Returns null when nothing applied (every response entry collided with + * a row that's already populated). + */ +export function applySourceMapSymbolicationResponse( + shared: RawProfileSharedData, + response: SourceMapSymbolicationResponse +): { + newFuncTable: FuncTable; + newFrameTable: FrameTable; + newOriginalLocation: SourceLocationTable; + newSources: SourceTable; + newStringArray: string[]; +} | null { + const { funcTable, frameTable, originalLocation, sources, stringArray } = + shared; + + const newFuncTable = shallowCloneFuncTable(funcTable); + const newFrameTable = shallowCloneFrameTable(frameTable); + const newOriginalLocation = shallowCloneSourceLocationTable(originalLocation); + const newSources = _shallowCloneSourceTable(sources); + const newStringArray = stringArray.slice(); + const stringTable = StringTable.withBackingArray(newStringArray); + + // filename string index to source index, covering all existing null-id + // (original source) entries. Updated in place as new entries are appended + // by _findOrCreateSource. + const sourceByFilename = new Map(); + for (let i = 0; i < newSources.length; i++) { + if (newSources.id[i] === null) { + sourceByFilename.set(newSources.filename[i], i); + } + } + + // Resolve each original-source URL to a source-table index, creating new + // entries as needed and upgrading content where the existing entry has + // none. + const urlToSourceIndex = new Map(); + for (const [url, { content }] of response.originalSources) { + const sourceIndex = _findOrCreateSource( + newSources, + stringTable, + url, + sourceByFilename + ); + if (content !== null && newSources.content[sourceIndex] === null) { + newSources.content[sourceIndex] = content; + } + urlToSourceIndex.set(url, sourceIndex); + } + + let applied = 0; + + for (const [funcIndex, resolution] of response.funcResults) { + // A concurrent run got here first. Skip the whole entry: the row and + // the name go together; writing a name without an originalLocation row + // would be inconsistent. + if (newFuncTable.originalLocation[funcIndex] !== null) { + continue; + } + const sourceIndex = urlToSourceIndex.get(resolution.originalSource); + if (sourceIndex === undefined) { + continue; + } + const rowIndex = newOriginalLocation.length; + newOriginalLocation.source.push(sourceIndex); + newOriginalLocation.line.push(resolution.originalLine); + newOriginalLocation.column.push(resolution.originalColumn); + newOriginalLocation.length++; + newFuncTable.originalLocation[funcIndex] = rowIndex; + if (resolution.name !== null) { + newFuncTable.name[funcIndex] = stringTable.indexForString( + resolution.name + ); + } + applied++; + } + + for (const [frameIndex, resolution] of response.frameResults) { + if (newFrameTable.originalLocation[frameIndex] !== null) { + continue; + } + const sourceIndex = urlToSourceIndex.get(resolution.originalSource); + if (sourceIndex === undefined) { + continue; + } + const rowIndex = newOriginalLocation.length; + newOriginalLocation.source.push(sourceIndex); + newOriginalLocation.line.push(resolution.originalLine); + newOriginalLocation.column.push(resolution.originalColumn); + newOriginalLocation.length++; + newFrameTable.originalLocation[frameIndex] = rowIndex; + applied++; + } + + if (applied === 0) { + return null; + } + + return { + newFuncTable, + newFrameTable, + newOriginalLocation, + newSources, + newStringArray, + }; +} + +function _shallowCloneSourceTable(sources: SourceTable): SourceTable { + return { + id: sources.id.slice(), + filename: sources.filename.slice(), + startLine: sources.startLine.slice(), + startColumn: sources.startColumn.slice(), + sourceMapURL: sources.sourceMapURL.slice(), + content: sources.content.slice(), + length: sources.length, + }; +} diff --git a/src/profile-logic/source-map-worker-types.ts b/src/profile-logic/source-map-worker-types.ts new file mode 100644 index 0000000000..d0487138fa --- /dev/null +++ b/src/profile-logic/source-map-worker-types.ts @@ -0,0 +1,70 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import type { + IndexIntoFuncTable, + IndexIntoFrameTable, + IndexIntoSourceTable, +} from 'firefox-profiler/types'; +import type { RawSourceMap } from 'source-map'; +import type { SourceMapSymbolicationInput } from './source-map-symbolication'; + +/** + * Data sent from the main thread to the source map symbolication worker. + * All values are structured-cloned (copied) across the thread boundary. + */ +export type WorkerInput = SourceMapSymbolicationInput & { + resolvedSourceMaps: Map; + // Compiled source texts keyed by bundle source index. + compiledSources: Map; +}; + +/** + * Resolved original location for a single frame. Line/column are 1-based + * (Gecko convention). `originalSource` is a URL string; the apply step on + * the main thread looks it up against the current sources table. + */ +export type FrameResolution = { + originalSource: string; + originalLine: number; + originalColumn: number; +}; + +/** + * Resolved original location plus optional function name for a single func. + * `name` is null when no name could be determined, in which case the existing + * funcTable.name entry is kept. + */ +export type FuncResolution = FrameResolution & { + name: string | null; +}; + +/** + * Opaque, position-keyed worker result. The apply step on the main thread + * (applySourceMapSymbolicationResponse) consumes this plus the current shared + * tables to produce new ones. + * + * Modeled like a symbolication API response: the worker doesn't allocate + * source-table or originalLocation indices, and doesn't intern strings. That + * bookkeeping happens main-side against the latest state, so concurrent + * worker runs compose correctly instead of one's stale snapshot stomping + * another's result. + */ +export type SourceMapSymbolicationResponse = { + // Funcs the worker resolved. Funcs not in this map keep their existing data. + // JS source-map symbolication never adds funcs, so funcIndex stays stable + // across concurrent runs. + funcResults: Map; + // Same idea for frames. + frameResults: Map; + // Original sources discovered during symbolication, keyed by URL. content + // is null when the source map didn't carry sourcesContent for this URL. + // The apply step dedupes URLs against the current sources table. + originalSources: Map; +}; + +export type WorkerOutput = + | { type: 'success'; response: SourceMapSymbolicationResponse } + | { type: 'no-op' } + | { type: 'error'; message: string }; diff --git a/src/profile-logic/source-map.worker.ts b/src/profile-logic/source-map.worker.ts new file mode 100644 index 0000000000..e051c839f8 --- /dev/null +++ b/src/profile-logic/source-map.worker.ts @@ -0,0 +1,37 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Web Worker entry point for source map symbolication. + * + * Receives a WorkerInput (all profile tables needed by symbolicateWithSourceMaps), + * runs @lezer/javascript parsing + source-map lookups off the main thread, and + * posts back a WorkerOutput with the updated tables. + * + * Must be built as a separate esbuild bundle (see sourceMapWorkerConfig) so + * that npm dependencies are bundled into the worker output file. + */ + +import { runSourceMapSymbolicationCore } from './source-map-symbolication'; +import type { WorkerInput, WorkerOutput } from './source-map-worker-types'; + +// Override the `self` type: in the browser this file runs as a DedicatedWorker, +// but TypeScript's DOM lib types `self` as `Window & typeof globalThis`. +// We only need the onmessage setter and postMessage for the worker protocol. +interface WorkerScope { + onmessage: ((e: MessageEvent) => void) | null; + postMessage: (message: WorkerOutput) => void; + location: Location; +} +const scope = self as unknown as WorkerScope; + +// Resolve mappings.wasm relative to this worker bundle's URL. The wasm is +// copied alongside the worker output by the esbuild copy plugin, so this +// works under both root and subpath deploys. +const mappingsWasmUrl = new URL('mappings.wasm', scope.location.href).href; + +scope.onmessage = async (e: MessageEvent) => { + const output = await runSourceMapSymbolicationCore(e.data, mappingsWasmUrl); + scope.postMessage(output); +}; diff --git a/src/profile-logic/symbolication.ts b/src/profile-logic/symbolication.ts index cdc01ac0d2..a6370bc6fa 100644 --- a/src/profile-logic/symbolication.ts +++ b/src/profile-logic/symbolication.ts @@ -795,6 +795,7 @@ function _partiallyApplySymbolicationStep( funcTable.source[funcIndex] = null; funcTable.lineNumber[funcIndex] = null; funcTable.columnNumber[funcIndex] = null; + funcTable.originalLocation[funcIndex] = null; // The name field will be filled below. funcTable.length++; } @@ -819,6 +820,7 @@ function _partiallyApplySymbolicationStep( sources.startLine.push(1); sources.startColumn.push(1); sources.sourceMapURL.push(null); + sources.content.push(null); sources.length++; } funcTable.source[funcIndex] = sourceIndex; @@ -851,6 +853,7 @@ function _partiallyApplySymbolicationStep( frameTable.innerWindowID[expansionFrameIndex] = innerWindowID; frameTable.address[expansionFrameIndex] = address; frameTable.nativeSymbol[expansionFrameIndex] = nativeSymbolIndex; + frameTable.originalLocation[expansionFrameIndex] = null; // These remaining fields are filled below. } diff --git a/src/profile-logic/transforms.ts b/src/profile-logic/transforms.ts index 5bf82499af..954bf4dc94 100644 --- a/src/profile-logic/transforms.ts +++ b/src/profile-logic/transforms.ts @@ -1614,7 +1614,8 @@ export function getBacktraceItemsForStack( implementationFilter: ImplementationFilter, thread: Thread ): BacktraceItem[] { - const { funcTable, stringTable, resourceTable, sources } = thread; + const { funcTable, stringTable, resourceTable, sources, originalLocation } = + thread; const { stackTable, frameTable } = thread; const unfilteredPath = []; @@ -1627,8 +1628,7 @@ export function getBacktraceItemsForStack( unfilteredPath.push({ category: stackTable.category[stackIndex], funcIndex: frameTable.func[frameIndex], - frameLine: frameTable.line[frameIndex], - frameColumn: frameTable.column[frameIndex], + frameIndex, inlineDepth: frameTable.inlineDepth[frameIndex], stackIndex, }); @@ -1639,26 +1639,20 @@ export function getBacktraceItemsForStack( funcMatchesImplementation(thread, funcIndex) ); return path.map( - ({ - category, - funcIndex, - frameLine, - frameColumn, - inlineDepth, - stackIndex, - }) => { + ({ category, funcIndex, frameIndex, inlineDepth, stackIndex }) => { return { funcName: stringTable.getString(funcTable.name[funcIndex]), category, isFrameLabel: funcTable.resource[funcIndex] === -1, origin: getOriginAnnotationForFunc( funcIndex, + frameIndex, + frameTable, funcTable, resourceTable, stringTable, sources, - frameLine, - frameColumn + originalLocation ), inlineDepth, stackIndex, diff --git a/src/profile-query/function-annotate.ts b/src/profile-query/function-annotate.ts index 4a94d96f26..9a98cc64e6 100644 --- a/src/profile-query/function-annotate.ts +++ b/src/profile-query/function-annotate.ts @@ -20,10 +20,12 @@ import { getNativeSymbolInfo, getNativeSymbolsForFunc, findAddressProofForFile, + getOriginalPositionForFrame, } from 'firefox-profiler/profile-logic/profile-data'; import { fetchAssembly } from 'firefox-profiler/utils/fetch-assembly'; import { fetchSource } from 'firefox-profiler/utils/fetch-source'; import type { ExternalCommunicationDelegate } from 'firefox-profiler/utils/query-api'; +import type { RawSourceMap } from 'source-map'; import type { Profile, IndexIntoFuncTable, @@ -56,6 +58,10 @@ class NodeExternalCommunicationDelegate implements ExternalCommunicationDelegate async fetchJSSourceFromBrowser(_source: string): Promise { throw new Error('No browser connection available in profiler-cli'); } + + async fetchSourceMapFromBrowser(_sourceId: string): Promise { + throw new Error('No browser connection available in profiler-cli'); + } } const nodeDelegate = new NodeExternalCommunicationDelegate(); @@ -71,8 +77,8 @@ async function fetchSourceAnnotation( contextOption: string ): Promise { const warnings: string[] = []; - const sourceIndex = profile.shared.funcTable.source[funcIndex]; - if (sourceIndex === null) { + const compiledSourceIndex = profile.shared.funcTable.source[funcIndex]; + if (compiledSourceIndex === null) { if (mode === 'src') { warnings.push( `Function ${functionHandle} has no source index. Use --mode asm for assembly view.` @@ -86,17 +92,34 @@ async function fetchSourceAnnotation( frameTable, funcTable: threadFuncTable, samples, + originalLocation, } = thread; + + // For JS-symbolicated funcs, prefer the original source from originalLocation + // so the source view shows the original file (filename, content, line/column + // hits) rather than the compiled bundle. Falls back to the compiled source + // when no source map info is present. + const { source: resolvedSourceIndex } = getOriginalPositionForFrame( + null, + funcIndex, + frameTable, + threadFuncTable, + originalLocation + ); + const sourceIndex = resolvedSourceIndex ?? compiledSourceIndex; + const filename = thread.stringTable.getString( thread.sources.filename[sourceIndex] ); const sourceUuid = thread.sources.id[sourceIndex]; + const inlineContent = thread.sources.content[sourceIndex]; const stackLineInfo = getStackLineInfo( stackTable, frameTable, threadFuncTable, - sourceIndex + sourceIndex, + originalLocation ); const { totalLineHits, selfLineHits } = getLineTimings( stackLineInfo, @@ -125,22 +148,30 @@ async function fetchSourceAnnotation( let fileLines: string[] | null = null; let totalFileLines: number | null = null; - const fetchResult = await fetchSource( - filename, - sourceUuid, - symbolServerUrl, - addressProof, - archiveCache, - nodeDelegate - ); - if (fetchResult.type === 'SUCCESS') { - fileLines = fetchResult.source.split('\n'); + if (inlineContent !== null) { + // Source content was embedded in the profile (e.g. captured from a source + // map's sourcesContent during JS symbolication). Use it directly so the + // source view works offline. + fileLines = inlineContent.split('\n'); totalFileLines = fileLines.length; } else { - const errorMessages = fetchResult.errors - .map((e) => JSON.stringify(e)) - .join('; '); - warnings.push(`Could not fetch source for ${filename}: ${errorMessages}`); + const fetchResult = await fetchSource( + filename, + sourceUuid, + symbolServerUrl, + addressProof, + archiveCache, + nodeDelegate + ); + if (fetchResult.type === 'SUCCESS') { + fileLines = fetchResult.source.split('\n'); + totalFileLines = fileLines.length; + } else { + const errorMessages = fetchResult.errors + .map((e) => JSON.stringify(e)) + .join('; '); + warnings.push(`Could not fetch source for ${filename}: ${errorMessages}`); + } } const annotatedLineNums = new Set([ diff --git a/src/reducers/profile-view.ts b/src/reducers/profile-view.ts index 9a611c1f69..92fa3f6f69 100644 --- a/src/reducers/profile-view.ts +++ b/src/reducers/profile-view.ts @@ -20,6 +20,7 @@ import type { Reducer, ProfileViewState, SymbolicationStatus, + SourceMapSymbolicationStatus, ThreadViewOptions, ThreadViewOptionsPerThreads, TableViewOptionsPerTab, @@ -58,6 +59,31 @@ const profile: Reducer = (state = null, action) => { threads: symbolicatedThreads, }; } + case 'BULK_SOURCE_MAP_SYMBOLICATION': { + if (state === null) { + throw new Error( + 'Assumed that a profile would be loaded for JS source map symbolication.' + ); + } + const { + newFuncTable, + newFrameTable, + newOriginalLocation, + newSources, + newStringArray, + } = action; + return { + ...state, + shared: { + ...state.shared, + funcTable: newFuncTable, + frameTable: newFrameTable, + originalLocation: newOriginalLocation, + sources: newSources, + stringArray: newStringArray, + }, + }; + } case 'DONE_SYMBOLICATING': { if (state === null) { throw new Error( @@ -136,6 +162,27 @@ const symbolicationStatus: Reducer = ( } }; +const sourceMapSymbolicationStatus: Reducer = ( + state = 'INACTIVE', + action +) => { + switch (action.type) { + case 'START_SOURCE_MAP_FETCHING': + return 'FETCHING'; + case 'START_SOURCE_MAP_SYMBOLICATION': + return 'SYMBOLICATING'; + // Fetching done but worker not yet started. Go back to INACTIVE. + // The next START_SOURCE_MAP_SYMBOLICATION will set it to SYMBOLICATING. + case 'DONE_SOURCE_MAP_FETCHING': + return state === 'FETCHING' ? 'INACTIVE' : state; + case 'BULK_SOURCE_MAP_SYMBOLICATION': + case 'SOURCE_MAP_SYMBOLICATION_FAILED': + return 'INACTIVE'; + default: + return state; + } +}; + export const defaultThreadViewOptions: ThreadViewOptions = { selectedNonInvertedCallNodePath: [], selectedInvertedCallNodePath: [], @@ -826,6 +873,7 @@ const profileViewReducer: Reducer = wrapReducerInResetter( viewOptions: combineReducers({ perThread: viewOptionsPerThread, symbolicationStatus, + sourceMapSymbolicationStatus, waitingForLibs, previewSelection, scrollToSelectionGeneration, diff --git a/src/selectors/code.tsx b/src/selectors/code.tsx index adb5a46152..0356388d7d 100644 --- a/src/selectors/code.tsx +++ b/src/selectors/code.tsx @@ -4,10 +4,10 @@ import { createSelector } from 'reselect'; import type { AssemblyCodeStatus, + IndexIntoSourceTable, Lib, SourceCodeStatus, Selector, - IndexIntoSourceTable, } from 'firefox-profiler/types'; import { getSourceViewSourceIndex, @@ -23,8 +23,19 @@ export const getSourceViewCode: Selector = createSelector( getSourceCodeCache, getSourceViewSourceIndex, - (sourceCodeCache, sourceIndex) => - sourceIndex !== null ? sourceCodeCache.get(sourceIndex) : undefined + getProfileOrNull, + (sourceCodeCache, sourceIndex, profile) => { + if (sourceIndex === null) { + return undefined; + } + // Prefer source content stored in the profile (from source map sourcesContent), + // so the source view works offline and when sharing profiles. + const inlineCode = profile?.shared.sources.content[sourceIndex]; + if (inlineCode !== null && inlineCode !== undefined) { + return { type: 'AVAILABLE', code: inlineCode }; + } + return sourceCodeCache.get(sourceIndex); + } ); export const getAssemblyCodeCache: Selector> = ( diff --git a/src/selectors/per-thread/index.ts b/src/selectors/per-thread/index.ts index 6b777e2ef5..36a3b941a9 100644 --- a/src/selectors/per-thread/index.ts +++ b/src/selectors/per-thread/index.ts @@ -225,17 +225,24 @@ export const selectedNodeSelectors: NodeSelectors = (() => { selectedThreadSelectors.getSelectedCallNodePath, selectedThreadSelectors.getFilteredThread, ProfileSelectors.getSourceTable, - (selectedPath, { stringTable, funcTable, resourceTable }, sources) => { + ( + selectedPath, + { stringTable, frameTable, funcTable, resourceTable, originalLocation }, + sources + ) => { if (!selectedPath.length) { return ''; } return ProfileData.getOriginAnnotationForFunc( ProfileData.getLeafFuncIndex(selectedPath), + null, + frameTable, funcTable, resourceTable, stringTable, - sources + sources, + originalLocation ); } ); diff --git a/src/selectors/per-thread/stack-sample.ts b/src/selectors/per-thread/stack-sample.ts index d39239f75b..cbf3dd4430 100644 --- a/src/selectors/per-thread/stack-sample.ts +++ b/src/selectors/per-thread/stack-sample.ts @@ -155,13 +155,19 @@ export function getStackAndSampleSelectorsPerThread( threadSelectors.getFilteredThread, UrlState.getSourceViewSourceIndex, ( - { stackTable, frameTable, funcTable }: Thread, + { stackTable, frameTable, funcTable, originalLocation }: Thread, sourceIndex ): StackLineInfo | null => { if (sourceIndex === null) { return null; } - return getStackLineInfo(stackTable, frameTable, funcTable, sourceIndex); + return getStackLineInfo( + stackTable, + frameTable, + funcTable, + sourceIndex, + originalLocation + ); } ); diff --git a/src/selectors/per-thread/thread.tsx b/src/selectors/per-thread/thread.tsx index 59db24d074..cd563ffcf0 100644 --- a/src/selectors/per-thread/thread.tsx +++ b/src/selectors/per-thread/thread.tsx @@ -193,6 +193,8 @@ export function getBasicThreadSelectorsPerThread( ProfileSelectors.getStringTable, ProfileSelectors.getSourceTable, getTracedValuesBuffer, + (state: State) => + ProfileSelectors.getRawProfileSharedData(state).originalLocation, ProfileData.createThreadFromDerivedTables ); diff --git a/src/selectors/profile.ts b/src/selectors/profile.ts index ad7f8ff41c..a2ad836a10 100644 --- a/src/selectors/profile.ts +++ b/src/selectors/profile.ts @@ -70,6 +70,7 @@ import type { State, ProfileViewState, SymbolicationStatus, + SourceMapSymbolicationStatus, MarkerSchema, MarkerSchemaByName, SampleUnits, @@ -105,6 +106,9 @@ export const getProfileRootRange: Selector = (state) => getProfileViewOptions(state).rootRange; export const getSymbolicationStatus: Selector = (state) => getProfileViewOptions(state).symbolicationStatus; +export const getSourceMapSymbolicationStatus: Selector< + SourceMapSymbolicationStatus +> = (state) => getProfileViewOptions(state).sourceMapSymbolicationStatus; export const getScrollToSelectionGeneration: Selector = (state) => getProfileViewOptions(state).scrollToSelectionGeneration; export const getFocusCallTreeGeneration: Selector = (state) => @@ -1098,7 +1102,7 @@ export const getSourceViewFile: Selector = createSelector( } const fileNameStrIndex = sources.filename[sourceIndex]; - return fileNameStrIndex !== null + return fileNameStrIndex !== null && fileNameStrIndex !== undefined ? stringTable.getString(fileNameStrIndex) : null; } diff --git a/src/test/components/BottomBox.test.tsx b/src/test/components/BottomBox.test.tsx index 47e7d23e83..e8b9728770 100644 --- a/src/test/components/BottomBox.test.tsx +++ b/src/test/components/BottomBox.test.tsx @@ -135,8 +135,8 @@ describe('BottomBox', () => { const sourceViewContent = await within(sourceViewElement).findByRole('textbox'); - // Because numbers and strings are split in several element, we're matching - // on the string "line" only. + // Verify source content is rendered. Syntax highlighting splits tokens + // across spans, so we match against each cm-line's full textContent. await within(sourceViewContent).findAllByText('line'); expect(sourceViewContent).toMatchSnapshot(); diff --git a/src/test/fixtures/mocks/web-channel.ts b/src/test/fixtures/mocks/web-channel.ts index d9a8b326b6..c2bb2ca72d 100644 --- a/src/test/fixtures/mocks/web-channel.ts +++ b/src/test/fixtures/mocks/web-channel.ts @@ -143,7 +143,7 @@ export function simulateWebChannel( requestId: message.requestId, response: { menuButtonIsEnabled: true, - version: 5, + version: 7, }, }); break; @@ -210,6 +210,24 @@ export function simulateWebChannel( }); break; } + case 'GET_JS_SOURCES': { + triggerResponse({ + type: 'ERROR_RESPONSE', + requestId: message.requestId, + error: + 'GET_JS_SOURCES is a valid message but not covered by this test.', + }); + break; + } + case 'GET_SOURCE_MAP': { + triggerResponse({ + type: 'ERROR_RESPONSE', + requestId: message.requestId, + error: + 'GET_SOURCE_MAP is a valid message but not covered by this test.', + }); + break; + } default: { break; diff --git a/src/test/fixtures/node-worker.ts b/src/test/fixtures/node-worker.ts index 94f05ccb08..df9fe21247 100644 --- a/src/test/fixtures/node-worker.ts +++ b/src/test/fixtures/node-worker.ts @@ -25,6 +25,7 @@ function getWorkerScript(file: string): string { CompressionStream, Response, }; + sandbox.self = sandbox; vm.runInNewContext(scriptContent, sandbox, { filename: "${file}" }); diff --git a/src/test/fixtures/profiles/call-nodes.ts b/src/test/fixtures/profiles/call-nodes.ts index 7b68a84c87..909b48e7e7 100644 --- a/src/test/fixtures/profiles/call-nodes.ts +++ b/src/test/fixtures/profiles/call-nodes.ts @@ -49,6 +49,7 @@ export default function getProfile(): Profile { source: Array(funcNames.length).fill(null), lineNumber: Array(funcNames.length).fill(null), columnNumber: Array(funcNames.length).fill(null), + originalLocation: Array(funcNames.length).fill(null), length: funcNames.length, }; @@ -82,6 +83,7 @@ export default function getProfile(): Profile { innerWindowID: Array(frameFuncs.length).fill(null), line: Array(frameFuncs.length).fill(null), column: Array(frameFuncs.length).fill(null), + originalLocation: Array(frameFuncs.length).fill(null), length: frameFuncs.length, }; diff --git a/src/test/fixtures/profiles/processed-profile.ts b/src/test/fixtures/profiles/processed-profile.ts index 428b559836..2fa0338069 100644 --- a/src/test/fixtures/profiles/processed-profile.ts +++ b/src/test/fixtures/profiles/processed-profile.ts @@ -978,6 +978,7 @@ function _buildThreadFromTextOnlyStacks( frameTable.nativeSymbol.push(nativeSymbol); frameTable.line.push(lineNumber); frameTable.column.push(null); + frameTable.originalLocation.push(null); frameIndex = frameTable.length++; } @@ -2061,6 +2062,9 @@ export function addInnerWindowIdToStacks( frameTable.nativeSymbol.push(frameTable.nativeSymbol[foundFrameIndex]); frameTable.line.push(frameTable.line[foundFrameIndex]); frameTable.column.push(frameTable.column[foundFrameIndex]); + frameTable.originalLocation.push( + frameTable.originalLocation[foundFrameIndex] + ); // And that one comes from the second tab. frameTable.innerWindowID.push(listOfOperations[1].innerWindowID); diff --git a/src/test/fixtures/source-map.worker.stub.js b/src/test/fixtures/source-map.worker.stub.js new file mode 100644 index 0000000000..75877e977c --- /dev/null +++ b/src/test/fixtures/source-map.worker.stub.js @@ -0,0 +1,15 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Test-only stub for src/profile-logic/source-map.worker.ts. The real worker +// bundles npm dependencies (lezer, source-map) into an IIFE via esbuild, so it +// can't be loaded directly from source by the node-worker fixture. Tests that +// actually exercise the worker's logic should mock the +// `actions/source-map-symbolication` module. This stub is the fallback that +// keeps the Worker spawn from leaking ENOENT errors and hanging tests when a +// dispatch slips through. + +onmessage = () => { + postMessage({ type: 'no-op' }); +}; diff --git a/src/test/fixtures/utils.ts b/src/test/fixtures/utils.ts index 84290f4f7b..dda9390d0e 100644 --- a/src/test/fixtures/utils.ts +++ b/src/test/fixtures/utils.ts @@ -172,7 +172,8 @@ export function computeThreadFromRawThread( shared.resourceTable, stringTable, shared.sources, - tracedValuesBuffer + tracedValuesBuffer, + shared.originalLocation ); } @@ -349,17 +350,15 @@ export function formatStack( ) { const frameIndex = stackTable.frame[stackIndex]; const funcIndex = frameTable.func[frameIndex]; - const frameLine = frameTable.line[frameIndex]; - const frameColumn = frameTable.column[frameIndex]; const funcName = stringTable.getString(funcTable.name[funcIndex]); const origin = getOriginAnnotationForFunc( funcIndex, + frameIndex, + frameTable, funcTable, resourceTable, stringTable, - sources, - frameLine, - frameColumn + sources ); lines.push(`${funcName} (${origin})`); } @@ -688,6 +687,7 @@ export function addSourceToTable( sources.startLine.push(startLine); sources.startColumn.push(startColumn); sources.sourceMapURL.push(sourceMapURLStringIndex); + sources.content.push(null); sources.length = sources.filename.length; return sourceIndex; diff --git a/src/test/integration/profiler-edit/__snapshots__/profiler-edit.test.ts.snap b/src/test/integration/profiler-edit/__snapshots__/profiler-edit.test.ts.snap index 71147b11fc..0cf767e180 100644 --- a/src/test/integration/profiler-edit/__snapshots__/profiler-edit.test.ts.snap +++ b/src/test/integration/profiler-edit/__snapshots__/profiler-edit.test.ts.snap @@ -87,7 +87,7 @@ Object { "markerSchema": Array [], "oscpu": "macOS 14.6.1", "pausedRanges": Array [], - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "a.out", "sampleUnits": Object { @@ -458,6 +458,50 @@ Object { 9, 3, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ 0, 0, @@ -565,6 +609,21 @@ Object { 13, 18, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -674,6 +733,12 @@ Object { 18, ], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [ null, @@ -710,6 +775,7 @@ Object { ], }, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, @@ -1386,7 +1452,7 @@ Object { "markerSchema": Array [], "oscpu": "macOS 14.6.1", "pausedRanges": Array [], - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "a.out", "sampleUnits": Object { @@ -1757,6 +1823,50 @@ Object { 9, 3, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ 0, 0, @@ -1864,6 +1974,21 @@ Object { 13, 18, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -1973,6 +2098,12 @@ Object { 18, ], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [ null, @@ -2009,6 +2140,7 @@ Object { ], }, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, @@ -2685,7 +2817,7 @@ Object { "markerSchema": Array [], "oscpu": "macOS 14.6.1", "pausedRanges": Array [], - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "a.out", "sampleUnits": Object { @@ -3056,6 +3188,50 @@ Object { 9, 3, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ 0, 0, @@ -3163,6 +3339,21 @@ Object { 13, 18, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -3272,6 +3463,12 @@ Object { 18, ], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [ null, @@ -3308,6 +3505,7 @@ Object { ], }, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, @@ -3984,7 +4182,7 @@ Object { "markerSchema": Array [], "oscpu": "macOS 14.6.1", "pausedRanges": Array [], - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "a.out", "sampleUnits": Object { @@ -4355,6 +4553,50 @@ Object { 9, 3, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ 0, 0, @@ -4462,6 +4704,21 @@ Object { 13, 18, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -4571,6 +4828,12 @@ Object { 18, ], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [ null, @@ -4607,6 +4870,7 @@ Object { ], }, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, diff --git a/src/test/setup.ts b/src/test/setup.ts index 29cd6ea6a5..0f69e4eb7a 100644 --- a/src/test/setup.ts +++ b/src/test/setup.ts @@ -28,6 +28,13 @@ fetchMock.mockGlobal(); // for files ending in .worker.js: The "default export" is the path to the file. jest.mock('../utils/gz.worker.js', () => 'src/utils/gz.worker.js'); +// The source-map worker is normally bundled as an IIFE by esbuild because its +// dependencies (lezer, source-map) can't run as ES modules in a Web Worker +// context. In tests there is no bundle: SOURCE_MAP_WORKER_PATH is defined as +// a global in jest.config.js to point at a stub that immediately responds with +// { type: 'no-op' }. Tests that actually need to exercise the worker logic must +// mock the 'actions/source-map-symbolication' module directly. + // Install a Worker class which is similar to the DOM Worker class. (global as any).Worker = NodeWorker; diff --git a/src/test/store/__snapshots__/profile-view.test.ts.snap b/src/test/store/__snapshots__/profile-view.test.ts.snap index 6702059ad9..1b61a791df 100644 --- a/src/test/store/__snapshots__/profile-view.test.ts.snap +++ b/src/test/store/__snapshots__/profile-view.test.ts.snap @@ -423,7 +423,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "Firefox", "sourceURL": "", @@ -532,6 +532,17 @@ Object { null, null, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ 0, 0, @@ -590,6 +601,17 @@ Object { 7, 8, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -631,6 +653,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [], "length": 0, @@ -639,6 +667,7 @@ Object { "type": Array [], }, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, @@ -2470,6 +2499,17 @@ CallTree { null, null, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ 0, 0, @@ -2528,6 +2568,17 @@ CallTree { 7, 8, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -2585,6 +2636,12 @@ CallTree { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "pausedRanges": Array [], "pid": "0", "processName": undefined, @@ -2644,6 +2701,7 @@ CallTree { }, "showMarkersInTimeline": undefined, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, @@ -2829,6 +2887,17 @@ Object { null, null, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ 0, 0, @@ -2887,6 +2956,17 @@ Object { 7, 8, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -2944,6 +3024,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "pausedRanges": Array [], "pid": "0", "processName": undefined, @@ -3003,6 +3089,7 @@ Object { }, "showMarkersInTimeline": undefined, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, @@ -3266,6 +3353,17 @@ Object { null, null, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ 0, 0, @@ -3324,6 +3422,17 @@ Object { 7, 8, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -3381,6 +3490,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "pausedRanges": Array [], "pid": "0", "processName": undefined, @@ -3428,6 +3543,7 @@ Object { }, "showMarkersInTimeline": undefined, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, @@ -3611,6 +3727,17 @@ Object { null, null, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ 0, 0, @@ -3669,6 +3796,17 @@ Object { 7, 8, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -3726,6 +3864,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "pausedRanges": Array [], "pid": "0", "processName": undefined, @@ -3785,6 +3929,7 @@ Object { }, "showMarkersInTimeline": undefined, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, @@ -3968,6 +4113,17 @@ Object { null, null, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ 0, 0, @@ -4026,6 +4182,17 @@ Object { 7, 8, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -4083,6 +4250,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "pausedRanges": Array [], "pid": "0", "processName": undefined, @@ -4142,6 +4315,7 @@ Object { }, "showMarkersInTimeline": undefined, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, diff --git a/src/test/store/source-map-symbolication.test.ts b/src/test/store/source-map-symbolication.test.ts new file mode 100644 index 0000000000..4b9cf3d0fe --- /dev/null +++ b/src/test/store/source-map-symbolication.test.ts @@ -0,0 +1,467 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// End-to-end test for the receive-profile -> JS source map symbolication +// pipeline. Drives `loadProfile` with a mocked BrowserConnection that serves +// a real source map and minified bundle, then asserts on: +// - what was fetched (filtering by id / sourceMapURL / WebChannel version), +// - the status reducer transitions, +// - the post-symbolication profile state, +// - the source view selector reading the original-source content. +// +// Worker plumbing: the production worker is bundled by esbuild and replaced +// in jest.config.js with a no-op stub. For these tests we override +// global.Worker with an in-process variant that calls +// runSourceMapSymbolicationCore directly, so the real +// doSourceMapSymbolication action runs its full Redux flow +// (START_SOURCE_MAP_SYMBOLICATION -> BULK_SOURCE_MAP_SYMBOLICATION / +// SOURCE_MAP_SYMBOLICATION_FAILED) and the worker internals are exercised. + +import { SourceMapGenerator } from 'source-map'; + +import { runSourceMapSymbolicationCore } from '../../profile-logic/source-map-symbolication'; +import { loadProfile } from '../../actions/receive-profile'; +import { + getSourceMapSymbolicationStatus, + getRawProfileSharedData, +} from '../../selectors/profile'; +import { getSourceViewCode } from '../../selectors/code'; +import { stateFromLocation } from '../../app-logic/url-handling'; +import { updateUrlState } from '../../actions/app'; +import { blankStore } from '../fixtures/stores'; +import { getProfileFromTextSamples } from '../fixtures/profiles/processed-profile'; + +import type { BrowserConnection } from '../../app-logic/browser-connection'; +import type { Profile } from 'firefox-profiler/types'; +import type { + WorkerInput, + WorkerOutput, +} from '../../profile-logic/source-map-worker-types'; +import type { RawSourceMap } from 'source-map'; + +// Original source file. Indentation and blank lines matter: the mappings +// below address specific (line, column) positions. +const ORIGINAL_SOURCE = `function greet(name) { + return "Hello, " + name; +} +`; + +// Minified single-line bundle. `greet` -> `a`, `name` -> `b`. +const BUNDLE_SOURCE = 'function a(b){return"Hello, "+b}'; + +const ORIGINAL_FILENAME = 'hello.js'; + +// Build a source map for BUNDLE_SOURCE referencing ORIGINAL_SOURCE. Includes +// sourcesContent so symbolication can populate sources.content for offline +// source viewing. +function buildSourceMap(bundleFilename: string): RawSourceMap { + const gen = new SourceMapGenerator({ file: bundleFilename }); + gen.setSourceContent(ORIGINAL_FILENAME, ORIGINAL_SOURCE); + // bundle 1:0 ('function') -> original 1:0 + gen.addMapping({ + source: ORIGINAL_FILENAME, + original: { line: 1, column: 0 }, + generated: { line: 1, column: 0 }, + name: 'greet', + }); + // bundle 1:9 ('a' identifier) -> original 1:9 ('greet' identifier) + gen.addMapping({ + source: ORIGINAL_FILENAME, + original: { line: 1, column: 9 }, + generated: { line: 1, column: 9 }, + name: 'greet', + }); + // bundle 1:14 ('return') -> original 2:2 ('return' inside the body) + gen.addMapping({ + source: ORIGINAL_FILENAME, + original: { line: 2, column: 2 }, + generated: { line: 1, column: 14 }, + }); + return JSON.parse(gen.toString()); +} + +type SourceDescriptor = { + filename: string; + id: string | null; + sourceMapURL: string | null; +}; + +// Build a profile with one JS func per source descriptor. Each func is +// positioned at bundle (line 1, col 10) — the start of the identifier `a` in +// BUNDLE_SOURCE — and each frame at (line 1, col 15) — the start of the +// `return` keyword. That way every eligible source produces a successful +// symbolication when paired with buildSourceMap. +function makeProfileWithJsSources(sources: SourceDescriptor[]): Profile { + // One thread per source so each source appears as the funcTable.source of a + // visible thread's stack. + const textSamples = sources.map((s) => `Ajs[file:${s.filename}]`); + const { profile } = getProfileFromTextSamples(...textSamples); + // Skip native symbolication — we only care about JS source map + // symbolication here. + profile.meta.symbolicated = true; + + const { + funcTable, + frameTable, + sources: sourceTable, + stringArray, + } = profile.shared; + + for (const desc of sources) { + const filenameStrIdx = stringArray.indexOf(desc.filename); + const sourceIndex = sourceTable.filename.findIndex( + (f) => f === filenameStrIdx + ); + if (sourceIndex === -1) { + throw new Error(`No source row for ${desc.filename}`); + } + sourceTable.id[sourceIndex] = desc.id; + if (desc.sourceMapURL !== null) { + const urlIdx = stringArray.length; + stringArray.push(desc.sourceMapURL); + sourceTable.sourceMapURL[sourceIndex] = urlIdx; + } else { + sourceTable.sourceMapURL[sourceIndex] = null; + } + } + + // Position every func + frame inside the bundle. funcs and frames are + // co-indexed with sources in the order they were added. + for (let i = 0; i < sources.length; i++) { + funcTable.lineNumber[i] = 1; + funcTable.columnNumber[i] = 10; + frameTable.line[i] = 1; + frameTable.column[i] = 15; + } + + return profile; +} + +type MockBrowserConnection = BrowserConnection & { + getSourceMap: jest.Mock; + getJSSource: jest.Mock; +}; + +// Build a BrowserConnection that serves the given source map and bundle +// fixtures. WebChannel version 7+ enables source-map fetching in +// finalizeProfileView; v6 disables it. +function makeMockBrowserConnection(opts: { + supportsSourceMapFetching: boolean; + sourceMapsById?: Map; + jsSourcesById?: Map; +}): MockBrowserConnection { + const sourceMapsById = opts.sourceMapsById ?? new Map(); + const jsSourcesById = opts.jsSourcesById ?? new Map(); + return { + supportsGetSourceMap: opts.supportsSourceMapFetching, + getSourceMap: jest.fn(async (id: string) => { + const map = sourceMapsById.get(id); + if (!map) { + throw new Error(`No source map fixture for "${id}"`); + } + return map; + }), + getJSSource: jest.fn(async (id: string) => { + const src = jsSourcesById.get(id); + if (src === undefined) { + throw new Error(`No JS source fixture for "${id}"`); + } + return src; + }), + getProfile: jest.fn(), + getExternalMarkers: jest.fn(), + getExternalPowerTracks: jest.fn(), + querySymbolicationApi: jest.fn(), + getSymbolTable: jest.fn(), + getPageFavicons: jest.fn(), + showFunctionInDevtools: jest.fn(), + } as unknown as MockBrowserConnection; +} + +// In-process replacement for global.Worker that runs the source-map worker +// core directly. See file-top comment for context. +class InProcessSourceMapWorker { + onmessage: ((event: { data: WorkerOutput }) => void) | null = null; + onerror: ((event: ErrorEvent) => void) | null = null; + + postMessage(input: WorkerInput): void { + runSourceMapSymbolicationCore(input, 'ignored-in-node').then((output) => { + if (this.onmessage) { + this.onmessage({ data: output }); + } + }); + } + + terminate(): void {} +} + +describe('receive-profile -> JS source map symbolication', function () { + let savedWorker: unknown; + + beforeEach(function () { + savedWorker = (global as any).Worker; + (global as any).Worker = InProcessSourceMapWorker; + }); + + afterEach(function () { + (global as any).Worker = savedWorker; + }); + + describe('fetching and filtering', function () { + it('fetches source maps and bundle sources for every source with a UUID and sourceMapURL', async function () { + const profile = makeProfileWithJsSources([ + { + filename: 'bundle-a.js', + id: 'uuid-a', + sourceMapURL: 'https://example.com/bundle-a.js.map', + }, + { + filename: 'bundle-b.js', + id: 'uuid-b', + sourceMapURL: 'https://example.com/bundle-b.js.map', + }, + ]); + + const browserConnection = makeMockBrowserConnection({ + supportsSourceMapFetching: true, + sourceMapsById: new Map([ + ['uuid-a', buildSourceMap('bundle-a.js')], + ['uuid-b', buildSourceMap('bundle-b.js')], + ]), + jsSourcesById: new Map([ + ['uuid-a', BUNDLE_SOURCE], + ['uuid-b', BUNDLE_SOURCE], + ]), + }); + + const { dispatch, getState } = blankStore(); + await dispatch(loadProfile(profile, { browserConnection })); + + expect( + browserConnection.getSourceMap.mock.calls.map((c) => c[0]) + ).toEqual(expect.arrayContaining(['uuid-a', 'uuid-b'])); + expect(browserConnection.getJSSource.mock.calls.map((c) => c[0])).toEqual( + expect.arrayContaining(['uuid-a', 'uuid-b']) + ); + + // Symbolication actually ran: both funcs are now named `greet`. + const { funcTable, stringArray } = getRawProfileSharedData(getState()); + expect(stringArray[funcTable.name[0]]).toBe('greet'); + expect(stringArray[funcTable.name[1]]).toBe('greet'); + }); + + it('does not fetch source maps when the browser lacks source-map support', async function () { + const profile = makeProfileWithJsSources([ + { + filename: 'bundle.js', + id: 'uuid-x', + sourceMapURL: 'https://example.com/bundle.js.map', + }, + ]); + const browserConnection = makeMockBrowserConnection({ + supportsSourceMapFetching: false, + }); + + const { dispatch, getState } = blankStore(); + await dispatch(loadProfile(profile, { browserConnection })); + + expect(browserConnection.getSourceMap).not.toHaveBeenCalled(); + expect(browserConnection.getJSSource).not.toHaveBeenCalled(); + + // No symbolication: the bundled name stays as-is. + const { funcTable, stringArray } = getRawProfileSharedData(getState()); + expect(stringArray[funcTable.name[0]]).toBe('Ajs'); + }); + + it('does not fetch sources without an id or without a sourceMapURL', async function () { + // - has-id-no-map: id set but no sourceMapURL → skip + // - has-map-no-id: sourceMapURL set but null id → skip + // - both: should be fetched + const profile = makeProfileWithJsSources([ + { + filename: 'has-id-no-map.js', + id: 'uuid-1', + sourceMapURL: null, + }, + { + filename: 'has-map-no-id.js', + id: null, + sourceMapURL: 'https://example.com/has-map-no-id.js.map', + }, + { + filename: 'both.js', + id: 'uuid-3', + sourceMapURL: 'https://example.com/both.js.map', + }, + ]); + + const browserConnection = makeMockBrowserConnection({ + supportsSourceMapFetching: true, + sourceMapsById: new Map([['uuid-3', buildSourceMap('both.js')]]), + jsSourcesById: new Map([['uuid-3', BUNDLE_SOURCE]]), + }); + + const { dispatch, getState } = blankStore(); + await dispatch(loadProfile(profile, { browserConnection })); + + expect( + browserConnection.getSourceMap.mock.calls.map((c) => c[0]) + ).toEqual(['uuid-3']); + + // Only the eligible source got symbolicated. + const { funcTable, stringArray } = getRawProfileSharedData(getState()); + expect(stringArray[funcTable.name[0]]).toBe('Ajs'); + expect(stringArray[funcTable.name[1]]).toBe('Ajs'); + expect(stringArray[funcTable.name[2]]).toBe('greet'); + }); + + it('silently continues when getSourceMap or getJSSource fails', async function () { + const profile = makeProfileWithJsSources([ + { + filename: 'broken.js', + id: 'uuid-broken', + sourceMapURL: 'https://example.com/broken.js.map', + }, + ]); + // No fixtures registered: getSourceMap and getJSSource throw, but the + // catches in doResolveSourceMaps swallow them. + const browserConnection = makeMockBrowserConnection({ + supportsSourceMapFetching: true, + }); + + // Silence the expected warnings. + jest.spyOn(console, 'warn').mockImplementation(() => {}); + + const { dispatch, getState } = blankStore(); + await dispatch(loadProfile(profile, { browserConnection })); + + expect(browserConnection.getSourceMap).toHaveBeenCalledWith( + 'uuid-broken' + ); + expect(browserConnection.getJSSource).toHaveBeenCalledWith('uuid-broken'); + + // No symbolication applied — the load completes cleanly with the func + // unchanged. + const { funcTable, stringArray } = getRawProfileSharedData(getState()); + expect(stringArray[funcTable.name[0]]).toBe('Ajs'); + expect(getSourceMapSymbolicationStatus(getState())).toBe('INACTIVE'); + }); + }); + + describe('status transitions', function () { + it('moves through FETCHING and SYMBOLICATING before settling back to INACTIVE', async function () { + const profile = makeProfileWithJsSources([ + { + filename: 'bundle.js', + id: 'uuid-x', + sourceMapURL: 'https://example.com/bundle.js.map', + }, + ]); + const browserConnection = makeMockBrowserConnection({ + supportsSourceMapFetching: true, + sourceMapsById: new Map([['uuid-x', buildSourceMap('bundle.js')]]), + jsSourcesById: new Map([['uuid-x', BUNDLE_SOURCE]]), + }); + + const store = blankStore(); + // Record every distinct sourceMapSymbolicationStatus value the store + // passes through. + const statuses: string[] = [ + getSourceMapSymbolicationStatus(store.getState()), + ]; + store.subscribe(() => { + const current = getSourceMapSymbolicationStatus(store.getState()); + if (statuses[statuses.length - 1] !== current) { + statuses.push(current); + } + }); + + await store.dispatch(loadProfile(profile, { browserConnection })); + + // FETCHING is set when fetching starts and reverted to INACTIVE on + // DONE_SOURCE_MAP_FETCHING; SYMBOLICATING is set when the worker starts + // and cleared by BULK_SOURCE_MAP_SYMBOLICATION on success. + expect(statuses).toEqual([ + 'INACTIVE', + 'FETCHING', + 'INACTIVE', + 'SYMBOLICATING', + 'INACTIVE', + ]); + }); + }); + + describe('post-symbolication profile state', function () { + async function loadAndSymbolicate() { + const profile = makeProfileWithJsSources([ + { + filename: 'bundle.js', + id: 'uuid-x', + sourceMapURL: 'https://example.com/bundle.js.map', + }, + ]); + const browserConnection = makeMockBrowserConnection({ + supportsSourceMapFetching: true, + sourceMapsById: new Map([['uuid-x', buildSourceMap('bundle.js')]]), + jsSourcesById: new Map([['uuid-x', BUNDLE_SOURCE]]), + }); + const { dispatch, getState } = blankStore(); + await dispatch(loadProfile(profile, { browserConnection })); + return { dispatch, getState }; + } + + it('renames the minified function to its original identifier', async function () { + const { getState } = await loadAndSymbolicate(); + const { funcTable, stringArray } = getRawProfileSharedData(getState()); + // The bundle defined `function a(b)`. After symbolication, the scope + // tree on the original source recovers the pre-minified name. + expect(stringArray[funcTable.name[0]]).toBe('greet'); + }); + + it('remaps the frame execution position to the original source', async function () { + const { getState } = await loadAndSymbolicate(); + const { frameTable, originalLocation, sources, stringArray } = + getRawProfileSharedData(getState()); + + const frameOriginalLocationIdx = frameTable.originalLocation[0]; + expect(frameOriginalLocationIdx).not.toBeNull(); + const originalSourceIdx = + originalLocation.source[frameOriginalLocationIdx!]; + expect(stringArray[sources.filename[originalSourceIdx]]).toBe( + ORIGINAL_FILENAME + ); + // bundle 1:15 ('return') maps to hello.js 2:3 (1-based). + expect(originalLocation.line[frameOriginalLocationIdx!]).toBe(2); + expect(originalLocation.column[frameOriginalLocationIdx!]).toBe(3); + }); + + it('returns the original source content via the source view selector', async function () { + const { dispatch, getState } = await loadAndSymbolicate(); + + // The original source was appended to the sources table during + // symbolication. Find its index by filename. + const { sources, stringArray } = getRawProfileSharedData(getState()); + const originalSourceIndex = sources.filename.findIndex( + (idx) => stringArray[idx] === ORIGINAL_FILENAME + ); + expect(originalSourceIndex).toBeGreaterThanOrEqual(0); + + // Point the source view URL state at the original source. + dispatch( + updateUrlState( + stateFromLocation({ + pathname: '/public/fakehash/', + search: `?sourceViewIndex=${originalSourceIndex}`, + hash: '', + }) + ) + ); + + expect(getSourceViewCode(getState())).toEqual({ + type: 'AVAILABLE', + code: ORIGINAL_SOURCE, + }); + }); + }); +}); diff --git a/src/test/store/transforms.test.ts b/src/test/store/transforms.test.ts index c99a1953de..76e678f978 100644 --- a/src/test/store/transforms.test.ts +++ b/src/test/store/transforms.test.ts @@ -1436,7 +1436,8 @@ describe('"collapse-direct-recursion" transform', function () { stackTable, frameTable, funcTable, - fileSourceIndex + fileSourceIndex, + filteredThread.originalLocation ); const lineTimings = getLineTimings(stackLineInfo, samples); @@ -1620,7 +1621,8 @@ describe('"collapse-recursion" transform', function () { stackTable, frameTable, funcTable, - fileSourceIndex + fileSourceIndex, + filteredThread.originalLocation ); const lineTimings = getLineTimings(stackLineInfo, samples); @@ -1733,7 +1735,8 @@ describe('"collapse-recursion" transform', function () { stackTable, frameTable, funcTable, - fileSourceIndex + fileSourceIndex, + filteredThread.originalLocation ); const lineTimings = getLineTimings(stackLineInfo, samples); @@ -1869,7 +1872,8 @@ describe('"collapse-recursion" transform', function () { stackTable, frameTable, funcTable, - fileSourceIndex + fileSourceIndex, + filteredThread.originalLocation ); const lineTimings = getLineTimings(stackLineInfo, samples); diff --git a/src/test/unit/__snapshots__/profile-conversion.test.ts.snap b/src/test/unit/__snapshots__/profile-conversion.test.ts.snap index 6c0983e811..ffcd31caf7 100644 --- a/src/test/unit/__snapshots__/profile-conversion.test.ts.snap +++ b/src/test/unit/__snapshots__/profile-conversion.test.ts.snap @@ -591,7 +591,7 @@ Object { "oscpu": undefined, "physicalCPUs": undefined, "platform": undefined, - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "ART Trace (Android)", "sampleUnits": undefined, @@ -22118,2697 +22118,11 @@ Object { null, null, ], - "subcategory": Array [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + "originalLocation": Array [ null, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, null, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, null, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, null, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - }, - "funcTable": Object { - "columnNumber": Array [ null, null, null, @@ -26753,3161 +24067,6 @@ Object { null, null, null, - ], - "isJS": Array [ - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - ], - "length": 1944, - "lineNumber": Array [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, null, null, null, @@ -30647,947 +24806,9476 @@ Object { null, null, ], - "name": Array [ + "subcategory": Array [ 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 71, - 72, - 73, - 74, - 75, - 76, - 77, - 78, - 79, - 80, - 81, - 82, - 83, - 84, - 85, - 86, - 87, - 88, - 89, - 90, - 91, - 92, - 93, - 94, - 95, - 96, - 97, - 98, - 99, - 100, - 101, - 102, - 103, - 104, - 105, - 106, - 107, - 108, - 109, - 110, - 111, - 112, - 113, - 114, - 115, - 116, - 117, - 118, - 119, - 120, - 121, - 122, - 123, - 124, - 125, - 126, - 127, - 128, - 129, - 130, - 131, - 132, - 133, - 134, - 135, - 136, - 137, - 138, - 139, - 140, - 141, - 142, - 143, - 144, - 145, - 146, - 147, - 148, - 149, - 150, - 151, - 152, - 153, - 154, - 155, - 156, - 157, - 158, - 159, - 160, - 161, - 162, - 163, - 164, - 165, - 166, - 167, - 168, - 169, - 170, - 171, - 172, - 173, - 174, - 175, - 176, - 177, - 178, - 179, - 180, - 181, - 182, - 183, - 184, - 185, - 186, - 187, - 188, - 189, - 190, - 191, - 192, - 193, - 194, - 195, - 196, - 197, - 198, - 199, - 200, - 201, - 202, - 203, - 204, - 205, - 206, - 207, - 208, - 209, - 210, - 211, - 212, - 213, - 214, - 215, - 216, - 217, - 218, - 219, - 220, - 221, - 222, - 223, - 224, - 225, - 226, - 227, - 228, - 229, - 230, - 231, - 232, - 233, - 234, - 235, - 236, - 237, - 238, - 239, - 240, - 241, - 242, - 243, - 244, - 245, - 246, - 247, - 248, - 249, - 250, - 251, - 252, - 253, - 254, - 255, - 256, - 257, - 258, - 259, - 260, - 261, - 262, - 263, - 264, - 265, - 266, - 267, - 268, - 269, - 270, - 271, - 272, - 273, - 274, - 275, - 276, - 277, - 278, - 279, - 280, - 281, - 282, - 283, - 284, - 285, - 286, - 287, - 288, - 289, - 290, - 291, - 292, - 293, - 294, - 295, - 296, - 297, - 298, - 299, - 300, - 301, - 302, - 303, - 304, - 305, - 306, - 307, - 308, - 309, - 310, - 311, - 312, - 313, - 314, - 315, - 316, - 317, - 318, - 319, - 320, - 321, - 322, - 323, - 324, - 325, - 326, - 327, - 328, - 329, - 330, - 331, - 332, - 333, - 334, - 335, - 336, - 337, - 338, - 339, - 340, - 341, - 342, - 343, - 344, - 345, - 346, - 347, - 348, - 349, - 350, - 351, - 352, - 353, - 354, - 355, - 356, - 357, - 358, - 359, - 360, - 361, - 362, - 363, - 364, - 365, - 366, - 367, - 368, - 369, - 370, - 371, - 372, - 373, - 374, - 375, - 376, - 377, - 378, - 379, - 380, - 381, - 382, - 383, - 384, - 385, - 386, - 387, - 388, - 389, - 390, - 391, - 392, - 393, - 394, - 395, - 396, - 397, - 398, - 399, - 400, - 401, - 402, - 403, - 404, - 405, - 406, - 407, - 408, - 409, - 410, - 411, - 412, - 413, - 414, - 415, - 416, - 417, - 418, - 419, - 420, - 421, - 422, - 423, - 424, - 425, - 426, - 427, - 428, - 429, - 430, - 431, - 432, - 433, - 434, - 435, - 436, - 437, - 438, - 439, - 440, - 441, - 442, - 443, - 444, - 445, - 446, - 447, - 448, - 449, - 450, - 451, - 452, - 453, - 454, - 455, - 456, - 457, - 458, - 459, - 460, - 461, - 462, - 463, - 464, - 465, - 466, - 467, - 468, - 469, - 470, - 471, - 472, - 473, - 474, - 475, - 476, - 477, - 478, - 479, - 480, - 481, - 482, - 483, - 484, - 485, - 486, - 487, - 488, - 489, - 490, - 491, - 492, - 493, - 494, - 495, - 496, - 497, - 498, - 499, - 500, - 501, - 502, - 503, - 504, - 505, - 506, - 507, - 508, - 509, - 510, - 511, - 512, - 513, - 514, - 515, - 516, - 517, - 518, - 519, - 520, - 521, - 522, - 523, - 524, - 525, - 526, - 527, - 528, - 529, - 530, - 531, - 532, - 533, - 534, - 535, - 536, - 537, - 538, - 539, - 540, - 541, - 542, - 543, - 544, - 545, - 546, - 547, - 548, - 549, - 550, - 551, - 552, - 553, - 554, - 555, - 556, - 557, - 558, - 559, - 560, - 561, - 562, - 563, - 564, - 565, - 566, - 567, - 568, - 569, - 570, - 571, - 572, - 573, - 574, - 575, - 576, - 577, - 578, - 579, - 580, - 581, - 582, - 583, - 584, - 585, - 586, - 587, - 588, - 589, - 590, - 591, - 592, - 593, - 594, - 595, - 596, - 597, - 598, - 599, - 600, - 601, - 602, - 603, - 604, - 605, - 606, - 607, - 608, - 609, - 610, - 611, - 612, - 613, - 614, - 615, - 616, - 617, - 618, - 619, - 620, - 621, - 622, - 623, - 624, - 625, - 626, - 627, - 628, - 629, - 630, - 631, - 632, - 633, - 634, - 635, - 636, - 637, - 638, - 639, - 640, - 641, - 642, - 643, - 644, - 645, - 646, - 647, - 648, - 649, - 650, - 651, - 652, - 653, - 654, - 655, - 656, - 657, - 658, - 659, - 660, - 661, - 662, - 663, - 664, - 665, - 666, - 667, - 668, - 669, - 670, - 671, - 672, - 673, - 674, - 675, - 676, - 677, - 678, - 679, - 680, - 681, - 682, - 683, - 684, - 685, - 686, - 687, - 688, - 689, - 690, - 691, - 692, - 693, - 694, - 695, - 696, - 697, - 698, - 699, - 700, - 701, - 702, - 703, - 704, - 705, - 706, - 707, - 708, - 709, - 710, - 711, - 712, - 713, - 714, - 715, - 716, - 717, - 718, - 719, - 720, - 721, - 722, - 723, - 724, - 725, - 726, - 727, - 728, - 729, - 730, - 731, - 732, - 733, - 734, - 735, - 736, - 737, - 738, - 739, - 740, - 741, - 742, - 743, - 744, - 745, - 746, - 747, - 748, - 749, - 750, - 751, - 752, - 753, - 754, - 755, - 756, - 757, - 758, - 759, - 760, - 761, - 762, - 763, - 764, - 765, - 766, - 767, - 768, - 769, - 770, - 771, - 772, - 773, - 774, - 775, - 776, - 777, - 778, - 779, - 780, - 781, - 782, - 783, - 784, - 785, - 786, - 787, - 788, - 789, - 790, - 791, - 792, - 793, - 794, - 795, - 796, - 797, - 798, - 799, - 800, - 801, - 802, - 803, - 804, - 805, - 806, - 807, - 808, - 809, - 810, - 811, - 812, - 813, - 814, - 815, - 816, - 817, - 818, - 819, - 820, - 821, - 822, - 823, - 824, - 825, - 826, - 827, - 828, - 829, - 830, - 831, - 832, - 833, - 834, - 835, - 836, - 837, - 838, - 839, - 840, - 841, - 842, - 843, - 844, - 845, - 846, - 847, - 848, - 849, - 850, - 851, - 852, - 853, - 854, - 855, - 856, - 857, - 858, - 859, - 860, - 861, - 862, - 863, - 864, - 865, - 866, - 867, - 868, - 869, - 870, - 871, - 872, - 873, - 874, - 875, - 876, - 877, - 878, - 879, - 880, - 881, - 882, - 883, - 884, - 885, - 886, - 887, - 888, - 889, - 890, - 891, - 892, - 893, - 894, - 895, - 896, - 897, - 898, - 899, - 900, - 901, - 902, - 903, - 904, - 905, - 906, - 907, - 908, - 909, - 910, - 911, - 912, - 913, - 914, - 915, - 916, - 917, - 918, - 919, - 920, - 921, - 922, - 923, - 924, - 925, - 926, - 927, - 928, - 929, - 930, - 931, - 932, - 933, - 934, - 935, - 936, - 937, - 938, - 939, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + null, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + null, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + null, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + null, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + }, + "funcTable": Object { + "columnNumber": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], + "isJS": Array [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + ], + "length": 1944, + "lineNumber": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], + "name": Array [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255, + 256, + 257, + 258, + 259, + 260, + 261, + 262, + 263, + 264, + 265, + 266, + 267, + 268, + 269, + 270, + 271, + 272, + 273, + 274, + 275, + 276, + 277, + 278, + 279, + 280, + 281, + 282, + 283, + 284, + 285, + 286, + 287, + 288, + 289, + 290, + 291, + 292, + 293, + 294, + 295, + 296, + 297, + 298, + 299, + 300, + 301, + 302, + 303, + 304, + 305, + 306, + 307, + 308, + 309, + 310, + 311, + 312, + 313, + 314, + 315, + 316, + 317, + 318, + 319, + 320, + 321, + 322, + 323, + 324, + 325, + 326, + 327, + 328, + 329, + 330, + 331, + 332, + 333, + 334, + 335, + 336, + 337, + 338, + 339, + 340, + 341, + 342, + 343, + 344, + 345, + 346, + 347, + 348, + 349, + 350, + 351, + 352, + 353, + 354, + 355, + 356, + 357, + 358, + 359, + 360, + 361, + 362, + 363, + 364, + 365, + 366, + 367, + 368, + 369, + 370, + 371, + 372, + 373, + 374, + 375, + 376, + 377, + 378, + 379, + 380, + 381, + 382, + 383, + 384, + 385, + 386, + 387, + 388, + 389, + 390, + 391, + 392, + 393, + 394, + 395, + 396, + 397, + 398, + 399, + 400, + 401, + 402, + 403, + 404, + 405, + 406, + 407, + 408, + 409, + 410, + 411, + 412, + 413, + 414, + 415, + 416, + 417, + 418, + 419, + 420, + 421, + 422, + 423, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437, + 438, + 439, + 440, + 441, + 442, + 443, + 444, + 445, + 446, + 447, + 448, + 449, + 450, + 451, + 452, + 453, + 454, + 455, + 456, + 457, + 458, + 459, + 460, + 461, + 462, + 463, + 464, + 465, + 466, + 467, + 468, + 469, + 470, + 471, + 472, + 473, + 474, + 475, + 476, + 477, + 478, + 479, + 480, + 481, + 482, + 483, + 484, + 485, + 486, + 487, + 488, + 489, + 490, + 491, + 492, + 493, + 494, + 495, + 496, + 497, + 498, + 499, + 500, + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520, + 521, + 522, + 523, + 524, + 525, + 526, + 527, + 528, + 529, + 530, + 531, + 532, + 533, + 534, + 535, + 536, + 537, + 538, + 539, + 540, + 541, + 542, + 543, + 544, + 545, + 546, + 547, + 548, + 549, + 550, + 551, + 552, + 553, + 554, + 555, + 556, + 557, + 558, + 559, + 560, + 561, + 562, + 563, + 564, + 565, + 566, + 567, + 568, + 569, + 570, + 571, + 572, + 573, + 574, + 575, + 576, + 577, + 578, + 579, + 580, + 581, + 582, + 583, + 584, + 585, + 586, + 587, + 588, + 589, + 590, + 591, + 592, + 593, + 594, + 595, + 596, + 597, + 598, + 599, + 600, + 601, + 602, + 603, + 604, + 605, + 606, + 607, + 608, + 609, + 610, + 611, + 612, + 613, + 614, + 615, + 616, + 617, + 618, + 619, + 620, + 621, + 622, + 623, + 624, + 625, + 626, + 627, + 628, + 629, + 630, + 631, + 632, + 633, + 634, + 635, + 636, + 637, + 638, + 639, + 640, + 641, + 642, + 643, + 644, + 645, + 646, + 647, + 648, + 649, + 650, + 651, + 652, + 653, + 654, + 655, + 656, + 657, + 658, + 659, + 660, + 661, + 662, + 663, + 664, + 665, + 666, + 667, + 668, + 669, + 670, + 671, + 672, + 673, + 674, + 675, + 676, + 677, + 678, + 679, + 680, + 681, + 682, + 683, + 684, + 685, + 686, + 687, + 688, + 689, + 690, + 691, + 692, + 693, + 694, + 695, + 696, + 697, + 698, + 699, + 700, + 701, + 702, + 703, + 704, + 705, + 706, + 707, + 708, + 709, + 710, + 711, + 712, + 713, + 714, + 715, + 716, + 717, + 718, + 719, + 720, + 721, + 722, + 723, + 724, + 725, + 726, + 727, + 728, + 729, + 730, + 731, + 732, + 733, + 734, + 735, + 736, + 737, + 738, + 739, + 740, + 741, + 742, + 743, + 744, + 745, + 746, + 747, + 748, + 749, + 750, + 751, + 752, + 753, + 754, + 755, + 756, + 757, + 758, + 759, + 760, + 761, + 762, + 763, + 764, + 765, + 766, + 767, + 768, + 769, + 770, + 771, + 772, + 773, + 774, + 775, + 776, + 777, + 778, + 779, + 780, + 781, + 782, + 783, + 784, + 785, + 786, + 787, + 788, + 789, + 790, + 791, + 792, + 793, + 794, + 795, + 796, + 797, + 798, + 799, + 800, + 801, + 802, + 803, + 804, + 805, + 806, + 807, + 808, + 809, + 810, + 811, + 812, + 813, + 814, + 815, + 816, + 817, + 818, + 819, + 820, + 821, + 822, + 823, + 824, + 825, + 826, + 827, + 828, + 829, + 830, + 831, + 832, + 833, + 834, + 835, + 836, + 837, + 838, + 839, + 840, + 841, + 842, + 843, + 844, + 845, + 846, + 847, + 848, + 849, + 850, + 851, + 852, + 853, + 854, + 855, + 856, + 857, + 858, + 859, + 860, + 861, + 862, + 863, + 864, + 865, + 866, + 867, + 868, + 869, + 870, + 871, + 872, + 873, + 874, + 875, + 876, + 877, + 878, + 879, + 880, + 881, + 882, + 883, + 884, + 885, + 886, + 887, + 888, + 889, + 890, + 891, + 892, + 893, + 894, + 895, + 896, + 897, + 898, + 899, + 900, + 901, + 902, + 903, + 904, + 905, + 906, + 907, + 908, + 909, + 910, + 911, + 912, + 913, + 914, + 915, + 916, + 917, + 918, + 919, + 920, + 921, + 922, + 923, + 924, + 925, + 926, + 927, + 928, + 929, + 930, + 931, + 932, + 933, + 934, + 935, + 936, + 937, + 938, + 939, 940, 941, 942, @@ -32593,6 +35281,1952 @@ Object { 1942, 1943, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -38439,6 +43073,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [], "length": 0, @@ -38447,6 +43087,7 @@ Object { "type": Array [], }, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, @@ -79192,7 +83833,7 @@ Object { "oscpu": undefined, "physicalCPUs": undefined, "platform": undefined, - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "ART Trace (Android)", "sampleUnits": undefined, @@ -111698,8 +116339,5422 @@ Object { 0, 0, ], - "length": 5412, - "line": Array [ + "length": 5412, + "line": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], + "nativeSymbol": Array [ null, null, null, @@ -117113,7 +127168,7 @@ Object { null, null, ], - "nativeSymbol": Array [ + "originalLocation": Array [ null, null, null, @@ -142560,6 +152615,3660 @@ Object { 3650, 3651, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -153530,6 +167239,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [], "length": 0, @@ -153538,6 +167253,7 @@ Object { "type": Array [], }, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, @@ -315562,7 +329278,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "Chrome Trace", "profilingEndTime": 119159778.026, @@ -315775,6 +329491,34 @@ Object { null, null, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ 0, 0, @@ -315914,6 +329658,33 @@ Object { 26, 27, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -316003,6 +329774,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [ 4, @@ -316019,6 +329796,9 @@ Object { ], }, "sources": Object { + "content": Array [ + null, + ], "filename": Array [ 2, ], @@ -350479,7 +364259,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "Chrome Trace", "profilingEndTime": 119159778.026, @@ -350692,6 +364472,34 @@ Object { null, null, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ 0, 0, @@ -350831,6 +364639,33 @@ Object { 26, 27, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -350920,6 +364755,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [ 4, @@ -350936,6 +364777,9 @@ Object { ], }, "sources": Object { + "content": Array [ + null, + ], "filename": Array [ 2, ], @@ -385375,7 +399219,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "Chrome Trace", "profilingEndTime": 66155012.423, @@ -385735,6 +399579,55 @@ Object { null, null, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ 0, 0, @@ -385959,6 +399852,49 @@ Object { 20, 35, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -386096,6 +400032,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [ 5, @@ -386112,6 +400054,11 @@ Object { ], }, "sources": Object { + "content": Array [ + null, + null, + null, + ], "filename": Array [ 3, 7, @@ -387980,7 +401927,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "Chrome Trace", "sourceURL": "", @@ -388002,6 +401949,7 @@ Object { "length": 0, "line": Array [], "nativeSymbol": Array [], + "originalLocation": Array [], "subcategory": Array [], }, "funcTable": Object { @@ -388010,6 +401958,7 @@ Object { "length": 0, "lineNumber": Array [], "name": Array [], + "originalLocation": Array [], "relevantForJS": Array [], "resource": Array [], "source": Array [], @@ -388021,6 +401970,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [], "length": 0, @@ -388029,6 +401984,7 @@ Object { "type": Array [], }, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, @@ -389809,7 +403765,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "Chrome Trace", "profilingEndTime": 355035987.653, @@ -390988,6 +404944,172 @@ Object { null, null, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ 0, 0, @@ -391633,6 +405755,125 @@ Object { 136, 138, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -391998,6 +406239,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [ null, @@ -392190,6 +406437,53 @@ Object { ], }, "sources": Object { + "content": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "filename": Array [ 3, 5, @@ -393482,7 +407776,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "Chrome Trace", "sourceURL": "", @@ -395128,6 +409422,239 @@ Object { null, null, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ 0, 0, @@ -395940,6 +410467,150 @@ Object { 117, 118, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -396380,6 +411051,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [ null, @@ -396484,6 +411161,31 @@ Object { ], }, "sources": Object { + "content": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "filename": Array [ 2, 4, @@ -398697,7 +413399,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "Chrome Trace", "profilingEndTime": 66155012.423, @@ -399057,6 +413759,55 @@ Object { null, null, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ 0, 0, @@ -399281,6 +414032,49 @@ Object { 20, 35, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -399418,6 +414212,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [ 5, @@ -399434,6 +414234,11 @@ Object { ], }, "sources": Object { + "content": Array [ + null, + null, + null, + ], "filename": Array [ 3, 7, @@ -399739,7 +414544,7 @@ Object { "oscpu": undefined, "physicalCPUs": undefined, "platform": undefined, - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "Firefox", "sampleUnits": undefined, @@ -401394,6 +416199,210 @@ Object { null, null, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ null, null, @@ -401897,6 +416906,80 @@ Object { 151, 153, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -402127,6 +417210,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [ null, @@ -402187,6 +417276,7 @@ Object { ], }, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, @@ -403614,7 +418704,7 @@ Object { "oscpu": undefined, "physicalCPUs": undefined, "platform": undefined, - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "Firefox", "sampleUnits": undefined, @@ -408533,6 +423623,618 @@ Object { null, null, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ null, null, @@ -411596,6 +427298,618 @@ Object { 1227, 1229, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -413440,6 +429754,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [ null, @@ -413572,6 +429892,7 @@ Object { ], }, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, @@ -418512,7 +434833,7 @@ Object { "oscpu": undefined, "physicalCPUs": undefined, "platform": undefined, - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "Firefox", "sampleUnits": undefined, @@ -420383,6 +436704,237 @@ Object { null, null, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ null, null, @@ -421541,6 +438093,237 @@ Object { 458, 460, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -422242,6 +439025,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [ null, @@ -422278,6 +439067,7 @@ Object { ], }, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, @@ -424719,7 +441509,7 @@ Object { "oscpu": undefined, "physicalCPUs": undefined, "platform": undefined, - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "Firefox", "sampleUnits": undefined, @@ -431174,6 +447964,810 @@ Object { null, null, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ null, null, @@ -434561,6 +452155,651 @@ Object { 1313, 1315, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -436504,6 +454743,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [ null, @@ -436604,6 +454849,7 @@ Object { ], }, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, @@ -441164,7 +459410,7 @@ Object { "keepProfileThreadOrder": true, "markerSchema": Array [], "platform": "Android", - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "com.example.sampleapplication", "sourceCodeIsNotOnSearchfox": true, @@ -452252,7 +470498,2223 @@ Object { 0, 0, ], - "innerWindowID": Array [ + "innerWindowID": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], + "length": 2213, + "line": Array [ null, null, null, @@ -454467,8 +474929,7 @@ Object { null, null, ], - "length": 2213, - "line": Array [ + "nativeSymbol": Array [ null, null, null, @@ -456683,7 +477144,7 @@ Object { null, null, ], - "nativeSymbol": Array [ + "originalLocation": Array [ null, null, null, @@ -469976,6 +490437,2221 @@ Object { 2269, 2270, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -476629,6 +499305,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [ null, @@ -476901,6 +499583,7 @@ Object { ], }, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, @@ -498819,7 +521502,7 @@ Object { "keepProfileThreadOrder": true, "markerSchema": Array [], "platform": "Android", - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "com.example.sampleapplication", "sourceCodeIsNotOnSearchfox": true, @@ -509227,7 +531910,2087 @@ Object { 0, 0, ], - "innerWindowID": Array [ + "innerWindowID": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], + "length": 2077, + "line": Array [ null, null, null, @@ -511306,8 +536069,7 @@ Object { null, null, ], - "length": 2077, - "line": Array [ + "nativeSymbol": Array [ null, null, null, @@ -513386,7 +538148,7 @@ Object { null, null, ], - "nativeSymbol": Array [ + "originalLocation": Array [ null, null, null, @@ -525863,6 +550625,2085 @@ Object { 2110, 2111, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -532108,6 +558949,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [ null, @@ -532276,6 +559123,7 @@ Object { ], }, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, @@ -561079,7 +587927,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "target/debug/examples/work_log (dhat)", "sourceURL": "", @@ -564549,6 +591397,438 @@ Object { null, null, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ 0, 0, @@ -565844,6 +593124,221 @@ Object { 86, 51, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -566497,6 +593992,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [], "length": 0, @@ -566505,6 +594006,50 @@ Object { "type": Array [], }, "sources": Object { + "content": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "filename": Array [ 0, 3, @@ -569632,7 +597177,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "Flamegraph", "sourceURL": "", @@ -570894,6 +598439,162 @@ Object { null, null, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ 0, 0, @@ -571677,6 +599378,162 @@ Object { 152, 153, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -572153,6 +600010,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [], "length": 0, @@ -572161,6 +600024,7 @@ Object { "type": Array [], }, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, @@ -877518,7 +905382,7 @@ Object { "oscpu": "", "physicalCPUs": 0, "platform": "", - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "Flamegraph", "sourceURL": "", @@ -877588,6 +905452,13 @@ Object { null, null, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + ], "subcategory": Array [ 0, 0, @@ -877626,6 +905497,13 @@ Object { 3, 4, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -877655,6 +905533,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [], "length": 0, @@ -877663,6 +905547,7 @@ Object { "type": Array [], }, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, diff --git a/src/test/unit/__snapshots__/profile-upgrading.test.ts.snap b/src/test/unit/__snapshots__/profile-upgrading.test.ts.snap index e85d4f0300..bbeb4fc3a2 100644 --- a/src/test/unit/__snapshots__/profile-upgrading.test.ts.snap +++ b/src/test/unit/__snapshots__/profile-upgrading.test.ts.snap @@ -40,7 +40,7 @@ Object { "oscpu": undefined, "physicalCPUs": undefined, "platform": undefined, - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "Firefox", "sampleUnits": undefined, @@ -1695,6 +1695,210 @@ Object { null, null, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ null, null, @@ -2198,6 +2402,80 @@ Object { 151, 153, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -2428,6 +2706,12 @@ Object { "libIndex": Array [], "name": Array [], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [ null, @@ -2488,6 +2772,7 @@ Object { ], }, "sources": Object { + "content": Array [], "filename": Array [], "id": Array [], "length": 0, @@ -7358,7 +7643,7 @@ Object { "misc": "rv:48.0", "oscpu": "Intel Mac OS X 10.11", "platform": "Macintosh", - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "Firefox", "stackwalk": 1, @@ -7553,6 +7838,29 @@ Object { 3, 2, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ null, null, @@ -7615,6 +7923,15 @@ Object { 6, 7, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -7670,6 +7987,12 @@ Object { 7, ], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [ null, @@ -7694,6 +8017,9 @@ Object { ], }, "sources": Object { + "content": Array [ + null, + ], "filename": Array [ 10, ], @@ -8693,7 +9019,7 @@ Object { "misc": "rv:48.0", "oscpu": "Intel Mac OS X 10.11", "platform": "Macintosh", - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "Firefox", "stackwalk": 1, @@ -8904,6 +9230,31 @@ Object { 3, 2, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ null, null, @@ -8976,6 +9327,17 @@ Object { 14, 15, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -9037,6 +9399,12 @@ Object { 8, ], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [ null, @@ -9061,6 +9429,9 @@ Object { ], }, "sources": Object { + "content": Array [ + null, + ], "filename": Array [ 11, ], @@ -10163,7 +10534,7 @@ Object { "misc": "rv:48.0", "oscpu": "Intel Mac OS X 10.11", "platform": "Macintosh", - "preprocessedProfileVersion": 62, + "preprocessedProfileVersion": 63, "processType": 0, "product": "Firefox", "stackwalk": 1, @@ -10400,6 +10771,31 @@ Object { 3, 2, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "subcategory": Array [ null, null, @@ -10472,6 +10868,17 @@ Object { 15, 16, ], + "originalLocation": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], "relevantForJS": Array [ false, false, @@ -10533,6 +10940,12 @@ Object { 9, ], }, + "originalLocation": Object { + "column": Array [], + "length": 0, + "line": Array [], + "source": Array [], + }, "resourceTable": Object { "host": Array [ null, @@ -10557,6 +10970,9 @@ Object { ], }, "sources": Object { + "content": Array [ + null, + ], "filename": Array [ 12, ], diff --git a/src/test/unit/fetch-assembly.test.ts b/src/test/unit/fetch-assembly.test.ts index a03404d8b0..890d4fba31 100644 --- a/src/test/unit/fetch-assembly.test.ts +++ b/src/test/unit/fetch-assembly.test.ts @@ -77,6 +77,9 @@ describe('fetchAssembly', function () { fetchJSSourceFromBrowser: async (_sourceUuid: string) => { throw new Error('No browser connection'); }, + fetchSourceMapFromBrowser: async (_sourceId: string) => { + throw new Error('No browser connection'); + }, }) ).toEqual({ type: 'SUCCESS', @@ -151,6 +154,9 @@ describe('fetchAssembly', function () { fetchJSSourceFromBrowser: async (_sourceUuid: string) => { throw new Error('No browser connection'); }, + fetchSourceMapFromBrowser: async (_sourceId: string) => { + throw new Error('No browser connection'); + }, } ) ).type @@ -184,6 +190,9 @@ describe('fetchAssembly', function () { fetchJSSourceFromBrowser: async (_sourceUuid: string) => { throw new Error('No browser connection'); }, + fetchSourceMapFromBrowser: async (_sourceId: string) => { + throw new Error('No browser connection'); + }, } ) ).type @@ -205,6 +214,9 @@ describe('fetchAssembly', function () { fetchJSSourceFromBrowser: async (_sourceUuid: string) => { throw new Error('No browser connection'); }, + fetchSourceMapFromBrowser: async (_sourceId: string) => { + throw new Error('No browser connection'); + }, }) ).toEqual({ type: 'ERROR', @@ -246,6 +258,9 @@ describe('fetchAssembly', function () { fetchJSSourceFromBrowser: async (_sourceUuid: string) => { throw new Error('No browser connection'); }, + fetchSourceMapFromBrowser: async (_sourceId: string) => { + throw new Error('No browser connection'); + }, } ) ).type diff --git a/src/test/unit/fetch-source.test.ts b/src/test/unit/fetch-source.test.ts index 7bb7b38e21..027e8db6ee 100644 --- a/src/test/unit/fetch-source.test.ts +++ b/src/test/unit/fetch-source.test.ts @@ -31,6 +31,9 @@ describe('fetchSource', function () { fetchJSSourceFromBrowser: async (_sourceUuid: string) => { throw new Error('No browser connection'); }, + fetchSourceMapFromBrowser: async (_sourceId: string) => { + throw new Error('No browser connection'); + }, } ) ).toEqual({ @@ -75,6 +78,9 @@ describe('fetchSource', function () { const fetchJSSourceFromBrowser = async (_sourceUuid: string) => { throw new Error('No browser connection'); }; + const fetchSourceMapFromBrowser = async (_sourceId: string) => { + throw new Error('No browser connection'); + }; const archiveCache = new Map>(); @@ -89,6 +95,7 @@ describe('fetchSource', function () { fetchUrlResponse, queryBrowserSymbolicationApi, fetchJSSourceFromBrowser, + fetchSourceMapFromBrowser, } ) ).toEqual({ @@ -112,6 +119,7 @@ describe('fetchSource', function () { fetchUrlResponse, queryBrowserSymbolicationApi, fetchJSSourceFromBrowser, + fetchSourceMapFromBrowser, } ) ).toEqual({ @@ -133,6 +141,7 @@ describe('fetchSource', function () { fetchUrlResponse, queryBrowserSymbolicationApi, fetchJSSourceFromBrowser, + fetchSourceMapFromBrowser, } ) ).toEqual({ @@ -171,6 +180,9 @@ describe('fetchSource', function () { fetchJSSourceFromBrowser: async (_sourceUuid: string) => { throw new Error('No browser connection'); }, + fetchSourceMapFromBrowser: async (_sourceId: string) => { + throw new Error('No browser connection'); + }, } ) ).toEqual({ @@ -218,6 +230,9 @@ describe('fetchSource', function () { fetchJSSourceFromBrowser: async (_sourceUuid: string) => { throw new Error('No browser connection'); }, + fetchSourceMapFromBrowser: async (_sourceId: string) => { + throw new Error('No browser connection'); + }, } ) ).toEqual({ @@ -272,6 +287,9 @@ describe('fetchSource', function () { fetchJSSourceFromBrowser: async (_sourceUuid: string) => { throw new Error('No browser connection'); }, + fetchSourceMapFromBrowser: async (_sourceId: string) => { + throw new Error('No browser connection'); + }, } ) ).toEqual({ @@ -326,6 +344,9 @@ describe('fetchSource', function () { fetchJSSourceFromBrowser: async (_sourceUuid: string) => { throw new Error('No browser connection'); }, + fetchSourceMapFromBrowser: async (_sourceId: string) => { + throw new Error('No browser connection'); + }, } ) ).toEqual({ @@ -378,6 +399,9 @@ describe('fetchSource', function () { fetchJSSourceFromBrowser: async (_sourceUuid: string) => { throw new Error('No browser connection'); }, + fetchSourceMapFromBrowser: async (_sourceId: string) => { + throw new Error('No browser connection'); + }, } ) ).toEqual({ @@ -412,6 +436,9 @@ describe('fetchSource', function () { fetchJSSourceFromBrowser: async (_sourceUuid: string) => { throw new Error('No browser connection'); }, + fetchSourceMapFromBrowser: async (_sourceId: string) => { + throw new Error('No browser connection'); + }, } ) ).toEqual({ @@ -456,6 +483,9 @@ describe('fetchSource', function () { fetchJSSourceFromBrowser: async (_sourceUuid: string) => { throw new Error('No browser connection'); }, + fetchSourceMapFromBrowser: async (_sourceId: string) => { + throw new Error('No browser connection'); + }, } ) ).toEqual({ @@ -492,6 +522,9 @@ describe('fetchSource', function () { fetchJSSourceFromBrowser: async (_sourceUuid: string) => { throw new Error('No browser connection'); }, + fetchSourceMapFromBrowser: async (_sourceId: string) => { + throw new Error('No browser connection'); + }, } ) ).toEqual({ @@ -541,6 +574,9 @@ describe('fetchSource', function () { fetchJSSourceFromBrowser: async (_sourceUuid: string) => { throw new Error('No browser connection'); }, + fetchSourceMapFromBrowser: async (_sourceId: string) => { + throw new Error('No browser connection'); + }, } ) ).toEqual({ @@ -581,6 +617,9 @@ describe('fetchSource', function () { } throw new Error(`Unexpected source: ${sourceUuid}`); }, + fetchSourceMapFromBrowser: async (_sourceId: string) => { + throw new Error('No browser connection'); + }, } ) ).toEqual({ @@ -612,6 +651,9 @@ describe('fetchSource', function () { `Source not found for source with ID: ${sourceUuid}` ); }, + fetchSourceMapFromBrowser: async (_sourceId: string) => { + throw new Error('No browser connection'); + }, } ) ).toEqual({ @@ -655,6 +697,9 @@ describe('fetchSource', function () { fetchJSSourceFromBrowser: async (_sourceUuid: string) => { throw new Error('Source not found in browser'); }, + fetchSourceMapFromBrowser: async (_sourceId: string) => { + throw new Error('No browser connection'); + }, } ) ).toEqual({ diff --git a/src/test/unit/line-timings.test.ts b/src/test/unit/line-timings.test.ts index fceb07b357..82de4b5c71 100644 --- a/src/test/unit/line-timings.test.ts +++ b/src/test/unit/line-timings.test.ts @@ -39,7 +39,8 @@ describe('getStackLineInfo', function () { stackTable, frameTable, funcTable, - fileOneSourceIndex + fileOneSourceIndex, + thread.originalLocation ); // Expect the returned stackIndexToLineSetIndex array to have the same length as the stackTable. @@ -52,14 +53,22 @@ describe('getStackLineInfo', function () { describe('getLineTimings for getStackLineInfo', function () { function getTimings(thread: Thread, file: string) { - const { stackTable, frameTable, funcTable, samples, stringTable } = thread; + const { + stackTable, + frameTable, + funcTable, + samples, + stringTable, + originalLocation, + } = thread; const fileStringIndex = stringTable.indexForString(file); const fileSourceIndex = thread.sources.filename.indexOf(fileStringIndex); const stackLineInfo = getStackLineInfo( stackTable, frameTable, funcTable, - fileSourceIndex + fileSourceIndex, + originalLocation ); return getLineTimings(stackLineInfo, samples); } @@ -140,7 +149,8 @@ describe('getLineTimings for getStackLineInfo', function () { stackTable, frameTable, funcTable, - fileSourceIndex + fileSourceIndex, + thread.originalLocation ); const lineTimings = getLineTimings(stackLineInfo, samples); @@ -149,6 +159,68 @@ describe('getLineTimings for getStackLineInfo', function () { expect(lineTimings.totalLineHits.get(20)).toBe(1); expect(lineTimings.totalLineHits.get(35)).toBe(1); }); + + it("attributes the func's original line when the frame is not source-mapped but the func is", function () { + // Case from the source-map symbolication design: the func definition was + // mapped to an original source, but the frame's execution point was not + // (e.g. it lacked line/column info, so it was never submitted for + // symbolication). Source determination falls back to the func's original + // source, so line attribution must also come from the func's originalLocation + // entry. Otherwise we'd attribute the compiled line value in + // frameTable.line[frame] onto an original-source view. + const { derivedThreads } = getProfileFromTextSamples(` + A[file:compiled.js][line:5] + B[file:compiled.js][line:7] + `); + const [thread] = derivedThreads; + const { stackTable, frameTable, funcTable, samples, stringTable } = thread; + + // Register an additional source representing the original .ts file. + const originalFilenameIndex = stringTable.indexForString('original.ts'); + const originalSourceIndex = thread.sources.length; + thread.sources.id.push(null); + thread.sources.filename.push(originalFilenameIndex); + thread.sources.sourceMapURL.push(null); + thread.sources.content.push(null); + thread.sources.length++; + + // For both funcs, add an originalLocation entry pointing at the original + // source at a known line. Leave frameTable.originalLocation as null on + // every frame. That is the case under test. + const funcAIndex = frameTable.func[stackTable.frame[0]]; + const funcBIndex = frameTable.func[stackTable.frame[stackTable.length - 1]]; + const funcALine = 100; + const funcBLine = 200; + const originalLocation = thread.originalLocation; + originalLocation.source.push(originalSourceIndex, originalSourceIndex); + originalLocation.line.push(funcALine, funcBLine); + originalLocation.column.push(1, 1); + const funcAOriginalLocationIdx = originalLocation.length; + const funcBOriginalLocationIdx = originalLocation.length + 1; + originalLocation.length += 2; + funcTable.originalLocation[funcAIndex] = funcAOriginalLocationIdx; + funcTable.originalLocation[funcBIndex] = funcBOriginalLocationIdx; + + const stackLineInfo = getStackLineInfo( + stackTable, + frameTable, + funcTable, + originalSourceIndex, + originalLocation + ); + const lineTimings = getLineTimings(stackLineInfo, samples); + + // Without the fix, the compiled line numbers (5 and 7) would have been + // attributed to the original-source view. With the fix, the func's + // original lines are used. + expect(lineTimings.selfLineHits.get(funcBLine)).toBe(1); + expect(lineTimings.selfLineHits.size).toBe(1); + expect(lineTimings.totalLineHits.get(funcALine)).toBe(1); + expect(lineTimings.totalLineHits.get(funcBLine)).toBe(1); + expect(lineTimings.totalLineHits.size).toBe(2); + expect(lineTimings.selfLineHits.get(5)).toBe(undefined); + expect(lineTimings.selfLineHits.get(7)).toBe(undefined); + }); }); describe('getTotalLineTimingsForCallNode', function () { @@ -158,7 +230,8 @@ describe('getTotalLineTimingsForCallNode', function () { defaultCategory: IndexIntoCategoryList, isInverted: boolean ): Map { - const { stackTable, frameTable, funcTable, samples } = thread; + const { stackTable, frameTable, funcTable, samples, originalLocation } = + thread; const nonInvertedCallNodeInfo = getCallNodeInfo( stackTable, frameTable, @@ -186,7 +259,9 @@ describe('getTotalLineTimingsForCallNode', function () { samples, callNodeFramePerStack, frameTable, - funcLine + funcTable, + funcLine, + originalLocation ); } diff --git a/src/test/unit/merge-compare.test.ts b/src/test/unit/merge-compare.test.ts index a68fa2462f..7c0917ca91 100644 --- a/src/test/unit/merge-compare.test.ts +++ b/src/test/unit/merge-compare.test.ts @@ -985,4 +985,121 @@ describe('mergeProfilesForDiffing with source tables', function () { singleProfile.profile.shared.sources ); }); + + it('should merge originalLocation tables and remap funcTable / frameTable references', function () { + const profileA = getProfileFromTextSamples(` + A[file:bundle-a.js] + `); + const profileB = getProfileFromTextSamples(` + B[file:bundle-b.js] + `); + + // Pre-populate originalLocation and the func/frame columns that reference it. + // bundle-a.js source is index 0 in each per-profile table (sources from + // getProfileFromTextSamples have null id but a filename string). + const sharedA = profileA.profile.shared; + sharedA.originalLocation.source.push(0); + sharedA.originalLocation.line.push(11); + sharedA.originalLocation.column.push(22); + sharedA.originalLocation.length = 1; + sharedA.funcTable.originalLocation[0] = 0; + sharedA.frameTable.originalLocation[0] = 0; + + const sharedB = profileB.profile.shared; + sharedB.originalLocation.source.push(0); + sharedB.originalLocation.line.push(33); + sharedB.originalLocation.column.push(44); + sharedB.originalLocation.length = 1; + sharedB.funcTable.originalLocation[0] = 0; + sharedB.frameTable.originalLocation[0] = 0; + + const profileState = stateFromLocation({ + pathname: '/public/fakehash1/', + search: '?thread=0&v=3', + hash: '', + }); + + const { profile: mergedProfile } = mergeProfilesForDiffing( + [profileA.profile, profileB.profile], + [profileState, profileState] + ); + + const { funcTable, frameTable, originalLocation, sources } = + mergedProfile.shared; + const stringTable = StringTable.withBackingArray( + mergedProfile.shared.stringArray + ); + + // Both inputs' originalLocation rows survive the merge (no dedup). + expect(originalLocation.length).toBe(2); + + // source indices are remapped through the merged sources table. + for (let i = 0; i < originalLocation.length; i++) { + const src = originalLocation.source[i]; + expect(src).not.toBeNull(); + expect(src).toBeGreaterThanOrEqual(0); + expect(src).toBeLessThan(sources.length); + } + + // Each input's funcTable.originalLocation[0] now points at the corresponding + // remapped row, and the remapped row carries the correct original line. + const funcAName = stringTable.indexForString('A'); + const funcBName = stringTable.indexForString('B'); + const funcAIndex = funcTable.name.indexOf(funcAName); + const funcBIndex = funcTable.name.indexOf(funcBName); + expect(funcAIndex).toBeGreaterThanOrEqual(0); + expect(funcBIndex).toBeGreaterThanOrEqual(0); + + const funcAOriginalLocationIdx = funcTable.originalLocation[funcAIndex]; + const funcBOriginalLocationIdx = funcTable.originalLocation[funcBIndex]; + expect(funcAOriginalLocationIdx).not.toBeNull(); + expect(funcBOriginalLocationIdx).not.toBeNull(); + expect(originalLocation.line[ensureExists(funcAOriginalLocationIdx)]).toBe( + 11 + ); + expect(originalLocation.line[ensureExists(funcBOriginalLocationIdx)]).toBe( + 33 + ); + + // The frame columns are populated for every row (catches the original bug + // where the column was left empty and any index access returned undefined). + for (let i = 0; i < frameTable.length; i++) { + const v = frameTable.originalLocation[i]; + expect(v === null || v >= 0).toBe(true); + } + for (let i = 0; i < funcTable.length; i++) { + const v = funcTable.originalLocation[i]; + expect(v === null || v >= 0).toBe(true); + } + }); + + it('should populate empty originalLocation columns on funcTable and frameTable when no symbolication ran', function () { + const profileA = getProfileFromTextSamples(`A[file:a.js]`); + const profileB = getProfileFromTextSamples(`B[file:b.js]`); + + const profileState = stateFromLocation({ + pathname: '/public/fakehash1/', + search: '?thread=0&v=3', + hash: '', + }); + + const { profile: mergedProfile } = mergeProfilesForDiffing( + [profileA.profile, profileB.profile], + [profileState, profileState] + ); + + const { funcTable, frameTable, originalLocation } = mergedProfile.shared; + + // Even without any symbolicated entries on the inputs, the columns must + // be filled (not undefined) so downstream `x !== null` checks work. + expect(originalLocation.length).toBe(0); + expect(funcTable.originalLocation).toHaveLength(funcTable.length); + expect(frameTable.originalLocation).toHaveLength(frameTable.length); + for (const v of funcTable.originalLocation) { + expect(v).toBeNull(); + } + for (const v of frameTable.originalLocation) { + expect(v).toBeNull(); + } + }); }); diff --git a/src/test/unit/nonymous.test.ts b/src/test/unit/nonymous.test.ts new file mode 100644 index 0000000000..a0d2c01983 --- /dev/null +++ b/src/test/unit/nonymous.test.ts @@ -0,0 +1,146 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import { + parseNonymousName, + serializeNonymousName, +} from '../../profile-logic/nonymous'; +import type { NonymousName } from '../../profile-logic/nonymous'; + +describe('parseNonymousName', () => { + it('parses a simple identifier', () => { + expect(parseNonymousName('foo')).toEqual([ + { kind: 'named', name: 'foo', contributesTo: false }, + ]); + }); + + it('parses a contributes-to name', () => { + expect(parseNonymousName('foo<')).toEqual([ + { kind: 'named', name: 'foo', contributesTo: true }, + ]); + }); + + it('parses a bare anonymous segment', () => { + expect(parseNonymousName('<')).toEqual([{ kind: 'anonymous' }]); + }); + + it('parses a scoped name', () => { + expect(parseNonymousName('outer/inner')).toEqual([ + { kind: 'named', name: 'outer', contributesTo: false }, + { kind: 'named', name: 'inner', contributesTo: false }, + ]); + }); + + it('parses a three-level scope chain', () => { + expect(parseNonymousName('foz/baz/bay')).toEqual([ + { kind: 'named', name: 'foz', contributesTo: false }, + { kind: 'named', name: 'baz', contributesTo: false }, + { kind: 'named', name: 'bay', contributesTo: false }, + ]); + }); + + it('parses a scoped contributes-to name', () => { + expect(parseNonymousName('main/foo<')).toEqual([ + { kind: 'named', name: 'main', contributesTo: false }, + { kind: 'named', name: 'foo', contributesTo: true }, + ]); + }); + + it('parses the canonical i2 { + expect(parseNonymousName('i2 { + expect(parseNonymousName('outer/<')).toEqual([ + { kind: 'named', name: 'outer', contributesTo: false }, + { kind: 'anonymous' }, + ]); + }); + + it('parses a property chain name', () => { + expect(parseNonymousName('this.eventPool_.createObject')).toEqual([ + { + kind: 'named', + name: 'this.eventPool_.createObject', + contributesTo: false, + }, + ]); + }); + + it('parses a numeric element access name', () => { + expect(parseNonymousName('arr[0]')).toEqual([ + { kind: 'named', name: 'arr[0]', contributesTo: false }, + ]); + }); + + it('returns anonymous for an empty string', () => { + expect(parseNonymousName('')).toEqual([{ kind: 'anonymous' }]); + }); +}); + +describe('serializeNonymousName', () => { + it('serializes a simple named local', () => { + const name: NonymousName = [ + { kind: 'named', name: 'foo', contributesTo: false }, + ]; + expect(serializeNonymousName(name)).toBe('foo'); + }); + + it('serializes a contributes-to local', () => { + const name: NonymousName = [ + { kind: 'named', name: 'foo', contributesTo: true }, + ]; + expect(serializeNonymousName(name)).toBe('foo<'); + }); + + it('serializes an anonymous local', () => { + const name: NonymousName = [{ kind: 'anonymous' }]; + expect(serializeNonymousName(name)).toBe('<'); + }); + + it('serializes a scoped name', () => { + const name: NonymousName = [ + { kind: 'named', name: 'outer', contributesTo: false }, + { kind: 'named', name: 'inner', contributesTo: false }, + ]; + expect(serializeNonymousName(name)).toBe('outer/inner'); + }); + + it('round-trips all parse test cases', () => { + const cases = [ + 'foo', + 'foo<', + '<', + 'outer/inner', + 'foz/baz/bay', + 'main/foo<', + 'i2 { + const cases = [ + 'foo', + 'foo<', + 'obj.method', + 'outer/inner', + 'outer/inner<', + 'outer/<', + 'i2 profile.shared.stringArray[n] === 'A' + ); + profile.shared.funcTable.originalLocation[funcA] = addOriginalLocation( + profile, + originalA, + 10, + 1 + ); + + // Func B is mapped to original-b.ts, but one of its frames is inlined + // from inlined.ts (frame originalLocation differs from func originalLocation). + const funcB = profile.shared.funcTable.name.findIndex( + (n) => profile.shared.stringArray[n] === 'B' + ); + profile.shared.funcTable.originalLocation[funcB] = addOriginalLocation( + profile, + originalB, + 20, + 1 + ); + const frameB = profile.shared.frameTable.func.findIndex((f) => f === funcB); + profile.shared.frameTable.originalLocation[frameB] = addOriginalLocation( + profile, + originalBInline, + 5, + 2 + ); + + const result = collectSourceIndicesFromThreads( + [0], + profile.threads, + profile.shared + ); + + expect(result).toEqual( + new Set([bundle, originalA, originalB, originalBInline]) + ); + }); + + it('skips threadIndex values that are out of range', function () { + const { profile } = getProfileFromTextSamples(` + A[file:a.js] + `); + const aJs = sourceIndexForFile(profile, 'a.js'); + + expect( + collectSourceIndicesFromThreads( + [0, 5, 99], + profile.threads, + profile.shared + ) + ).toEqual(new Set([aJs])); + }); + + it('includes sources reached via jsAllocations and nativeAllocations stacks', function () { + const { profile } = getProfileFromTextSamples( + // Samples have one source... + ` + A[file:samples.js] + `, + // ...and the other thread has two unrelated stacks we'll re-attach as + // allocation stacks below. + ` + B[file:alloc-js.js] + C[file:alloc-native.js] + ` + ); + + const samplesJs = sourceIndexForFile(profile, 'samples.js'); + const allocJs = sourceIndexForFile(profile, 'alloc-js.js'); + const allocNative = sourceIndexForFile(profile, 'alloc-native.js'); + + // Grab two distinct stack indices from thread 1 to use as the allocation + // sample stacks for thread 0. + const thread1 = profile.threads[1]; + const allocJsStack = thread1.samples.stack[0]; // top frame = B (alloc-js.js) + // For the native one, walk to a stack whose top is C. + const { stackTable, frameTable, funcTable, stringArray } = profile.shared; + const cFunc = funcTable.name.findIndex((n) => stringArray[n] === 'C'); + let allocNativeStack: number | null = null; + for (let i = 0; i < stackTable.length; i++) { + if (funcTable.source[frameTable.func[stackTable.frame[i]]] !== null) { + if (frameTable.func[stackTable.frame[i]] === cFunc) { + allocNativeStack = i; + break; + } + } + } + if (allocNativeStack === null) { + throw new Error('Could not find a stack for func C'); + } + + profile.threads[0].jsAllocations = { + time: [0], + className: [0], + typeName: [0], + coarseType: [0], + weight: [1], + weightType: 'bytes', + inNursery: [false], + stack: [allocJsStack], + length: 1, + } as any; + profile.threads[0].nativeAllocations = { + time: [0], + weight: [1], + weightType: 'bytes', + stack: [allocNativeStack], + length: 1, + } as any; + + // Only collecting from thread 0. But allocations point into thread 1's + // stacks, which live in the shared stack table. + const result = collectSourceIndicesFromThreads( + [0], + profile.threads, + profile.shared + ); + + expect(result.has(samplesJs)).toBe(true); + expect(result.has(allocJs)).toBe(true); + expect(result.has(allocNative)).toBe(true); + }); +}); diff --git a/src/test/unit/profile-tree.test.ts b/src/test/unit/profile-tree.test.ts index f50cd99fe8..5ab98e1b16 100644 --- a/src/test/unit/profile-tree.test.ts +++ b/src/test/unit/profile-tree.test.ts @@ -16,6 +16,7 @@ import { getCallNodeInfo, getInvertedCallNodeInfo, getOriginAnnotationForFunc, + getOriginalPositionForFrame, filterRawThreadSamplesToRange, getSampleIndexToCallNodeIndex, } from '../../profile-logic/profile-data'; @@ -724,6 +725,8 @@ describe('origin annotation', function () { function getOrigin(funcName: string): string { return getOriginAnnotationForFunc( funcNames.indexOf(funcName), + null, + shared.frameTable, shared.funcTable, shared.resourceTable, stringTable, @@ -749,3 +752,224 @@ describe('origin annotation', function () { expect(getOrigin('D')).toEqual('libxul.so'); }); }); + +describe('getOriginAnnotationForFunc with originalLocation', function () { + function setup() { + const { profile, stringTable } = getProfileFromTextSamples(`A`); + const { shared } = profile; + + const bundleIndex = addSourceToTable( + shared.sources, + stringTable.indexForString('http://example.com/bundle.js') + ); + const originalIndex = addSourceToTable( + shared.sources, + stringTable.indexForString('http://example.com/original.ts') + ); + + // Wire the single func to the bundle and give it a compiled position. + shared.funcTable.source[0] = bundleIndex; + shared.funcTable.lineNumber[0] = 1; + shared.funcTable.columnNumber[0] = 100; + + // The text sample produces one frame referencing func 0. Give it a + // compiled position so tier-3 fallback has something meaningful to surface. + shared.frameTable.line[0] = 5; + shared.frameTable.column[0] = 10; + + function addOriginalLocationRow( + source: number, + line: number, + column: number + ): number { + const idx = shared.originalLocation.length; + shared.originalLocation.source.push(source); + shared.originalLocation.line.push(line); + shared.originalLocation.column.push(column); + shared.originalLocation.length++; + return idx; + } + + function callOrigin(frameOriginalLocationIdx: number | null): string { + shared.frameTable.originalLocation[0] = frameOriginalLocationIdx; + return getOriginAnnotationForFunc( + 0, + 0, + shared.frameTable, + shared.funcTable, + shared.resourceTable, + stringTable, + shared.sources, + shared.originalLocation + ); + } + + return { + shared, + originalIndex, + bundleIndex, + addOriginalLocationRow, + callOrigin, + }; + } + + it('uses the frame original position when both source and line are mapped', function () { + const { originalIndex, addOriginalLocationRow, callOrigin } = setup(); + const idx = addOriginalLocationRow(originalIndex, 42, 7); + expect(callOrigin(idx)).toEqual('http://example.com/original.ts:42:7'); + }); + + it('falls back to func mapping when the frame has no source-map entry', function () { + const { shared, originalIndex, addOriginalLocationRow, callOrigin } = + setup(); + shared.funcTable.originalLocation[0] = addOriginalLocationRow( + originalIndex, + 10, + 4 + ); + expect(callOrigin(null)).toEqual('http://example.com/original.ts:10:4'); + }); + + it("uses the frame's compiled position when the frame has no source-map entry", function () { + const { callOrigin } = setup(); + expect(callOrigin(null)).toEqual('http://example.com/bundle.js:5:10'); + }); +}); + +describe('getOriginalPositionForFrame', function () { + function setup() { + const { profile, stringTable } = getProfileFromTextSamples(`A`); + const { shared } = profile; + + const bundleIndex = addSourceToTable( + shared.sources, + stringTable.indexForString('http://example.com/bundle.js') + ); + const originalIndex = addSourceToTable( + shared.sources, + stringTable.indexForString('http://example.com/original.ts') + ); + + shared.funcTable.source[0] = bundleIndex; + shared.funcTable.lineNumber[0] = 1; + shared.funcTable.columnNumber[0] = 100; + shared.frameTable.line[0] = 5; + shared.frameTable.column[0] = 10; + + function addOriginalLocationRow( + source: number, + line: number, + column: number + ): number { + const idx = shared.originalLocation.length; + shared.originalLocation.source.push(source); + shared.originalLocation.line.push(line); + shared.originalLocation.column.push(column); + shared.originalLocation.length++; + return idx; + } + + return { shared, bundleIndex, originalIndex, addOriginalLocationRow }; + } + + it("returns the frame's source-mapped position when present (tier 1)", function () { + const { shared, originalIndex, addOriginalLocationRow } = setup(); + shared.frameTable.originalLocation[0] = addOriginalLocationRow( + originalIndex, + 42, + 7 + ); + expect( + getOriginalPositionForFrame( + 0, + 0, + shared.frameTable, + shared.funcTable, + shared.originalLocation + ) + ).toEqual({ source: originalIndex, line: 42, column: 7 }); + }); + + it("falls back to the func's source-mapped position when the frame has no source-map entry (tier 2)", function () { + const { shared, originalIndex, addOriginalLocationRow } = setup(); + shared.funcTable.originalLocation[0] = addOriginalLocationRow( + originalIndex, + 10, + 4 + ); + expect( + getOriginalPositionForFrame( + 0, + 0, + shared.frameTable, + shared.funcTable, + shared.originalLocation + ) + ).toEqual({ source: originalIndex, line: 10, column: 4 }); + }); + + it("falls back to the frame's compiled position when no source-map info applies (tier 3)", function () { + const { shared, bundleIndex } = setup(); + expect( + getOriginalPositionForFrame( + 0, + 0, + shared.frameTable, + shared.funcTable, + shared.originalLocation + ) + ).toEqual({ source: bundleIndex, line: 5, column: 10 }); + }); + + it("falls back to the func's compiled line/column when the frame's are null", function () { + const { shared, bundleIndex } = setup(); + shared.frameTable.line[0] = null; + shared.frameTable.column[0] = null; + expect( + getOriginalPositionForFrame( + 0, + 0, + shared.frameTable, + shared.funcTable, + shared.originalLocation + ) + ).toEqual({ source: bundleIndex, line: 1, column: 100 }); + }); + + it("uses the func's position when frameIndex is null (tooltip/call-node sites)", function () { + const { shared, originalIndex, addOriginalLocationRow } = setup(); + shared.funcTable.originalLocation[0] = addOriginalLocationRow( + originalIndex, + 10, + 4 + ); + expect( + getOriginalPositionForFrame( + null, + 0, + shared.frameTable, + shared.funcTable, + shared.originalLocation + ) + ).toEqual({ source: originalIndex, line: 10, column: 4 }); + }); + + it('treats originalLocation = null as no symbolication available', function () { + const { shared, bundleIndex, originalIndex, addOriginalLocationRow } = + setup(); + shared.funcTable.originalLocation[0] = addOriginalLocationRow( + originalIndex, + 10, + 4 + ); + expect( + getOriginalPositionForFrame( + 0, + 0, + shared.frameTable, + shared.funcTable, + null + ) + ).toEqual({ source: bundleIndex, line: 5, column: 10 }); + }); +}); diff --git a/src/test/unit/query-api.test.ts b/src/test/unit/query-api.test.ts index f760d7bc35..a96215c915 100644 --- a/src/test/unit/query-api.test.ts +++ b/src/test/unit/query-api.test.ts @@ -35,6 +35,12 @@ describe('queryApiWithFallback', function () { throw new Error('Not implemented'); }) ), + fetchSourceMapFromBrowser: jest.fn( + overrides.fetchSourceMapFromBrowser ?? + (async () => { + throw new Error('Not implemented'); + }) + ), }; } @@ -278,6 +284,8 @@ describe('RegularExternalCommunicationDelegate', function () { getPageFavicons: jest.fn(bcOverrides.getPageFavicons), showFunctionInDevtools: jest.fn(bcOverrides.showFunctionInDevtools), getJSSource: jest.fn(bcOverrides.getJSSource), + getSourceMap: jest.fn(bcOverrides.getSourceMap), + supportsGetSourceMap: false, } : null; diff --git a/src/test/unit/source-map-scope-tree.test.ts b/src/test/unit/source-map-scope-tree.test.ts new file mode 100644 index 0000000000..97aae80d3a --- /dev/null +++ b/src/test/unit/source-map-scope-tree.test.ts @@ -0,0 +1,606 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import { + parseJsScopeTree, + findInnermostFunctionScope, + dialectForFilename, +} from '../../profile-logic/source-map-scope-tree'; + +describe('parseJsScopeTree', () => { + it('returns an empty array for empty source', () => { + const scopes = parseJsScopeTree(''); + expect(scopes).toHaveLength(0); + }); + + it('handles invalid JS without throwing', () => { + expect(() => + parseJsScopeTree('this is not valid javascript !!!') + ).not.toThrow(); + }); + + it('parses a named function declaration', () => { + const src = 'function foo() {}'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.start).toBe(0); + expect(fn.end).toBe(src.length); + expect(fn.astName).toBe('foo'); + expect(fn.kind).toBe('function'); + // nameMappingLocations: [namePos, parenPos] + expect(fn.nameMappingLocations).toHaveLength(2); + expect(src[fn.nameMappingLocations[0]]).toBe('f'); // start of 'foo' + expect(src[fn.nameMappingLocations[1]]).toBe('('); // paren + }); + + it('parses a parenthesised arrow function with two locations', () => { + // ArrowFunction created directly (not through VariableDeclarator). + const src = '[(x) => x]'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.kind).toBe('arrow'); + expect(fn.astName).toBeNull(); + // nameMappingLocations: [parenPos, arrowPos ('=')] + expect(fn.nameMappingLocations).toHaveLength(2); + expect(src[fn.nameMappingLocations[0]]).toBe('('); + expect(src[fn.nameMappingLocations[1]]).toBe('='); + expect(src[fn.nameMappingLocations[1] + 1]).toBe('>'); // confirm it's => + }); + + it('parses an anonymous function assigned to a variable', () => { + const src = 'const foo = function() {}'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.astName).toBeNull(); + expect(fn.kind).toBe('function'); + // nameMappingLocations: [parenPos, identifierPos] + // identifier ('foo') comes last + expect(fn.nameMappingLocations).toHaveLength(2); + expect(src[fn.nameMappingLocations[0]]).toBe('('); + expect(src[fn.nameMappingLocations[1]]).toBe('f'); // start of 'foo' + // identifier loc must be before paren loc (foo comes before function) + expect(fn.nameMappingLocations[1]).toBeLessThan(fn.nameMappingLocations[0]); + }); + + it('uses the arrow pos and identifier pos for arrow assigned to variable', () => { + const src = 'const foo = (x) => x'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.kind).toBe('arrow'); + expect(fn.astName).toBeNull(); + // nameMappingLocations: [parenPos, arrowPos, identifierPos('foo')] + expect(fn.nameMappingLocations).toHaveLength(3); + expect(src[fn.nameMappingLocations[0]]).toBe('('); + expect(src[fn.nameMappingLocations[1]]).toBe('='); + expect(src[fn.nameMappingLocations[2]]).toBe('f'); // start of 'foo' + }); + + it('parses a single-param unparenthesised arrow with only the arrow location', () => { + // No `(` before `x` → no paren loc. Only arrow loc. + const src = '[x => x]'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.kind).toBe('arrow'); + // nameMappingLocations: [arrowPos] only. No paren + expect(fn.nameMappingLocations).toHaveLength(1); + expect(src[fn.nameMappingLocations[0]]).toBe('='); + expect(src[fn.nameMappingLocations[0] + 1]).toBe('>'); + }); + + it('prepends the key location for a method in an object literal', () => { + const src = 'const o = { foo() {} }'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.astName).toBe('foo'); + expect(fn.kind).toBe('function'); + // nameMappingLocations: [keyPos('foo'), parenPos] + expect(fn.nameMappingLocations.length).toBeGreaterThanOrEqual(1); + expect(src[fn.nameMappingLocations[0]]).toBe('f'); // start of 'foo' + }); + + it('prepends the key location for a named FunctionExpression property', () => { + const src = 'const o = { foo: function() {} }'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.astName).toBe('foo'); + expect(fn.kind).toBe('function'); + expect(src[fn.nameMappingLocations[0]]).toBe('f'); // key 'foo' + expect(src[fn.nameMappingLocations[1]]).toBe('('); // paren + }); + + it('marks an arrow-valued property as kind arrow', () => { + const src = 'const o = { foo: (x) => x }'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.kind).toBe('arrow'); + expect(fn.astName).toBe('foo'); + expect(src[fn.nameMappingLocations[0]]).toBe('f'); // key + expect(src[fn.nameMappingLocations[1]]).toBe('('); // arrow paren + expect(src[fn.nameMappingLocations[2]]).toBe('='); // arrow => + }); + + it('parses a private class method with # in astName', () => { + const src = 'class C { #foo() {} }'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.astName).toBe('#foo'); + expect(fn.kind).toBe('function'); + expect(src[fn.nameMappingLocations[0]]).toBe('#'); + }); + + it('does not create a scope for a shorthand property', () => { + const src = 'const o = { a, b }'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(0); + }); + + it('creates no scope for an empty computed method', () => { + // Computed key `{ [k]() {} }`: no stable key location → we walk the body + // but find no nested functions, so no scopes are created. + const src = 'const k = "foo"; const o = { [k]() {} }'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(0); + }); + + it('finds nested functions inside a computed method body', () => { + const src = 'const o = { [k]() { function inner() {} } }'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + expect(scopes[0].astName).toBe('inner'); + }); + + it('nests child scopes inside the parent scope', () => { + const src = 'function outer() { function inner() {} }'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const outer = scopes[0]; + expect(outer.astName).toBe('outer'); + expect(outer.children).toHaveLength(1); + + const inner = outer.children[0]; + expect(inner.astName).toBe('inner'); + expect(inner.children).toHaveLength(0); + }); + + it('gives a named function expression its own astName', () => { + // `const foo = function bar() {}` → astName = 'bar', not inferred from foo. + const src = 'const foo = function bar() {}'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.astName).toBe('bar'); + expect(fn.kind).toBe('function'); + }); + + it('infers name from assignment target for `obj.foo = function() {}`', () => { + const src = 'obj.foo = function() {}'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.astName).toBeNull(); + expect(fn.kind).toBe('function'); + // Last nameMappingLocation should point to 'foo' in obj.foo + const lastLoc = fn.nameMappingLocations[fn.nameMappingLocations.length - 1]; + expect(src[lastLoc]).toBe('f'); // 'foo' + }); + + it('does not mark direct assignment as contributesTo', () => { + // `const foo = arrow`. The arrow IS `foo`, so no `<`. + const src = 'const foo = () => {}'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + expect(scopes[0].contributesTo).toBe(false); + }); + + it('does not mark plain function declarations as contributesTo', () => { + const src = 'function foo() {}'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + expect(scopes[0].contributesTo).toBe(false); + }); + + it('infers name through `new` for `var observer = new C((entries) => {})`', () => { + const src = 'const observer = new IntersectionObserver((entries) => {})'; + const scopes = parseJsScopeTree(src); + // Only one scope: the arrow argument of `new`. No plain anonymous scope. + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.kind).toBe('arrow'); + expect(fn.astName).toBeNull(); + expect(fn.contributesTo).toBe(true); + // The last nameMappingLocation should be the start of `observer`. + const lastLoc = fn.nameMappingLocations[fn.nameMappingLocations.length - 1]; + expect(src[lastLoc]).toBe('o'); // start of 'observer' + expect(src.slice(lastLoc, lastLoc + 8)).toBe('observer'); + }); + + it('infers name through call for `var x = wrap(() => {})`', () => { + const src = 'const cb = wrap(() => {})'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.kind).toBe('arrow'); + expect(fn.contributesTo).toBe(true); + const lastLoc = fn.nameMappingLocations[fn.nameMappingLocations.length - 1]; + expect(src[lastLoc]).toBe('c'); // start of 'cb' + expect(src.slice(lastLoc, lastLoc + 2)).toBe('cb'); + }); + + it('infers names for every function arg of a wrap call', () => { + const src = 'const x = wrap(() => 1, () => 2)'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(2); + + for (const fn of scopes) { + expect(fn.kind).toBe('arrow'); + expect(fn.contributesTo).toBe(true); + const lastLoc = + fn.nameMappingLocations[fn.nameMappingLocations.length - 1]; + expect(src[lastLoc]).toBe('x'); + } + }); + + it('peels parens around an arrow init: `var x = ((y) => y)`', () => { + // Parens are pure-syntactic: the arrow still IS x, so contributesTo=false. + const src = 'const x = ((y) => y)'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.kind).toBe('arrow'); + expect(fn.contributesTo).toBe(false); + const lastLoc = fn.nameMappingLocations[fn.nameMappingLocations.length - 1]; + expect(src[lastLoc]).toBe('x'); + }); + + it('does not infer for a named FunctionExpression argument', () => { + // `const x = wrap(function bar() {})`. `bar` keeps its own name, so the + // scope must not be marked contributesTo and must carry astName = 'bar'. + const src = 'const x = wrap(function bar() {})'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.kind).toBe('function'); + expect(fn.astName).toBe('bar'); + expect(fn.contributesTo).toBe(false); + }); + + it('does not infer through arbitrary expressions: `var x = obj.prop`', () => { + // A non-wrap expression should not propagate the identifier into nested + // functions. Only direct values, ParenthesizedExpression, + // CallExpression and NewExpression do. + const src = 'const x = obj[(() => 0)()]'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + expect(scopes[0].contributesTo).toBe(false); + }); + + it('infers through `obj.foo = new C(() => {})`', () => { + const src = 'obj.foo = new IntersectionObserver(() => {})'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.kind).toBe('arrow'); + expect(fn.contributesTo).toBe(true); + const lastLoc = fn.nameMappingLocations[fn.nameMappingLocations.length - 1]; + expect(src[lastLoc]).toBe('f'); // start of 'foo' + }); + + it('infers through nested wrappers: `var x = wrap1(wrap2(() => {}))`', () => { + const src = 'const x = a(b(() => {}))'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.kind).toBe('arrow'); + expect(fn.contributesTo).toBe(true); + const lastLoc = fn.nameMappingLocations[fn.nameMappingLocations.length - 1]; + expect(src[lastLoc]).toBe('x'); + }); + + it('does not infer for compound assignments: `x += () => 0`', () => { + const src = 'x += () => 0'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.kind).toBe('arrow'); + expect(fn.contributesTo).toBe(false); + }); + + it('direct member assignment is not contributesTo: `obj.foo = arrow`', () => { + // `obj.foo = arrow`. The arrow IS obj.foo, so contributesTo=false. + const src = 'obj.foo = () => {}'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.kind).toBe('arrow'); + expect(fn.contributesTo).toBe(false); + const lastLoc = fn.nameMappingLocations[fn.nameMappingLocations.length - 1]; + expect(src[lastLoc]).toBe('f'); // start of 'foo' + }); + + it('computed-member assignment sets computedKeyLoc: `obj[key] = arrow`', () => { + // `obj[key] = arrow`. The arrow IS obj[key], so contributesTo=false. + // computedKeyLoc points to the start of `key` so the resolver can build + // a compound `${receiver}[${key}]` name. + const src = 'obj[key] = (...args) => f(...args)'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.kind).toBe('arrow'); + expect(fn.contributesTo).toBe(false); + // Last name mapping location = start of the receiver `obj`. + const lastLoc = fn.nameMappingLocations[fn.nameMappingLocations.length - 1]; + expect(src[lastLoc]).toBe('o'); // start of 'obj' + // computedKeyLoc = start of `key`. + expect(fn.computedKeyLoc).not.toBeNull(); + expect(src[fn.computedKeyLoc!]).toBe('k'); // start of 'key' + expect(src.slice(fn.computedKeyLoc!, fn.computedKeyLoc! + 3)).toBe('key'); + }); + + it('keeps contributesTo through wrapper for `obj[key] = wrap(arrow)`', () => { + // Wrapping with a call still bumps contributesTo to true, while + // computedKeyLoc still tracks the bracket-key. + const src = 'obj[key] = wrap(() => {})'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.contributesTo).toBe(true); + expect(fn.computedKeyLoc).not.toBeNull(); + }); + + it('preserves astName on named function expressions: `const x = function bar() {}`', () => { + // The function's own name `bar` must win over the inferred variable name. + // The resolver also looks this up in the *original* source so esbuild's + // name-stripped minified output still recovers `bar` at symbolication time. + const src = 'const x = function bar() {}'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.kind).toBe('function'); + expect(fn.astName).toBe('bar'); + expect(fn.contributesTo).toBe(false); + }); + + it('does not set computedKeyLoc for non-computed LHS', () => { + expect(parseJsScopeTree('const x = () => {}')[0].computedKeyLoc).toBeNull(); + expect(parseJsScopeTree('obj.foo = () => {}')[0].computedKeyLoc).toBeNull(); + }); + + it('captures lhsText for a simple variable assignment', () => { + const src = 'const foo = () => {}'; + const scopes = parseJsScopeTree(src); + expect(scopes[0].lhsText).toBe('foo'); + }); + + it('captures lhsText for a dotted member assignment', () => { + const src = 'Watcher.prototype.run = function () {}'; + const scopes = parseJsScopeTree(src); + expect(scopes[0].lhsText).toBe('Watcher.prototype.run'); + }); + + it('captures lhsText for a computed member assignment', () => { + const src = 'obj[key] = (...args) => f(...args)'; + const scopes = parseJsScopeTree(src); + expect(scopes[0].lhsText).toBe('obj[key]'); + }); + + it('captures lhsText for a wrap-pattern assignment', () => { + // The wrap wrapper doesn't change the LHS. The lhsText is still the + // assignment target, even though contributesTo is true. + const src = 'const observer = new C(() => {})'; + const scopes = parseJsScopeTree(src); + expect(scopes[0].lhsText).toBe('observer'); + expect(scopes[0].contributesTo).toBe(true); + }); + + it('captures lhsText for `this.foo = ...`', () => { + const src = 'this.handler = function () {}'; + const scopes = parseJsScopeTree(src); + expect(scopes[0].lhsText).toBe('this.handler'); + }); + + it('does not set lhsText for non-inferred scopes', () => { + // Plain function declarations / arrows without an assignment target + // shouldn't carry lhsText. + expect(parseJsScopeTree('function foo() {}')[0].lhsText).toBeNull(); + expect(parseJsScopeTree('[x => x]')[0].lhsText).toBeNull(); + }); + + it('captures lhsText for `const foo = async function (data) {}`', () => { + // Regression: there was a window where the resolver picked "async" as the + // function name (via a funcOffset probe landing on the `async` keyword). + // The inferred scope must carry the variable name as lhsText so the + // original-source lookup short-circuits the compiled-side probes. + const src = 'const getElementRects = async function (data) {}'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.kind).toBe('function'); + expect(fn.astName).toBeNull(); + expect(fn.lhsText).toBe('getElementRects'); + expect(fn.contributesTo).toBe(false); + }); + + it('captures lhsText for `const foo = async (x) => x`', () => { + const src = 'const getElementRects = async (data) => data'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.kind).toBe('arrow'); + expect(fn.astName).toBeNull(); + expect(fn.lhsText).toBe('getElementRects'); + }); + + it('infers name through optional call: `const cb = wrap?.(() => {})`', () => { + // Optional calls share the `CallExpression` node in @lezer/javascript, so + // they should already get wrap-pattern inference. + const src = 'const cb = wrap?.(() => {})'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.kind).toBe('arrow'); + expect(fn.contributesTo).toBe(true); + expect(fn.lhsText).toBe('cb'); + }); + + it('infers name through await: `const x = await wrap(() => {})`', () => { + const src = 'const x = await wrap(() => {})'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.kind).toBe('arrow'); + expect(fn.contributesTo).toBe(true); + expect(fn.lhsText).toBe('x'); + }); + + it('infers name through tagged template: `const x = tag`${() => {}}``', () => { + const src = 'const x = tag`a${() => {}}b`'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(1); + + const fn = scopes[0]; + expect(fn.kind).toBe('arrow'); + expect(fn.contributesTo).toBe(true); + expect(fn.lhsText).toBe('x'); + }); + + it('infers names for every interpolated arrow in a tagged template', () => { + const src = 'const x = tag`a${() => 1}b${() => 2}c`'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(2); + + for (const fn of scopes) { + expect(fn.kind).toBe('arrow'); + expect(fn.contributesTo).toBe(true); + expect(fn.lhsText).toBe('x'); + } + }); + + it('parses a TypeScript function with type annotations using the ts dialect', () => { + // Without the `ts` dialect, the parameter and return type annotations + // produce Error nodes that prevent astName recovery. + const src = 'function foo(x: number, y: string): void { return; }'; + const scopes = parseJsScopeTree(src, 'ts'); + expect(scopes).toHaveLength(1); + expect(scopes[0].astName).toBe('foo'); + expect(scopes[0].kind).toBe('function'); + }); + + it('parses a TSX arrow returning JSX with the `ts jsx` dialect', () => { + const src = + 'const Foo = (props: { name: string }) =>
{props.name}
;'; + const scopes = parseJsScopeTree(src, 'ts jsx'); + expect(scopes).toHaveLength(1); + expect(scopes[0].kind).toBe('arrow'); + expect(scopes[0].lhsText).toBe('Foo'); + }); +}); + +describe('dialectForFilename', () => { + it('returns ts for .ts/.mts/.cts', () => { + expect(dialectForFilename('foo.ts')).toBe('ts'); + expect(dialectForFilename('foo.mts')).toBe('ts'); + expect(dialectForFilename('foo.cts')).toBe('ts'); + }); + + it('returns ts jsx for .tsx', () => { + expect(dialectForFilename('foo.tsx')).toBe('ts jsx'); + }); + + it('returns jsx for .jsx', () => { + expect(dialectForFilename('foo.jsx')).toBe('jsx'); + }); + + it('returns empty string for plain js extensions', () => { + expect(dialectForFilename('foo.js')).toBe(''); + expect(dialectForFilename('foo.mjs')).toBe(''); + expect(dialectForFilename('noext')).toBe(''); + }); + + it('matches case-insensitively', () => { + expect(dialectForFilename('FOO.TSX')).toBe('ts jsx'); + expect(dialectForFilename('Bar.TS')).toBe('ts'); + }); + + it('handles URL-style source paths', () => { + expect(dialectForFilename('webpack:///./src/utils.ts')).toBe('ts'); + expect(dialectForFilename('../src/Component.tsx')).toBe('ts jsx'); + }); +}); + +describe('findInnermostFunctionScope', () => { + it('returns null when no scope contains the offset', () => { + const src = 'const x = 1;'; + const scopes = parseJsScopeTree(src); + expect(findInnermostFunctionScope(scopes, 0)).toBeNull(); + }); + + it('finds the innermost scope for a nested function', () => { + const src = 'function outer() { function inner() {} }'; + const scopes = parseJsScopeTree(src); + + // Offset inside 'inner'. Pick a position inside `function inner() {}` + // 'inner' starts at 28 in the source + const innerOffset = src.indexOf('inner'); + const path = findInnermostFunctionScope(scopes, innerOffset); + expect(path).not.toBeNull(); + expect(path![0].astName).toBe('inner'); + expect(path![1].astName).toBe('outer'); + }); + + it('finds the second of several non-overlapping top-level scopes', () => { + // Exercises the bisection: three siblings, offset inside the second. + const src = 'function a() {} function b() {} function c() {}'; + const scopes = parseJsScopeTree(src); + expect(scopes).toHaveLength(3); + + const bOffset = src.indexOf('function b'); + const path = findInnermostFunctionScope(scopes, bOffset); + expect(path).not.toBeNull(); + expect(path).toHaveLength(1); + expect(path![0].astName).toBe('b'); + }); + + it('returns null for an offset between two sibling scopes', () => { + const src = 'function a() {} function b() {}'; + const scopes = parseJsScopeTree(src); + // Position 15 is the space between the two functions. + expect(findInnermostFunctionScope(scopes, 15)).toBeNull(); + }); +}); diff --git a/src/test/unit/source-map-worker-mock.test.ts b/src/test/unit/source-map-worker-mock.test.ts new file mode 100644 index 0000000000..8349526cfd --- /dev/null +++ b/src/test/unit/source-map-worker-mock.test.ts @@ -0,0 +1,40 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Verifies that the source-map worker spawn is mocked at the build/test-infra +// level (via src/test/setup.ts). Without this mock, dispatching +// doSourceMapSymbolication with non-empty maps would attempt to load the +// nonexistent /source-map.worker.js bundle, throw ENOENT inside the worker +// thread, and leave the dispatched Promise hanging until Jest's per-test +// timeout fires. + +import { doSourceMapSymbolication } from '../../actions/source-map-symbolication'; +import { storeWithProfile } from '../fixtures/stores'; +import { getProfileFromTextSamples } from '../fixtures/profiles/processed-profile'; +import type { RawSourceMap } from 'source-map'; + +describe('source map worker stub', function () { + it('does not hang when doSourceMapSymbolication is dispatched with a non-empty map', async function () { + const { profile } = getProfileFromTextSamples('A'); + const { dispatch } = storeWithProfile(profile); + const fakeMap: RawSourceMap = { + version: 3, + file: 'a.js', + sources: ['a.ts'], + names: [], + mappings: '', + }; + + // Without the worker mock, this dispatch never resolves. The stub worker + // responds with { type: 'no-op' } so the dispatch finishes cleanly. + await expect( + dispatch( + doSourceMapSymbolication( + new Map([[0, fakeMap]]), + new Map([[0, 'function a(){}\n']]) + ) + ) + ).resolves.toBeUndefined(); + }); +}); diff --git a/src/types/actions.ts b/src/types/actions.ts index e49eddc7ec..205b5b4063 100644 --- a/src/types/actions.ts +++ b/src/types/actions.ts @@ -14,6 +14,10 @@ import type { IndexIntoLibs, PageList, IndexIntoSourceTable, + FuncTable, + FrameTable, + SourceLocationTable, + SourceTable, } from './profile'; import type { Thread, @@ -442,6 +446,18 @@ type ReceiveProfileAction = | { readonly type: 'UPDATE_PAGES'; readonly newPages: PageList; + } + | { readonly type: 'START_SOURCE_MAP_FETCHING' } + | { readonly type: 'DONE_SOURCE_MAP_FETCHING' } + | { readonly type: 'START_SOURCE_MAP_SYMBOLICATION' } + | { readonly type: 'SOURCE_MAP_SYMBOLICATION_FAILED' } + | { + readonly type: 'BULK_SOURCE_MAP_SYMBOLICATION'; + readonly newFuncTable: FuncTable; + readonly newFrameTable: FrameTable; + readonly newOriginalLocation: SourceLocationTable; + readonly newSources: SourceTable; + readonly newStringArray: string[]; }; type UrlEnhancerAction = diff --git a/src/types/globals/global.d.ts b/src/types/globals/global.d.ts index 969c46442c..676f09189e 100644 --- a/src/types/globals/global.d.ts +++ b/src/types/globals/global.d.ts @@ -4,6 +4,7 @@ // Added by esbuild's define option declare const AVAILABLE_STAGING_LOCALES: string[] | null; +declare const SOURCE_MAP_WORKER_PATH: string; declare module '*.worker.js' { const content: string; diff --git a/src/types/profile-derived.ts b/src/types/profile-derived.ts index cc641fbe78..2009afd63b 100644 --- a/src/types/profile-derived.ts +++ b/src/types/profile-derived.ts @@ -33,6 +33,7 @@ import type { SourceTable, IndexIntoSourceTable, CounterDisplayConfig, + SourceLocationTable, } from './profile'; import type { IndexedArray } from './utils'; import type { BitSet } from '../utils/bitset'; @@ -109,6 +110,8 @@ export type Thread = { // Sources for profiles are collected into a single table, containing file sources // with their UUIDs and filenames. sources: SourceTable; + // Source map symbolication results, copied from profile.shared.originalLocation. + originalLocation: SourceLocationTable; // The stack samples collected for this thread. This field is different from // RawThread in that the `time` column is always present. samples: SamplesTable; diff --git a/src/types/profile.ts b/src/types/profile.ts index 04d8a2ee79..b0888e04d0 100644 --- a/src/types/profile.ts +++ b/src/types/profile.ts @@ -23,6 +23,7 @@ export type IndexIntoNativeSymbolTable = number; export type IndexIntoCategoryList = number; export type IndexIntoSubcategoryListForCategory = number; export type IndexIntoSourceTable = number; +export type IndexIntoSourceLocationTable = number; export type ThreadIndex = number; // The Tid is most often a number. However in some cases such as merged profiles // we could generate a string. @@ -296,6 +297,12 @@ export type FrameTable = { line: (number | null)[]; column: (number | null)[]; + + // Index into the originalLocation table, or null if not source-mapped. + // Points to the original source file, line, and column for this frame's + // execution point. + originalLocation: Array; + length: number; }; @@ -342,6 +349,11 @@ export type FuncTable = { lineNumber: Array; columnNumber: Array; + // Index into the originalLocation table, or null if not source-mapped. + // Points to the original source file, line, and column for this function's + // definition. + originalLocation: Array; + length: number; }; @@ -980,6 +992,28 @@ export type SourceTable = { startLine: Array; startColumn: Array; sourceMapURL: Array; + // Original source file contents from source map sourcesContent, or null if + // not available. Stored for offline source view when a profile is shared. + content: Array; +}; + +/** + * Table holding source locations, currently populated from source map + * symbolication. Each row stores a (source, line, column) triple. Frames and + * funcs index into this table via their `originalLocation` column to record + * the pre-compilation counterpart to their inline (generated) line/column. + */ +export type SourceLocationTable = { + // Source file index. + // For funcs: the file where the function is defined. + // For frames: the source file for the execution point. Usually matches the + // func's source, but can differ for inlined code. + source: IndexIntoSourceTable[]; + // 1-based line number. + line: number[]; + // 1-based column number. + column: number[]; + length: number; }; export type RawProfileSharedData = { @@ -994,6 +1028,8 @@ export type RawProfileSharedData = { // Optional sources table for JS source UUID to URL mapping. // Added for UUID-based source fetching. sources: SourceTable; + // Source map symbolication results, shared across all threads. + originalLocation: SourceLocationTable; }; /** diff --git a/src/types/state.ts b/src/types/state.ts index 7e47f287ae..38eca24509 100644 --- a/src/types/state.ts +++ b/src/types/state.ts @@ -51,6 +51,10 @@ export type Reducer = (state: T | undefined, action: Action) => T; export type UploadedProfileInformation = ImportedUploadedProfileInformation; export type SymbolicationStatus = 'DONE' | 'SYMBOLICATING'; +export type SourceMapSymbolicationStatus = + | 'INACTIVE' + | 'FETCHING' + | 'SYMBOLICATING'; export type ThreadViewOptions = { readonly selectedNonInvertedCallNodePath: CallNodePath; readonly selectedInvertedCallNodePath: CallNodePath; @@ -94,6 +98,7 @@ export type ProfileViewState = { readonly viewOptions: { perThread: ThreadViewOptionsPerThreads; symbolicationStatus: SymbolicationStatus; + sourceMapSymbolicationStatus: SourceMapSymbolicationStatus; waitingForLibs: Set; previewSelection: PreviewSelection | null; scrollToSelectionGeneration: number; diff --git a/src/utils/line-offsets.ts b/src/utils/line-offsets.ts new file mode 100644 index 0000000000..b754efdb3c --- /dev/null +++ b/src/utils/line-offsets.ts @@ -0,0 +1,46 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import { bisectionRight } from './bisect'; + +/** + * Build a line-offset table for binary search. + * `lineOffsets[i]` is the character offset of the start of line `i` (0-based). + */ +export function buildLineOffsets(text: string): number[] { + const offsets = [0]; + let pos = 0; + for (;;) { + const next = text.indexOf('\n', pos); + if (next === -1) { + break; + } + offsets.push(next + 1); + pos = next + 1; + } + return offsets; +} + +/** + * Convert a character offset to a 0-based { line, col } position. + * Uses binary search over the line-offset table. + */ +export function offsetToLineCol( + offset: number, + lineOffsets: number[] +): { line: number; col: number } { + const line = bisectionRight(lineOffsets, offset) - 1; + return { line, col: offset - lineOffsets[line] }; +} + +/** + * Convert a 0-based { line, col } position back to a character offset. + */ +export function lineColToOffset( + line: number, + col: number, + lineOffsets: number[] +): number { + return lineOffsets[line] + col; +} diff --git a/src/utils/query-api.ts b/src/utils/query-api.ts index a72764dfe1..975e2d4098 100644 --- a/src/utils/query-api.ts +++ b/src/utils/query-api.ts @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import type { RawSourceMap } from 'source-map'; import type { MixedObject, ApiQueryError } from 'firefox-profiler/types'; import type { BrowserConnection } from 'firefox-profiler/app-logic/browser-connection'; @@ -25,6 +26,10 @@ export interface ExternalCommunicationDelegate { ): Promise; fetchJSSourceFromBrowser(source: string): Promise; + + // Get source map of the given source directly from the browser. + // Requires WebChannel version 7+. + fetchSourceMapFromBrowser(sourceId: string): Promise; } export type ApiQueryResult = @@ -169,4 +174,13 @@ export class RegularExternalCommunicationDelegate implements ExternalCommunicati this._callbacks.onBeginBrowserConnectionQuery(); return browserConnection.getJSSource(source); } + + fetchSourceMapFromBrowser(sourceId: string): Promise { + const browserConnection = this._browserConnection; + if (browserConnection === null) { + throw new Error('No connection to the browser.'); + } + this._callbacks.onBeginBrowserConnectionQuery(); + return browserConnection.getSourceMap(sourceId); + } } diff --git a/yarn.lock b/yarn.lock index 843c7cf975..47ab22642e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1801,10 +1801,10 @@ dependencies: vary "^1.1.2" -"@lezer/common@^1.0.0", "@lezer/common@^1.3.0", "@lezer/common@^1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@lezer/common/-/common-1.5.0.tgz#db227b596260189b67ba286387d9dc81fb07c70b" - integrity sha512-PNGcolp9hr4PJdXR4ix7XtixDrClScvtSCYW3rQG106oVMOOI+jFb+0+J3mbeL/53g1Zd6s0kJzaw6Ri68GmAA== +"@lezer/common@^1.0.0", "@lezer/common@^1.2.0", "@lezer/common@^1.3.0", "@lezer/common@^1.5.0": + version "1.5.2" + resolved "https://registry.yarnpkg.com/@lezer/common/-/common-1.5.2.tgz#d6840db13779e3f1b42e70c9a97c4086d12fae22" + integrity sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ== "@lezer/cpp@^1.0.0": version "1.0.0" @@ -1814,25 +1814,26 @@ "@lezer/highlight" "^1.0.0" "@lezer/lr" "^1.0.0" -"@lezer/highlight@^1.0.0", "@lezer/highlight@^1.2.3": +"@lezer/highlight@^1.0.0", "@lezer/highlight@^1.1.3", "@lezer/highlight@^1.2.3": version "1.2.3" resolved "https://registry.yarnpkg.com/@lezer/highlight/-/highlight-1.2.3.tgz#a20f324b71148a2ea9ba6ff42e58bbfaec702857" integrity sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g== dependencies: "@lezer/common" "^1.3.0" -"@lezer/javascript@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@lezer/javascript/-/javascript-1.0.0.tgz#60f0b6c295ef526a51fb33603403daad452d9470" - integrity sha512-RawBSrMD9yrVdrXWKn7hqo5BqgBaFelUx80i6p2/V0f+0THjncSSrRC6v3QWVv++RpqWT59L8ujKZjlExJq9xw== +"@lezer/javascript@^1.0.0", "@lezer/javascript@^1.5.4": + version "1.5.4" + resolved "https://registry.yarnpkg.com/@lezer/javascript/-/javascript-1.5.4.tgz#11746955f957d33c0933f17d7594db54a8b4beea" + integrity sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA== dependencies: - "@lezer/highlight" "^1.0.0" - "@lezer/lr" "^1.0.0" + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.1.3" + "@lezer/lr" "^1.3.0" -"@lezer/lr@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@lezer/lr/-/lr-1.0.0.tgz#89e30c1e710b8715ac5c847ad063418c51d6e750" - integrity sha512-k6DEqBh4HxqO/cVGedb6Ern6LS7K6IOzfydJ5WaqCR26v6UR9sIFyb6PS+5rPUs/mXgnBR/QQCW7RkyjSCMoQA== +"@lezer/lr@^1.0.0", "@lezer/lr@^1.3.0": + version "1.4.10" + resolved "https://registry.yarnpkg.com/@lezer/lr/-/lr-1.4.10.tgz#b3acc36e5ad049b74ddb7719594e7e74d9161ff5" + integrity sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A== dependencies: "@lezer/common" "^1.0.0" @@ -9224,6 +9225,11 @@ pump@^1.0.1: end-of-stream "^1.1.0" once "^1.3.1" +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== + punycode@^2.1.0, punycode@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" @@ -9253,7 +9259,7 @@ qrcode-terminal@^0.12.0: resolved "https://registry.yarnpkg.com/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz#bb5b699ef7f9f0505092a3748be4464fe71b5819" integrity sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ== -qs@^6.5.2: +qs@^6.12.3, qs@^6.5.2: version "6.15.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.15.2.tgz#fd55426d710403ddccc45e0f9eab16db7727ece9" integrity sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw== @@ -10233,6 +10239,11 @@ source-map@^0.6.0: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +source-map@^0.7.6: + version "0.7.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.6.tgz#a3658ab87e5b6429c8a1f3ba0083d4c61ca3ef02" + integrity sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ== + source-map@^0.8.0-beta.0: version "0.8.0-beta.0" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11" @@ -11399,6 +11410,14 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +url@^0.11.4: + version "0.11.4" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.4.tgz#adca77b3562d56b72746e76b330b7f27b6721f3c" + integrity sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg== + dependencies: + punycode "^1.4.1" + qs "^6.12.3" + use-sync-external-store@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz#adbc795d8eeb47029963016cefdf89dc799fcebc"