forked from firefox-devtools/debugger
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuildGeneratedBindingList.js
More file actions
125 lines (107 loc) · 3.97 KB
/
Copy pathbuildGeneratedBindingList.js
File metadata and controls
125 lines (107 loc) · 3.97 KB
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
/* 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/>. */
// @flow
import { has } from "lodash";
import type { SourceScope, BindingLocation } from "../../../workers/parser";
import type { Scope, BindingContents } from "../../../types";
import { createObjectClient } from "../../../client/firefox";
import { locColumn } from "./locColumn";
export type GeneratedBindingLocation = {
name: string,
loc: BindingLocation,
desc: () => Promise<BindingContents | null>
};
export function buildGeneratedBindingList(
scopes: Scope,
generatedAstScopes: SourceScope[],
thisBinding: ?BindingContents
): Array<GeneratedBindingLocation> {
// The server's binding data doesn't include general 'this' binding
// information, so we manually inject the one 'this' binding we have into
// the normal binding data we are working with.
const frameThisOwner = generatedAstScopes.find(
generated => "this" in generated.bindings
);
let globalScope = null;
const clientScopes = [];
for (let s = scopes; s; s = s.parent) {
const bindings = s.bindings
? Object.assign({}, ...s.bindings.arguments, s.bindings.variables)
: {};
clientScopes.push(bindings);
globalScope = s;
}
const generatedMainScopes = generatedAstScopes.slice(0, -2);
const generatedGlobalScopes = generatedAstScopes.slice(-2);
const clientMainScopes = clientScopes.slice(0, generatedMainScopes.length);
const clientGlobalScopes = clientScopes.slice(generatedMainScopes.length);
// Map the main parsed script body using the nesting hierarchy of the
// generated and client scopes.
const generatedBindings = generatedMainScopes.reduce((acc, generated, i) => {
const bindings = clientMainScopes[i];
if (generated === frameThisOwner && thisBinding) {
bindings.this = {
value: thisBinding
};
}
for (const name of Object.keys(generated.bindings)) {
// If there is no 'this' value, we exclude the binding entirely.
// Otherwise it would pass through as found, but "(unscoped)", causing
// the search logic to stop with a match.
if (name === "this" && !bindings[name]) {
continue;
}
const { refs } = generated.bindings[name];
for (const loc of refs) {
acc.push({
name,
loc,
desc: () => Promise.resolve(bindings[name] || null)
});
}
}
return acc;
}, []);
// Bindings in the global/lexical global of the generated code may or
// may not be the real global if the generated code is running inside
// of an evaled context. To handle this, we just look up the client scope
// hierarchy to find the closest binding with that name.
for (const generated of generatedGlobalScopes) {
for (const name of Object.keys(generated.bindings)) {
const { refs } = generated.bindings[name];
const bindings = clientGlobalScopes.find(b => has(b, name));
for (const loc of refs) {
if (bindings) {
generatedBindings.push({
name,
loc,
desc: () => Promise.resolve(bindings[name])
});
} else {
const globalGrip = globalScope && globalScope.object;
if (globalGrip) {
// Should always exist, just checking to keep Flow happy.
generatedBindings.push({
name,
loc,
desc: async () => {
const objectClient = createObjectClient(globalGrip);
return (await objectClient.getProperty(name)).descriptor;
}
});
}
}
}
}
}
// Sort so we can binary-search.
return generatedBindings.sort((a, b) => {
const aStart = a.loc.start;
const bStart = b.loc.start;
if (aStart.line === bStart.line) {
return locColumn(aStart) - locColumn(bStart);
}
return aStart.line - bStart.line;
});
}