/* 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 . */ // @flow import { getSelectedFrameId, getSource, getSourceContent, isMapScopesEnabled, getSelectedFrame, getSelectedGeneratedScope, getSelectedOriginalScope, getThreadContext, } from "../../selectors"; import { loadSourceText } from "../sources/loadSourceText"; import { PROMISE } from "../utils/middleware/promise"; import assert from "../../utils/assert"; import { log } from "../../utils/log"; import { isGenerated, isOriginal } from "../../utils/source"; import type { Frame, Scope, ThreadContext, XScopeVariables, SourceLocation, } from "../../types"; import type { ThunkArgs } from "../types"; import { buildMappedScopes, type MappedFrameLocation, } from "../../utils/pause/mapScopes"; import { isFulfilled } from "../../utils/async-value"; import type { OriginalScope } from "../../utils/pause/mapScopes"; import { getMappedLocation } from "../../utils/source-maps"; const expressionRegex = /\bfp\(\)/g; export async function buildOriginalScopes( frame: Frame, client: any, cx: ThreadContext, frameId: any, generatedScopes: Promise ): Promise { if (!frame.originalVariables) { throw new TypeError("(frame.originalVariables: XScopeVariables)"); } const originalVariables: XScopeVariables = frame.originalVariables; const frameBase = originalVariables.frameBase || ""; const inputs = []; for (let i = 0; i < originalVariables.vars.length; i++) { const { expr } = originalVariables.vars[i]; const expression = expr ? expr.replace(expressionRegex, frameBase) : "void 0"; inputs[i] = expression; } const results = await client.evaluateExpressions(inputs, { frameId, thread: cx.thread, }); const variables = {}; for (let i = 0; i < originalVariables.vars.length; i++) { const { name } = originalVariables.vars[i]; variables[name] = { value: results[i].result }; } const bindings = { arguments: [], variables, }; const { actor } = await generatedScopes; const scope = { type: "function", scopeKind: "", actor, bindings, parent: null, function: null, block: null, }; return { mappings: {}, scope, }; } export function toggleMapScopes() { return async function({ dispatch, getState, client, sourceMaps }: ThunkArgs) { if (isMapScopesEnabled(getState())) { return dispatch({ type: "TOGGLE_MAP_SCOPES", mapScopes: false }); } dispatch({ type: "TOGGLE_MAP_SCOPES", mapScopes: true }); const cx = getThreadContext(getState()); if (getSelectedOriginalScope(getState(), cx.thread)) { return; } const scopes = getSelectedGeneratedScope(getState(), cx.thread); const frame = getSelectedFrame(getState(), cx.thread); if (!scopes || !frame) { return; } dispatch(mapScopes(cx, Promise.resolve(scopes.scope), frame)); }; } export function mapScopes( cx: ThreadContext, scopes: Promise, frame: Frame ) { return async function(thunkArgs: ThunkArgs) { const { dispatch, client, getState } = thunkArgs; assert(cx.thread == frame.thread, "Thread mismatch"); await dispatch({ type: "MAP_SCOPES", cx, thread: cx.thread, frame, [PROMISE]: (async function() { if (frame.isOriginal && frame.originalVariables) { const frameId = getSelectedFrameId(getState(), cx.thread); return buildOriginalScopes(frame, client, cx, frameId, scopes); } return dispatch(getMappedScopes(cx, scopes, frame)); })(), }); }; } export function getMappedScopes( cx: ThreadContext, scopes: ?Promise, frame: MappedFrameLocation ) { return async function(thunkArgs: ThunkArgs) { const { getState, dispatch } = thunkArgs; const generatedSource = getSource( getState(), frame.generatedLocation.sourceId ); const source = getSource(getState(), frame.location.sourceId); if ( !isMapScopesEnabled(getState()) || !source || !generatedSource || generatedSource.isWasm || source.isPrettyPrinted || isGenerated(source) ) { return null; } await dispatch(loadSourceText({ cx, source })); if (isOriginal(source)) { await dispatch(loadSourceText({ cx, source: generatedSource })); } try { const content = getSource(getState(), source.id) && getSourceContent(getState(), source.id); return await buildMappedScopes( source, content && isFulfilled(content) ? content.value : { type: "text", value: "", contentType: undefined }, frame, await scopes, thunkArgs ); } catch (e) { log(e); return null; } }; } export function getMappedScopesForLocation(location: SourceLocation) { return async function(thunkArgs: ThunkArgs) { const { dispatch, getState, sourceMaps } = thunkArgs; const cx = getThreadContext(getState()); const mappedLocation: $Shape = await getMappedLocation( getState(), sourceMaps, location ); return dispatch(getMappedScopes(cx, null, mappedLocation)); }; }