Skip to content

Commit e9a3ced

Browse files
iocalebsjasonLaster
authored andcommitted
[CallStack] Simplify call stacks involving Babel async transforms (firefox-devtools#5518)
1 parent eae3cb9 commit e9a3ced

File tree

7 files changed

+367
-16
lines changed

7 files changed

+367
-16
lines changed

.eslintrc

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@
135135
}
136136
],
137137
// Maximum depth callbacks can be nested.
138-
"max-nested-callbacks": [2, 3],
138+
"max-nested-callbacks": [2, 4],
139139
// Don't limit the number of parameters that can be used in a function.
140140
"max-params": 0,
141141
// Don't limit the maximum number of statement allowed in a function. We

assets/images/Svg.js

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const svg = {
66
"angle-brackets": require("./angle-brackets.svg"),
77
angular: require("./angular.svg"),
88
arrow: require("./arrow.svg"),
9+
babel: require("./babel.svg"),
910
backbone: require("./backbone.svg"),
1011
blackBox: require("./blackBox.svg"),
1112
breakpoint: require("./breakpoint.svg"),

assets/images/babel.svg

+107
Loading

src/components/SecondaryPanes/Frames/Group.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,11 @@ import "./Group.css";
1414
import FrameComponent from "./Frame";
1515

1616
import type { LocalFrame } from "./types";
17-
import type { Frame } from "../../../types";
1817
import Badge from "../../shared/Badge";
1918

20-
type FrameLocationProps = { frame: Frame };
19+
type FrameLocationProps = { frame: LocalFrame };
2120
function FrameLocation({ frame }: FrameLocationProps) {
22-
const library = getLibraryFromUrl(frame);
21+
const library = frame.library || getLibraryFromUrl(frame);
2322
if (!library) {
2423
return null;
2524
}

src/selectors/getCallStackFrames.js

+8-8
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
getSourceInSources
99
} from "../reducers/sources";
1010
import { getFrames } from "../reducers/pause";
11-
import { annotateFrame } from "../utils/frame";
11+
import { annotateFrames } from "../utils/frame";
1212
import { isOriginalId } from "devtools-source-map";
1313
import { get } from "lodash";
1414
import type { Frame, Source } from "../types";
@@ -45,17 +45,17 @@ export function formatCallStackFrames(
4545
return null;
4646
}
4747

48-
return frames
48+
const formattedFrames = frames
4949
.filter(frame => getSourceForFrame(sources, frame))
5050
.map(frame => appendSource(sources, frame, selectedSource))
51-
.filter(frame => !get(frame, "source.isBlackBoxed"))
52-
.map(annotateFrame);
51+
.filter(frame => !get(frame, "source.isBlackBoxed"));
52+
53+
return annotateFrames(formattedFrames);
5354
}
5455

