Skip to content

Commit

Permalink
with statements WIP 3
Browse files Browse the repository at this point in the history
  • Loading branch information
overlookmotel committed Aug 29, 2023
1 parent f388403 commit 24ed4b9
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 11 deletions.
1 change: 1 addition & 0 deletions lib/instrument/blocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ function getOrCreateExternalVar(externalVars, block, varName, bindingOrExternalV
varNode: bindingOrExternalVar.varNode,
isReadFrom: false,
isAssignedTo: false,
isNameLocked: false,
argNames: bindingOrExternalVar.argNames,
trails: []
};
Expand Down
1 change: 1 addition & 0 deletions lib/instrument/visitors/function.js
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,7 @@ function createFunctionInfoFunction(fn, scopes, astJson, fnInfoVarNode, state) {
return {
isReadFrom: varProps.isReadFrom || undefined,
isAssignedTo: varProps.isAssignedTo || undefined,
isNameLocked: varProps.isNameLocked || undefined,
trails: varProps.trails
};
})
Expand Down
48 changes: 39 additions & 9 deletions lib/instrument/visitors/identifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ function ThisExpression(node, state) {
// Ignore if internal to function, unless in class constructor or prototype class property
// of class with super class.
if (block.id < fn.id) {
recordExternalVar(binding, block, 'this', fn, [...state.trail], true, false, state);
recordExternalVar(binding, block, 'this', fn, [...state.trail], true, false, false, state);
} else if (fn.hasSuperClass) {
createArrayOrPush(fn.internalVars, 'this', [...state.trail]);
}
Expand All @@ -105,7 +105,7 @@ function NewTargetExpression(node, state) {
const block = state.currentThisBlock;
if (block.id < fn.id) {
recordExternalVar(
block.bindings['new.target'], block, 'new.target', fn, [...state.trail], true, false, state
block.bindings['new.target'], block, 'new.target', fn, [...state.trail], true, false, false, state
);
}
}
Expand Down Expand Up @@ -148,7 +148,7 @@ function visitIdentifier(node, varName, isReadFrom, isAssignedTo, state) {
function resolveIdentifierInSecondPass(node, block, varName, fn, isReadFrom, isAssignedTo, state) {
state.secondPass(
resolveIdentifier,
node, block, varName, fn, [...state.trail], isReadFrom, isAssignedTo, state.isStrict, state
node, block, varName, fn, [...state.trail], isReadFrom, isAssignedTo, false, state.isStrict, state
);
}

Expand All @@ -161,14 +161,30 @@ function resolveIdentifierInSecondPass(node, block, varName, fn, isReadFrom, isA
* @param {Array<string|number>} trail - Trail
* @param {boolean} isReadFrom - `true` if variable is read from
* @param {boolean} isAssignedTo - `true` if variable is assigned to
* @param {boolean} isBehindWith - `true` if variable may be shadowed by a `with () {}` statement
* @param {boolean} isStrict - `true` if variable used in strict mode
* @param {Object} state - State object
* @returns {undefined}
*/
function resolveIdentifier(node, block, varName, fn, trail, isReadFrom, isAssignedTo, isStrict, state) {
function resolveIdentifier(
node, block, varName, fn, trail, isReadFrom, isAssignedTo, isBehindWith, isStrict, state
) {
// Find binding
let binding;
let binding,
bindingVarName = varName,
isWithBinding = false;
do {
// Check for a `with () {}` block which can intercept any variable
if (block.bindings.with) {
// Ignore `with () {}` blocks internal to function
if (block.id >= fn.id) continue;

binding = block.bindings.with;
bindingVarName = 'with';
isWithBinding = true;
break;
}

binding = block.bindings[varName];
} while (!binding && (block = block.parent)); // eslint-disable-line no-cond-assign

Expand All @@ -188,7 +204,8 @@ function resolveIdentifier(node, block, varName, fn, trail, isReadFrom, isAssign
}

// Record external var
if (isAssignedTo && binding.isConst) {
const bindingIsAssignedTo = isAssignedTo && !isWithBinding;
if (bindingIsAssignedTo && binding.isConst) {
// Record const violation
fn.amendments.push({
type: binding.isSilentConst && !isStrict
Expand All @@ -204,7 +221,16 @@ function resolveIdentifier(node, block, varName, fn, trail, isReadFrom, isAssign
isAssignedTo = false;
}

recordExternalVar(binding, block, varName, fn, trail, isReadFrom, isAssignedTo, state);
recordExternalVar(
binding, block, bindingVarName, fn, trail, isReadFrom, bindingIsAssignedTo, isBehindWith, state
);

// If is a `with () {}` block, continue to search for bindings further down the scope chain
if (isWithBinding) {
resolveIdentifier(
node, block.parent, varName, fn, trail, isReadFrom, isAssignedTo, true, isStrict, state
);
}
}

/**
Expand All @@ -216,14 +242,18 @@ function resolveIdentifier(node, block, varName, fn, trail, isReadFrom, isAssign
* @param {Array<string|number>} trail - Trail
* @param {boolean} isReadFrom - `true` if variable is read from
* @param {boolean} isAssignedTo - `true` if variable is assigned to
* @param {boolean} isBehindWith - `true` if variable may be shadowed by a `with () {}` statement
* @param {Object} state - State object
* @returns {undefined}
*/
function recordExternalVar(binding, block, varName, fn, trail, isReadFrom, isAssignedTo, state) {
function recordExternalVar(
binding, block, varName, fn, trail, isReadFrom, isAssignedTo, isBehindWith, state
) {
activateBlock(block, state);
activateBinding(binding, varName);
const externalVar = getOrCreateExternalVar(fn.externalVars, block, varName, binding);
if (isReadFrom) externalVar.isReadFrom = true;
if (isAssignedTo) externalVar.isAssignedTo = true;
externalVar.trails.push(trail);
if (isBehindWith) externalVar.isNameLocked = true;
if (varName !== 'with') externalVar.trails.push(trail);
}
12 changes: 10 additions & 2 deletions lib/serialize/parseFunction.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,11 @@ module.exports = function parseFunction(
blockName,
vars: mapValues(vars, (varProps, varName) => {
externalVars[varName] = [];
return {isReadFrom: !!varProps.isReadFrom, isAssignedTo: !!varProps.isAssignedTo};
return {
isReadFrom: !!varProps.isReadFrom,
isAssignedTo: !!varProps.isAssignedTo,
isNameLocked: !!varProps.isNameLocked
};
})
});
}
Expand Down Expand Up @@ -865,11 +869,15 @@ function resolveFunctionInfo(
const {blockId} = scope;
if (blockId < fnId) {
// External var
for (const [varName, {isReadFrom, isAssignedTo, trails}] of Object.entries(scope.vars)) {
for (
const [varName, {isReadFrom, isAssignedTo, isNameLocked, trails}]
of Object.entries(scope.vars)
) {
if (isNestedFunction) {
const scopeDefVar = scopeDefs.get(blockId).vars[varName];
if (isReadFrom) scopeDefVar.isReadFrom = true;
if (isAssignedTo) scopeDefVar.isAssignedTo = true;
if (isNameLocked) scopeDefVar.isNameLocked = true;
}

externalVars[varName].push(...trailsToNodes(fnNode, trails, varName));
Expand Down

0 comments on commit 24ed4b9

Please sign in to comment.