|
4 | 4 |
|
5 | 5 | // @flow |
6 | 6 |
|
| 7 | +import { has } from "lodash"; |
7 | 8 | import { getSource } from "../../selectors"; |
8 | 9 | import { loadSourceText } from "../sources/loadSourceText"; |
9 | 10 | import { |
@@ -352,69 +353,81 @@ function buildGeneratedBindingList( |
352 | 353 | generatedAstScopes: SourceScope[], |
353 | 354 | thisBinding: ?BindingContents |
354 | 355 | ): Array<GeneratedBindingLocation> { |
355 | | - const clientScopes = []; |
356 | | - for (let s = scopes; s; s = s.parent) { |
357 | | - clientScopes.push(s); |
358 | | - } |
359 | | - |
360 | 356 | // The server's binding data doesn't include general 'this' binding |
361 | 357 | // information, so we manually inject the one 'this' binding we have into |
362 | 358 | // the normal binding data we are working with. |
363 | 359 | const frameThisOwner = generatedAstScopes.find( |
364 | 360 | generated => "this" in generated.bindings |
365 | 361 | ); |
366 | 362 |
|
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 | + : {}; |
371 | 368 |
|
372 | | - const bindings = s.bindings |
373 | | - ? Object.assign({}, ...s.bindings.arguments, s.bindings.variables) |
374 | | - : {}; |
| 369 | + clientScopes.push(bindings); |
| 370 | + } |
375 | 371 |
|
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); |
381 | 374 |
|
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 |
388 | 386 | }; |
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 | + }); |
405 | 397 | } |
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 | + } |
415 | 419 | } |
416 | | - return aStart.line - bStart.line; |
417 | | - }); |
| 420 | + } |
| 421 | + } |
418 | 422 |
|
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 | + }); |
420 | 433 | } |
0 commit comments