Bug 1633529 - Improve flow coverage of debugger's actions r=loganfsmyth

Differential Revision: https://phabricator.services.mozilla.com/D72734
This commit is contained in:
David Walsh 2020-05-08 18:36:30 +00:00
parent f593e8a09b
commit 623b2f52f6
17 changed files with 79 additions and 46 deletions

View File

@ -19,7 +19,9 @@ import type { AstLocation } from "../../workers/parser";
import type { ThunkArgs } from "../types"; import type { ThunkArgs } from "../types";
import type { Context, SourceLocation } from "../../types"; import type { Context, SourceLocation } from "../../types";
function getOutOfScopeLines(outOfScopeLocations: ?(AstLocation[])) { function getOutOfScopeLines(
outOfScopeLocations: ?(AstLocation[])
): ?(AstLocation[]) {
if (!outOfScopeLocations) { if (!outOfScopeLocations) {
return null; return null;
} }

View File

@ -37,10 +37,14 @@ import { fulfilled } from "../../utils/async-value";
import type { ThunkArgs } from "../../actions/types"; import type { ThunkArgs } from "../../actions/types";
import { loadSourceActorBreakpointColumns } from "../source-actors"; import { loadSourceActorBreakpointColumns } from "../source-actors";
type LocationsList = {
number: ?(number[]),
};
async function mapLocations( async function mapLocations(
generatedLocations: SourceLocation[], generatedLocations: SourceLocation[],
{ sourceMaps }: ThunkArgs { sourceMaps }: ThunkArgs
) { ): Promise<MappedLocation[]> {
if (generatedLocations.length == 0) { if (generatedLocations.length == 0) {
return []; return [];
} }
@ -56,18 +60,24 @@ async function mapLocations(
} }
// Filter out positions, that are not in the original source Id // Filter out positions, that are not in the original source Id
function filterBySource(positions: MappedLocation[], sourceId: SourceId) { function filterBySource(
positions: MappedLocation[],
sourceId: SourceId
): MappedLocation[] {
if (!isOriginalId(sourceId)) { if (!isOriginalId(sourceId)) {
return positions; return positions;
} }
return positions.filter(position => position.location.sourceId == sourceId); return positions.filter(position => position.location.sourceId == sourceId);
} }
function filterByUniqLocation(positions: MappedLocation[]) { function filterByUniqLocation(positions: MappedLocation[]): MappedLocation[] {
return uniqBy(positions, ({ location }) => makeBreakpointId(location)); return uniqBy(positions, ({ location }) => makeBreakpointId(location));
} }
function convertToList(results, source: Source) { function convertToList(
results: LocationsList,
source: Source
): SourceLocation[] {
const { id, url } = source; const { id, url } = source;
const positions = []; const positions = [];
@ -112,7 +122,7 @@ async function _setBreakpointPositions(
sourceId: SourceId, sourceId: SourceId,
line, line,
thunkArgs: ThunkArgs thunkArgs: ThunkArgs
) { ): Promise<void> {
const { client, dispatch, getState, sourceMaps } = thunkArgs; const { client, dispatch, getState, sourceMaps } = thunkArgs;
let generatedSource = getSource(getState(), sourceId); let generatedSource = getSource(getState(), sourceId);
if (!generatedSource) { if (!generatedSource) {
@ -199,7 +209,7 @@ async function _setBreakpointPositions(
}); });
} }
function generatedSourceActorKey(state, sourceId: SourceId) { function generatedSourceActorKey(state, sourceId: SourceId): string {
const generatedSource = getSource( const generatedSource = getSource(
state, state,
isOriginalId(sourceId) ? originalToGeneratedId(sourceId) : sourceId isOriginalId(sourceId) ? originalToGeneratedId(sourceId) : sourceId

View File

@ -37,6 +37,7 @@ import type {
SourceLocation, SourceLocation,
Context, Context,
} from "../../types"; } from "../../types";
import type { State } from "../../reducers/types";
// This file has the primitive operations used to modify individual breakpoints // This file has the primitive operations used to modify individual breakpoints
// and keep them in sync with the breakpoints installed on server threads. These // and keep them in sync with the breakpoints installed on server threads. These
@ -62,7 +63,7 @@ import type {
// breakpoint will be added to the reducer, to restore the above invariant. // breakpoint will be added to the reducer, to restore the above invariant.
// See syncBreakpoint.js for more. // See syncBreakpoint.js for more.
function clientSetBreakpoint(client, state, breakpoint: Breakpoint) { function clientSetBreakpoint(client, state: State, breakpoint: Breakpoint) {
const breakpointLocation = makeBreakpointLocation( const breakpointLocation = makeBreakpointLocation(
state, state,
breakpoint.generatedLocation breakpoint.generatedLocation
@ -72,7 +73,7 @@ function clientSetBreakpoint(client, state, breakpoint: Breakpoint) {
function clientRemoveBreakpoint( function clientRemoveBreakpoint(
client, client,
state, state: State,
generatedLocation: SourceLocation generatedLocation: SourceLocation
) { ) {
const breakpointLocation = makeBreakpointLocation(state, generatedLocation); const breakpointLocation = makeBreakpointLocation(state, generatedLocation);

View File

@ -12,7 +12,7 @@ export default function remapLocations(
breakpoints: Breakpoint[], breakpoints: Breakpoint[],
sourceId: SourceId, sourceId: SourceId,
sourceMaps: SourceMaps sourceMaps: SourceMaps
) { ): Promise<Breakpoint[]> {
const sourceBreakpoints: Promise<Breakpoint>[] = breakpoints.map( const sourceBreakpoints: Promise<Breakpoint>[] = breakpoints.map(
async breakpoint => { async breakpoint => {
if (breakpoint.location.sourceId !== sourceId) { if (breakpoint.location.sourceId !== sourceId) {

View File

@ -5,6 +5,7 @@
// @flow // @flow
import type { ThunkArgs } from "../types"; import type { ThunkArgs } from "../types";
import type { PauseAction } from "../types/PauseAction";
import type { ThreadContext } from "../../types"; import type { ThreadContext } from "../../types";
/** /**
@ -15,7 +16,7 @@ import type { ThreadContext } from "../../types";
* @memberof actions/pause * @memberof actions/pause
* @static * @static
*/ */
export function breakOnNext(cx: ThreadContext) { export function breakOnNext(cx: ThreadContext): PauseAction {
return async ({ dispatch, getState, client }: ThunkArgs) => { return async ({ dispatch, getState, client }: ThunkArgs) => {
await client.breakOnNext(cx.thread); await client.breakOnNext(cx.thread);
return dispatch({ type: "BREAK_ON_NEXT", thread: cx.thread }); return dispatch({ type: "BREAK_ON_NEXT", thread: cx.thread });

View File

@ -17,7 +17,7 @@ import type { ThunkArgs } from "../types";
// b is a single position (line and column). // b is a single position (line and column).
// This function tests to see if the b position // This function tests to see if the b position
// falls within the range given in a. // falls within the range given in a.
function inHouseContainsPosition(a: Object, b: Object) { function inHouseContainsPosition(a: Object, b: Object): boolean {
const bColumn = b.column || 0; const bColumn = b.column || 0;
const startsBefore = const startsBefore =
a.start.line < b.line || a.start.line < b.line ||

View File

@ -16,10 +16,11 @@ import { validateThreadContext } from "../../utils/context";
import type { OriginalScope } from "../../utils/pause/mapScopes"; import type { OriginalScope } from "../../utils/pause/mapScopes";
import type { ThreadContext, Frame, Scope, Preview } from "../../types"; import type { ThreadContext, Frame, Scope, Preview } from "../../types";
import type { ThunkArgs } from "../types"; import type { ThunkArgs } from "../types";
import type { SourceScope } from "../../workers/parser/getScopes";
// We need to display all variables in the current functional scope so // We need to display all variables in the current functional scope so
// include all data for block scopes until the first functional scope // include all data for block scopes until the first functional scope
function getLocalScopeLevels(originalAstScopes): number { function getLocalScopeLevels(originalAstScopes: SourceScope[]): number {
let levels = 0; let levels = 0;
while ( while (
originalAstScopes[levels] && originalAstScopes[levels] &&

View File

@ -8,10 +8,10 @@ import { getFrames, getSymbols, getSource } from "../../selectors";
import { findClosestFunction } from "../../utils/ast"; import { findClosestFunction } from "../../utils/ast";
import type { ThreadContext } from "../../types"; import type { Frame, ThreadContext } from "../../types";
import type { ThunkArgs } from "../types"; import type { ThunkArgs } from "../types";
function mapDisplayName(frame, { getState }) { function mapDisplayName(frame: Frame, { getState }) {
if (frame.isOriginal) { if (frame.isOriginal) {
return frame; return frame;
} }

View File

@ -8,18 +8,28 @@ import { getFrames, getSource, getSelectedFrame } from "../../selectors";
import assert from "../../utils/assert"; import assert from "../../utils/assert";
import type { Frame, OriginalFrame, ThreadContext } from "../../types"; import type {
Frame,
FrameId,
OriginalFrame,
ThreadContext,
ThreadId,
} from "../../types";
import type { State } from "../../reducers/types"; import type { State } from "../../reducers/types";
import type { ThunkArgs } from "../types"; import type { ThunkArgs } from "../types";
import SourceMaps, { isGeneratedId } from "devtools-source-map"; import SourceMaps, { isGeneratedId } from "devtools-source-map";
function isFrameBlackboxed(state, frame) { function isFrameBlackboxed(state: State, frame: Frame): boolean {
const source = getSource(state, frame.location.sourceId); const source = getSource(state, frame.location.sourceId);
return source?.isBlackBoxed; return !!source?.isBlackBoxed;
} }
function getSelectedFrameId(state, thread, frames) { function getSelectedFrameId(
state: State,
thread: ThreadId,
frames: Frame[]
): ?FrameId {
let selectedFrame = getSelectedFrame(state, thread); let selectedFrame = getSelectedFrame(state, thread);
if (selectedFrame && !isFrameBlackboxed(state, selectedFrame)) { if (selectedFrame && !isFrameBlackboxed(state, selectedFrame)) {
return selectedFrame.id; return selectedFrame.id;
@ -32,7 +42,7 @@ function getSelectedFrameId(state, thread, frames) {
export function updateFrameLocation( export function updateFrameLocation(
frame: Frame, frame: Frame,
sourceMaps: typeof SourceMaps sourceMaps: typeof SourceMaps
) { ): Promise<Frame> {
if (frame.isOriginal) { if (frame.isOriginal) {
return Promise.resolve(frame); return Promise.resolve(frame);
} }

View File

@ -9,7 +9,10 @@
* @module actions/sources * @module actions/sources
*/ */
import { isOriginalId, originalToGeneratedId } from "devtools-source-map"; import SourceMaps, {
isOriginalId,
originalToGeneratedId,
} from "devtools-source-map";
import { recordEvent } from "../../utils/telemetry"; import { recordEvent } from "../../utils/telemetry";
import { features } from "../../utils/prefs"; import { features } from "../../utils/prefs";
import { getSourceActorsForSource } from "../../selectors"; import { getSourceActorsForSource } from "../../selectors";
@ -18,27 +21,27 @@ import { PROMISE } from "../utils/middleware/promise";
import type { Source, Context, SourceId } from "../../types"; import type { Source, Context, SourceId } from "../../types";
import type { ThunkArgs } from "../types"; import type { ThunkArgs } from "../types";
import type { State } from "../../reducers/types";
async function blackboxActors( async function blackboxActors(
state, state: State,
client, client,
sourceId: SourceId, sourceId: SourceId,
isBlackBoxed: boolean, isBlackBoxed: boolean,
range? range?
) { ): Promise<{ isBlackBoxed: boolean }> {
for (const actor of getSourceActorsForSource(state, sourceId)) { for (const actor of getSourceActorsForSource(state, sourceId)) {
await client.blackBox(actor, isBlackBoxed, range); await client.blackBox(actor, isBlackBoxed, range);
} }
return { isBlackBoxed: !isBlackBoxed }; return { isBlackBoxed: !isBlackBoxed };
} }
async function getSourceId(source: Source, sourceMaps) { async function getSourceId(source: Source, sourceMaps: typeof SourceMaps) {
let sourceId, range; let sourceId = source.id,
range;
if (features.originalBlackbox && isOriginalId(source.id)) { if (features.originalBlackbox && isOriginalId(source.id)) {
range = await sourceMaps.getFileGeneratedRange(source.id); range = await sourceMaps.getFileGeneratedRange(source.id);
sourceId = originalToGeneratedId(source.id); sourceId = originalToGeneratedId(source.id);
} else {
sourceId = source.id;
} }
return { sourceId, range }; return { sourceId, range };
} }

View File

@ -12,7 +12,7 @@ import type { Context, SourceId } from "../../types";
import type { ThunkArgs } from "../../actions/types"; import type { ThunkArgs } from "../../actions/types";
import { loadSourceActorBreakableLines } from "../source-actors"; import { loadSourceActorBreakableLines } from "../source-actors";
function calculateBreakableLines(positions) { function calculateBreakableLines(positions): number[] {
const lines = []; const lines = [];
for (const line in positions) { for (const line in positions) {
if (positions[line].length > 0) { if (positions[line].length > 0) {

View File

@ -30,13 +30,14 @@ import { Telemetry } from "devtools-modules";
import type { ThunkArgs } from "../types"; import type { ThunkArgs } from "../types";
import type { Source, Context, SourceId } from "../../types"; import type { Source, Context, SourceId } from "../../types";
import type { State } from "../../reducers/types";
// Measures the time it takes for a source to load // Measures the time it takes for a source to load
const loadSourceHistogram = "DEVTOOLS_DEBUGGER_LOAD_SOURCE_MS"; const loadSourceHistogram = "DEVTOOLS_DEBUGGER_LOAD_SOURCE_MS";
const telemetry = new Telemetry(); const telemetry = new Telemetry();
async function loadSource( async function loadSource(
state, state: State,
source: Source, source: Source,
{ sourceMaps, client, getState } { sourceMaps, client, getState }
): Promise<?{ ): Promise<?{

View File

@ -46,12 +46,13 @@ import type {
URL, URL,
} from "../../types"; } from "../../types";
import type { ThunkArgs } from "../types"; import type { ThunkArgs } from "../types";
import type { SourceAction } from "../types/SourceAction";
export const setSelectedLocation = ( export const setSelectedLocation = (
cx: Context, cx: Context,
source: Source, source: Source,
location: SourceLocation location: SourceLocation
) => ({ ): SourceAction => ({
type: "SET_SELECTED_LOCATION", type: "SET_SELECTED_LOCATION",
cx, cx,
source, source,
@ -62,15 +63,15 @@ export const setPendingSelectedLocation = (
cx: Context, cx: Context,
url: URL, url: URL,
options?: PartialPosition options?: PartialPosition
) => ({ ): SourceAction => ({
type: "SET_PENDING_SELECTED_LOCATION", type: "SET_PENDING_SELECTED_LOCATION",
cx, cx,
url, url,
line: options ? options.line : null, line: options?.line,
column: options ? options.column : null, column: options?.column,
}); });
export const clearSelectedLocation = (cx: Context) => ({ export const clearSelectedLocation = (cx: Context): SourceAction => ({
type: "CLEAR_SELECTED_LOCATION", type: "CLEAR_SELECTED_LOCATION",
cx, cx,
}); });

View File

@ -21,7 +21,7 @@ import type { Source, Context } from "../../types";
import type { Symbols } from "../../reducers/types"; import type { Symbols } from "../../reducers/types";
async function doSetSymbols( async function doSetSymbols(
cx, cx: Context,
source: Source, source: Source,
{ dispatch, getState, parser }: ThunkArgs { dispatch, getState, parser }: ThunkArgs
) { ) {

View File

@ -27,12 +27,15 @@ import type {
OrientationType, OrientationType,
SelectedPrimaryPaneTabType, SelectedPrimaryPaneTabType,
} from "../reducers/ui"; } from "../reducers/ui";
import type { UIAction } from "./types/UIAction";
export function setPrimaryPaneTab(tabName: SelectedPrimaryPaneTabType) { export function setPrimaryPaneTab(
tabName: SelectedPrimaryPaneTabType
): UIAction {
return { type: "SET_PRIMARY_PANE_TAB", tabName }; return { type: "SET_PRIMARY_PANE_TAB", tabName };
} }
export function closeActiveSearch() { export function closeActiveSearch(): UIAction {
return { return {
type: "TOGGLE_ACTIVE_SEARCH", type: "TOGGLE_ACTIVE_SEARCH",
value: null, value: null,
@ -157,7 +160,7 @@ export function flashLineRange(location: {
* @memberof actions/sources * @memberof actions/sources
* @static * @static
*/ */
export function clearHighlightLineRange() { export function clearHighlightLineRange(): UIAction {
return { return {
type: "CLEAR_HIGHLIGHT_LINES", type: "CLEAR_HIGHLIGHT_LINES",
}; };
@ -166,7 +169,7 @@ export function clearHighlightLineRange() {
export function openConditionalPanel( export function openConditionalPanel(
location: ?SourceLocation, location: ?SourceLocation,
log: boolean = false log: boolean = false
) { ): ?UIAction {
if (!location) { if (!location) {
return; return;
} }
@ -178,13 +181,13 @@ export function openConditionalPanel(
}; };
} }
export function closeConditionalPanel() { export function closeConditionalPanel(): UIAction {
return { return {
type: "CLOSE_CONDITIONAL_PANEL", type: "CLOSE_CONDITIONAL_PANEL",
}; };
} }
export function clearProjectDirectoryRoot(cx: Context) { export function clearProjectDirectoryRoot(cx: Context): UIAction {
return { return {
type: "SET_PROJECT_DIRECTORY_ROOT", type: "SET_PROJECT_DIRECTORY_ROOT",
cx, cx,
@ -224,18 +227,18 @@ export function setProjectDirectoryRoot(cx: Context, newRoot: string) {
}; };
} }
export function updateViewport() { export function updateViewport(): UIAction {
return { return {
type: "SET_VIEWPORT", type: "SET_VIEWPORT",
viewport: getLocationsInViewport(getEditor()), viewport: getLocationsInViewport(getEditor()),
}; };
} }
export function updateCursorPosition(cursorPosition: SourceLocation) { export function updateCursorPosition(cursorPosition: SourceLocation): UIAction {
return { type: "SET_CURSOR_POSITION", cursorPosition }; return { type: "SET_CURSOR_POSITION", cursorPosition };
} }
export function setOrientation(orientation: OrientationType) { export function setOrientation(orientation: OrientationType): UIAction {
return { type: "SET_ORIENTATION", orientation }; return { type: "SET_ORIENTATION", orientation };
} }

View File

@ -11,7 +11,7 @@ import {
import type { ThunkArgs } from "../../types"; import type { ThunkArgs } from "../../types";
function validateActionContext(getState, action) { function validateActionContext(getState, action): void {
if (action.type == "COMMAND" && action.status == "done") { if (action.type == "COMMAND" && action.status == "done") {
// The thread will have resumed execution since the action was initiated, // The thread will have resumed execution since the action was initiated,
// so just make sure we haven't navigated. // so just make sure we haven't navigated.

View File

@ -408,7 +408,7 @@ async function toggleEventLogging(logEventBreakpoints: boolean) {
); );
} }
function getAllThreadFronts() { function getAllThreadFronts(): ThreadFront[] {
const fronts = [currentThreadFront()]; const fronts = [currentThreadFront()];
for (const { threadFront } of (Object.values(targets): any)) { for (const { threadFront } of (Object.values(targets): any)) {
fronts.push(threadFront); fronts.push(threadFront);