Skip to content

Commit

Permalink
Scopes fix WIP 2
Browse files Browse the repository at this point in the history
  • Loading branch information
overlookmotel committed Aug 18, 2020
1 parent 5ba5191 commit d0344d3
Show file tree
Hide file tree
Showing 2 changed files with 953 additions and 8 deletions.
34 changes: 26 additions & 8 deletions lib/serialize/records.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,28 +153,46 @@ function createScope(id, block, parent) {

/**
* Update parent for existing scope.
*
* The new parent may be above or below current parent in hierarchy.
* The parentage of previously-recorded scope's scope chain is interleaved with new parent's scope chain.
* e.g. If:
* - current scope has block ID 6 with ancestors 4 and 2,
* - new parent has block ID 5 with with ancestors 3 and 1
* then the chains are joined 5 -> 4 -> 3 -> 2 -> 1.
*
* The parent may be in a higher block than current scope.
* Current 2 -> 1, parent 3 -> 1 => result 3 -> 2 -> 1.
*
* NB Block IDs are always higher for nested scopes.
* i.e. for `function x() { function y() {} }`, `y` will have higher block ID than `x`.
* But scope IDs are essentially random - they are allocated in order functions are called,
* which may not be in line with how nested the function is.
*
* @param {Object} scope - Scope object
* @param {Object} parent - Scope object for new parent
* @returns {undefined}
*/
function updateScopeParent(scope, parent) {
while (true) { // eslint-disable-line no-constant-condition
if (scope === parent) return;

if (parent.block.id > scope.block.id) [scope, parent] = [parent, scope];
if (scope === parent) return;

while (true) { // eslint-disable-line no-constant-condition
// Locate where to insert parent in scope chain
let nextScope = scope.parent;
while (true) { // eslint-disable-line no-constant-condition
if (nextScope === parent) return;
if (nextScope.block.id < parent.block.id) break;
scope = nextScope;
nextScope = nextScope.parent;
nextScope = scope.parent;
}

const nextParent = scope.parent;
// Insert parent
scope.parent = parent;
scope = parent;
parent = nextParent;

// Step down scope and repeat.
// Swap `parent` and `scope` if necessary to ensure `scope` is nested inside `parent`.
scope = nextScope;
if (parent.block.id > scope.block.id) [scope, parent] = [parent, scope];
}
}

Expand Down
Loading

0 comments on commit d0344d3

Please sign in to comment.