5556
export const getCallStackFrames = createSelector(
56-
getSelectedSource,
57-
getSources,
5857
getFrames,
59-
(selectedSource, sources, frames) =>
60-
formatCallStackFrames(frames, sources, selectedSource)
58+
getSources,
59+
getSelectedSource,
60+
formatCallStackFrames
6161
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
4+
5+
import { getCallStackFrames } from "../getCallStackFrames";
6+
import { fromJS } from "immutable";
7+
import { pullAt } from "lodash";
8+
9+
describe("getCallStackFrames selector", () => {
10+
describe("library annotation", () => {
11+
it("annotates React frames", () => {
12+
const state = {
13+
frames: [
14+
{ location: { sourceId: "source1" } },
15+
{ location: { sourceId: "source2" } },
16+
{ location: { sourceId: "source2" } }
17+
],
18+
sources: fromJS({
19+
source1: { id: "source1", url: "webpack:///src/App.js" },
20+
source2: {
21+
id: "source2",
22+
url: "webpack:///~/react-dom/lib/ReactCompositeComponent.js"
23+
}
24+
}),
25+
selectedSource: fromJS({
26+
id: "sourceId-originalSource"
27+
})
28+
};
29+
30+
const frames = getCallStackFrames.resultFunc(
31+
state.frames,
32+
state.sources,
33+
state.selectedSource,
34+
true
35+
);
36+
37+
expect(frames[0]).not.toHaveProperty("library");
38+
expect(frames[1]).toHaveProperty("library", "React");
39+
expect(frames[2]).toHaveProperty("library", "React");
40+
});
41+
42+
// Multiple Babel async frame groups occur when you have an async function
43+
// calling another async function (a common case).
44+
//
45+
// There are two possible frame groups that can occur depending on whether
46+
// one sets a breakpoint before or after an await
47+
it("annotates frames related to Babel async transforms", () => {
48+
const preAwaitGroup = [
49+
{
50+
displayName: "_callee$",
51+
location: { sourceId: "bundle" }
52+
},
53+
{
54+
displayName: "tryCatch",
55+
location: { sourceId: "regenerator" }
56+
},
57+
{
58+
displayName: "invoke",
59+
location: { sourceId: "regenerator" }
60+
},
61+
{
62+
displayName: "defineIteratorMethods/</prototype[method]",
63+
location: { sourceId: "regenerator" }
64+
},
65+
{
66+
displayName: "step",
67+
location: { sourceId: "bundle" }
68+
},
69+
{
70+
displayName: "_asyncToGenerator/</<",
71+
location: { sourceId: "bundle" }
72+
},
73+
{
74+
displayName: "Promise",
75+
location: { sourceId: "promise" }
76+
},
77+
{
78+
displayName: "_asyncToGenerator/<",
79+
location: { sourceId: "bundle" }
80+
},
81+
{
82+
displayName: "asyncAppFunction",
83+
location: { sourceId: "app" }
84+
}
85+
];
86+
87+
const postAwaitGroup = [
88+
{
89+
displayName: "_callee$",
90+
location: { sourceId: "bundle" }
91+
},
92+
{
93+
displayName: "tryCatch",
94+
location: { sourceId: "regenerator" }
95+
},
96+
{
97+
displayName: "invoke",
98+
location: { sourceId: "regenerator" }
99+
},
100+
{
101+
displayName: "defineIteratorMethods/</prototype[method]",
102+
location: { sourceId: "regenerator" }
103+
},
104+
{
105+
displayName: "step",
106+
location: { sourceId: "bundle" }
107+
},
108+
{
109+
displayName: "step/<",
110+
location: { sourceId: "bundle" }
111+
},
112+
{
113+
displayName: "run",
114+
location: { sourceId: "bundle" }
115+
},
116+
{
117+
displayName: "notify/<",
118+
location: { sourceId: "bundle" }
119+
},
120+
{
121+
displayName: "flush",
122+
location: { sourceId: "microtask" }
123+
}
124+
];
125+
126+
const state = {
127+
frames: [...preAwaitGroup, ...postAwaitGroup],
128+
sources: fromJS({
129+
app: { id: "app", url: "webpack///app.js" },
130+
bundle: { id: "bundle", url: "https://foo.com/bundle.js" },
131+
regenerator: {
132+
id: "regenerator",
133+
url: "webpack:///foo/node_modules/regenerator-runtime/runtime.js"
134+
},
135+
microtask: {
136+
id: "microtask",
137+
url: "webpack:///foo/node_modules/core-js/modules/_microtask.js"
138+
},
139+
promise: {
140+
id: "promise",
141+
url: "webpack///foo/node_modules/core-js/modules/es6.promise.js"
142+
}
143+
}),
144+
selectedSource: fromJS({
145+
id: "sourceId-originalSource"
146+
})
147+
};
148+
149+
const frames = getCallStackFrames.resultFunc(
150+
state.frames,
151+
state.sources,
152+
state.selectedSource
153+
);
154+
155+
const babelFrames = pullAt(frames, [
156+
1,
157+
2,
158+
3,
159+
4,
160+
5,
161+
6,
162+
7,
163+
10,
164+
11,
165+
12,
166+
13,
167+
14,
168+
15,
169+
16,
170+
17
171+
]);
172+
const otherFrames = frames;
173+
174+
expect(babelFrames).toEqual(
175+
Array(babelFrames.length).fill(
176+
expect.objectContaining({ library: "Babel" })
177+
)
178+
);
179+
expect(otherFrames).not.toEqual(
180+
Array(babelFrames.length).fill(
181+
expect.objectContaining({ library: "Babel" })
182+
)
183+
);
184+
});
185+
});
186+
});

0 commit comments

Comments
 (0)