Skip to content
This repository was archived by the owner on Jan 11, 2023. It is now read-only.

Commit 368520d

Browse files
loganfsmythjasonLaster
authored andcommitted
Handle original source maps used on code running inside an eval() (#5749)
1 parent 9886262 commit 368520d

File tree

12 files changed

+1651
-1352
lines changed

12 files changed

+1651
-1352
lines changed

src/actions/pause/mapScopes.js

Lines changed: 64 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
// @flow
66

7+
import { has } from "lodash";
78
import { getSource } from "../../selectors";
89
import { loadSourceText } from "../sources/loadSourceText";
910
import {
@@ -352,69 +353,81 @@ function buildGeneratedBindingList(
352353
generatedAstScopes: SourceScope[],
353354
thisBinding: ?BindingContents
354355
): Array<GeneratedBindingLocation> {
355-
const clientScopes = [];
356-
for (let s = scopes; s; s = s.parent) {
357-
clientScopes.push(s);
358-
}
359-
360356
// The server's binding data doesn't include general 'this' binding
361357
// information, so we manually inject the one 'this' binding we have into
362358
// the normal binding data we are working with.
363359
const frameThisOwner = generatedAstScopes.find(
364360
generated => "this" in generated.bindings
365361
);
366362

367-
const generatedBindings = clientScopes
368-
.reverse()
369-
.map((s, i) => {
370-
const generated = generatedAstScopes[generatedAstScopes.length - 1 - i];
363+
const clientScopes = [];
364+
for (let s = scopes; s; s = s.parent) {
365+
const bindings = s.bindings
366+
? Object.assign({}, ...s.bindings.arguments, s.bindings.variables)
367+
: {};
371368

372-
const bindings = s.bindings
373-
? Object.assign({}, ...s.bindings.arguments, s.bindings.variables)
374-
: {};
369+
clientScopes.push(bindings);
370+
}
375371

376-
if (generated === frameThisOwner && thisBinding) {
377-
bindings.this = {
378-
value: thisBinding
379-
};
380-
}
372+
const generatedMainScopes = generatedAstScopes.slice(0, -2);
373+
const generatedGlobalScopes = generatedAstScopes.slice(-2);
381374

382-
return {
383-
generated,
384-
client: {
385-
...s,
386-
bindings
387-
}
375+
const clientMainScopes = clientScopes.slice(0, generatedMainScopes.length);
376+
const clientGlobalScopes = clientScopes.slice(generatedMainScopes.length);
377+
378+
// Map the main parsed script body using the nesting hierarchy of the
379+
// generated and client scopes.
380+
const generatedBindings = generatedMainScopes.reduce((acc, generated, i) => {
381+
const bindings = clientMainScopes[i];
382+
383+
if (generated === frameThisOwner && thisBinding) {
384+
bindings.this = {
385+
value: thisBinding
388386
};
389-
})
390-
.slice(2)
391-
.reduce((acc, { client: { bindings }, generated }) => {
392-
// If the parser worker's result didn't match the client scopes,
393-
// there might not be a generated scope that matches.
394-
if (generated) {
395-
for (const name of Object.keys(generated.bindings)) {
396-
const { refs } = generated.bindings[name];
397-
for (const loc of refs) {
398-
acc.push({
399-
name,
400-
loc,
401-
desc: bindings[name] || null
402-
});
403-
}
404-
}
387+
}
388+
389+
for (const name of Object.keys(generated.bindings)) {
390+
const { refs } = generated.bindings[name];
391+
for (const loc of refs) {
392+
acc.push({
393+
name,
394+
loc,
395+
desc: bindings[name] || null
396+
});
405397
}
406-
return acc;
407-
}, [])
408-
// Sort so we can binary-search.
409-
.sort((a, b) => {
410-
const aStart = a.loc.start;
411-
const bStart = a.loc.start;
412-
413-
if (aStart.line === bStart.line) {
414-
return locColumn(aStart) - locColumn(bStart);
398+
}
399+
return acc;
400+
}, []);
401+
402+
// Bindings in the global/lexical global of the generated code may or
403+
// may not be the real global if the generated code is running inside
404+
// of an evaled context. To handle this, we just look up the client scope
405+
// hierarchy to find the closest binding with that name.
406+
for (const generated of generatedGlobalScopes) {
407+
for (const name of Object.keys(generated.bindings)) {
408+
const { refs } = generated.bindings[name];
409+
for (const loc of refs) {
410+
const bindings = clientGlobalScopes.find(b => has(b, name));
411+
412+
if (bindings) {
413+
generatedBindings.push({
414+
name,
415+
loc,
416+
desc: bindings[name]
417+
});
418+
}
415419
}
416-
return aStart.line - bStart.line;
417-
});
420+
}
421+
}
418422

419-
return generatedBindings;
423+
// Sort so we can binary-search.
424+
return generatedBindings.sort((a, b) => {
425+
const aStart = a.loc.start;
426+
const bStart = a.loc.start;
427+
428+
if (aStart.line === bStart.line) {
429+
return locColumn(aStart) - locColumn(bStart);
430+
}
431+
return aStart.line - bStart.line;
432+
});
420433
}

src/actions/tests/__snapshots__/ast.spec.js.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ Array [
4545
1,
4646
2,
4747
7,
48+
12,
4849
]
4950
`;
5051

src/test/mochitest/browser.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ support-files =
77
!/devtools/client/commandline/test/helpers.js
88
!/devtools/client/shared/test/shared-head.js
99
examples/babel/polyfill-bundle.js
10+
examples/babel/fixtures/eval-source-maps/output.js
11+
examples/babel/fixtures/eval-source-maps/output.js.map
1012
examples/babel/fixtures/for-of/output.js
1113
examples/babel/fixtures/for-of/output.js.map
1214
examples/babel/fixtures/shadowed-vars/output.js

src/test/mochitest/browser_dbg-babel-scopes.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,19 @@ add_task(async function() {
9393

9494
const dbg = await initDebugger("doc-babel.html");
9595

96+
await breakpointScopes(dbg, "eval-source-maps", { line: 14, column: 4 }, [
97+
"Block",
98+
["three", "5"],
99+
["two", "4"],
100+
"Block",
101+
["three", "3"],
102+
["two", "2"],
103+
"root",
104+
["one", "1"],
105+
"Module",
106+
"root()"
107+
]);
108+
96109
await breakpointScopes(dbg, "for-of", { line: 5, column: 4 }, [
97110
"For",
98111
["x", "1"],
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
export default function root() {
3+
var one = 1;
4+
let two = 2;
5+
const three = 3;
6+
7+
one;
8+
two;
9+
10+
{
11+
let two = 4;
12+
const three = 5;
13+
14+
console.log("pause here", one, two, three);
15+
}
16+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
var evalSourceMaps =
2+
/******/ (function(modules) { // webpackBootstrap
3+
/******/ // The module cache
4+
/******/ var installedModules = {};
5+
/******/
6+
/******/ // The require function
7+
/******/ function __webpack_require__(moduleId) {
8+
/******/
9+
/******/ // Check if module is in cache
10+
/******/ if(installedModules[moduleId]) {
11+
/******/ return installedModules[moduleId].exports;
12+
/******/ }
13+
/******/ // Create a new module (and put it into the cache)
14+
/******/ var module = installedModules[moduleId] = {
15+
/******/ i: moduleId,
16+
/******/ l: false,
17+
/******/ exports: {}
18+
/******/ };
19+
/******/
20+
/******/ // Execute the module function
21+
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
22+
/******/
23+
/******/ // Flag the module as loaded
24+
/******/ module.l = true;
25+
/******/
26+
/******/ // Return the exports of the module
27+
/******/ return module.exports;
28+
/******/ }
29+
/******/
30+
/******/
31+
/******/ // expose the modules object (__webpack_modules__)
32+
/******/ __webpack_require__.m = modules;
33+
/******/
34+
/******/ // expose the module cache
35+
/******/ __webpack_require__.c = installedModules;
36+
/******/
37+
/******/ // define getter function for harmony exports
38+
/******/ __webpack_require__.d = function(exports, name, getter) {
39+
/******/ if(!__webpack_require__.o(exports, name)) {
40+
/******/ Object.defineProperty(exports, name, {
41+
/******/ configurable: false,
42+
/******/ enumerable: true,
43+
/******/ get: getter
44+
/******/ });
45+
/******/ }
46+
/******/ };
47+
/******/
48+
/******/ // getDefaultExport function for compatibility with non-harmony modules
49+
/******/ __webpack_require__.n = function(module) {
50+
/******/ var getter = module && module.__esModule ?
51+
/******/ function getDefault() { return module['default']; } :
52+
/******/ function getModuleExports() { return module; };
53+
/******/ __webpack_require__.d(getter, 'a', getter);
54+
/******/ return getter;
55+
/******/ };
56+
/******/
57+
/******/ // Object.prototype.hasOwnProperty.call
58+
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
59+
/******/
60+
/******/ // __webpack_public_path__
61+
/******/ __webpack_require__.p = "";
62+
/******/
63+
/******/ // Load entry module and return exports
64+
/******/ return __webpack_require__(__webpack_require__.s = 0);
65+
/******/ })
66+
/************************************************************************/
67+
/******/ ([
68+
/* 0 */
69+
/***/ (function(module, exports, __webpack_require__) {
70+
71+
"use strict";
72+
eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = root;\nfunction root() {\n var one = 1;\n var two = 2;\n var three = 3;\n\n one;\n two;\n\n {\n var _two = 4;\n var _three = 5;\n\n console.log(\"pause here\", one, _two, _three);\n }\n}\nmodule.exports = exports[\"default\"];//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9maXh0dXJlcy9ldmFsLXNvdXJjZS1tYXBzL2lucHV0LmpzP2VkYzgiXSwibmFtZXMiOlsicm9vdCIsIm9uZSIsInR3byIsInRocmVlIiwiY29uc29sZSIsImxvZyJdLCJtYXBwaW5ncyI6Ijs7Ozs7a0JBQ3dCQSxJO0FBQVQsU0FBU0EsSUFBVCxHQUFnQjtBQUM3QixNQUFJQyxNQUFNLENBQVY7QUFDQSxNQUFJQyxNQUFNLENBQVY7QUFDQSxNQUFNQyxRQUFRLENBQWQ7O0FBRUFGO0FBQ0FDOztBQUVBO0FBQ0UsUUFBSUEsT0FBTSxDQUFWO0FBQ0EsUUFBTUMsU0FBUSxDQUFkOztBQUVBQyxZQUFRQyxHQUFSLENBQVksWUFBWixFQUEwQkosR0FBMUIsRUFBK0JDLElBQS9CLEVBQW9DQyxNQUFwQztBQUNEO0FBQ0YiLCJmaWxlIjoiMC5qcyIsInNvdXJjZXNDb250ZW50IjpbIlxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gcm9vdCgpIHtcbiAgdmFyIG9uZSA9IDE7XG4gIGxldCB0d28gPSAyO1xuICBjb25zdCB0aHJlZSA9IDM7XG5cbiAgb25lO1xuICB0d287XG5cbiAge1xuICAgIGxldCB0d28gPSA0O1xuICAgIGNvbnN0IHRocmVlID0gNTtcblxuICAgIGNvbnNvbGUubG9nKFwicGF1c2UgaGVyZVwiLCBvbmUsIHR3bywgdGhyZWUpO1xuICB9XG59XG5cblxuXG4vLyBXRUJQQUNLIEZPT1RFUiAvL1xuLy8gLi9maXh0dXJlcy9ldmFsLXNvdXJjZS1tYXBzL2lucHV0LmpzIl0sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///0\n");
73+
74+
/***/ })
75+
/******/ ]);

src/test/mochitest/examples/babel/fixtures/eval-source-maps/output.js.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/test/mochitest/examples/babel/webpack.config.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ module.exports = [
5151
const babelEnabled = name !== "webpackStandalone";
5252
const babelEnv = name !== "webpackModulesEs6";
5353
const babelModules = name !== "webpackModules";
54+
const devtool =
55+
name === "evalSourceMaps" ? "eval-source-map" : "source-map";
5456

5557
return {
5658
context: __dirname,
@@ -62,7 +64,7 @@ module.exports = [
6264
libraryTarget: "var",
6365
library: name
6466
},
65-
devtool: "sourcemap",
67+
devtool,
6668
module: {
6769
loaders: babelEnabled
6870
? [

src/test/mochitest/examples/doc-babel.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
<button onclick="classes()">Run classes</button>
1818
<script src="babel/fixtures/commonjs/output.js"></script>
1919
<button onclick="commonjs()">Run commonjs</button>
20+
<script src="babel/fixtures/eval-source-maps/output.js"></script>
21+
<button onclick="evalSourceMaps()">Run evalSourceMaps</button>
2022
<script src="babel/fixtures/flowtype-bindings/output.js"></script>
2123
<button onclick="flowtypeBindings()">Run flowtypeBindings</button>
2224
<script src="babel/fixtures/for-loops/output.js"></script>

src/utils/ast.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,6 @@ export function findEmptyLines(
4545
return [];
4646
}
4747
const lineCount = selectedSource.text.split("\n").length;
48-
const sourceLines = range(1, lineCount);
48+
const sourceLines = range(1, lineCount + 1);
4949
return without(sourceLines, ...breakpointLines);
5050
}

0 commit comments

Comments
 (0)