Skip to content

Commit

Permalink
with statements WIP 7
Browse files Browse the repository at this point in the history
  • Loading branch information
overlookmotel committed Aug 30, 2023
1 parent 1053102 commit 9998902
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 31 deletions.
17 changes: 17 additions & 0 deletions lib/instrument/blocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ module.exports = {
createArgumentsBinding,
createNewTargetBinding,
getOrCreateExternalVar,
createInternalVar,
activateBlock,
activateBinding,
createBlockTempVar
Expand Down Expand Up @@ -135,6 +136,7 @@ function createBindingWithoutNameCheck(block, varName, props) {
isSilentConst: !!props.isSilentConst,
isVar: !!props.isVar,
isFunction: !!props.isFunction,
isBehindWith: false,
argNames: props.argNames
};
}
Expand Down Expand Up @@ -208,6 +210,21 @@ function getOrCreateExternalVar(externalVars, block, varName, bindingOrExternalV
return externalVar;
}

/**
* Create an internal var in a function.
* @param {Object} fn - Function object
* @param {string} varName - Var name
* @param {Object} binding - Binding object
* @param {Array} [trail] - Trail for where binding appears (optional)
* @returns {undefined}
*/
function createInternalVar(fn, varName, binding, trail) {
const {internalVars} = fn;
let internalVar = internalVars[varName];
if (!internalVar) internalVar = internalVars[varName] = {binding, trails: []};
if (trail) internalVar.trails.push(trail);
}

/**
* Activate block.
* Called when block contains a binding which is referenced within a function.
Expand Down
28 changes: 6 additions & 22 deletions lib/instrument/visitors/assignee.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@ const assert = require('simple-invariant');
const Expression = require('./expression.js'),
{IdentifierAssignOnly, IdentifierReadAndAssign} = require('./identifier.js'),
MemberExpression = require('./memberExpression.js'),
{createBinding} = require('../blocks.js'),
{visitKey, visitKeyContainer, visitKeyContainerWithEmptyMembers} = require('../visit.js'),
{createArrayOrPush} = require('../../shared/functions.js');
{createBinding, createInternalVar} = require('../blocks.js'),
{visitKey, visitKeyContainer, visitKeyContainerWithEmptyMembers} = require('../visit.js');

// Exports

Expand Down Expand Up @@ -157,11 +156,11 @@ function visitConstOrLetIdentifier(node, isConst, state) {
const varName = node.name,
block = state.currentBlock;
assertNoCommonJsVarsClash(block, varName, state);
createBinding(block, varName, {isConst}, state);
const binding = createBinding(block, varName, {isConst}, state);

// Record as internal var
const fn = state.currentFunction;
if (fn) createArrayOrPush(fn.internalVars, varName, [...state.trail]);
if (fn) createInternalVar(fn, varName, binding, [...state.trail]);
}

/**
Expand All @@ -183,24 +182,9 @@ function IdentifierVar(node, state) {
return;
}

// Leave until later to add to `internalVars` in case binding is redeclared later
// as a function declaration. Vars defined as functions must not be
// added to `internalVars` to avoid their names being mangled.
// Record as internal var
const fn = state.currentFunction;
if (fn) state.secondPass(addVarIdentifierToInternalVars, node, binding, varName, fn, [...state.trail]);
}

/**
* Add identifier to parent function's internal vars.
* @param {Object} node - Identifier AST node (not needed but passed for debug reasons)
* @param {Object} binding - Binding object
* @param {string} varName - Variable name
* @param {Object} fn - Function object
* @param {Array<string|number>} trail - Trail
* @returns {undefined}
*/
function addVarIdentifierToInternalVars(node, binding, varName, fn, trail) {
if (!binding.isFunction) createArrayOrPush(fn.internalVars, varName, trail);
if (fn) createInternalVar(fn, varName, binding, [...state.trail]);
}

/**
Expand Down
10 changes: 9 additions & 1 deletion lib/instrument/visitors/function.js
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,14 @@ function insertTrackerComment(fnId, fnType, commentHolderNode, commentType, stat
* @returns {undefined}
*/
function createFunctionInfoFunction(fn, scopes, astJson, fnInfoVarNode, state) {
// Remove internal vars for functions,
// and remove trails for vars which are accessed from within `with ()`
const internalVars = Object.fromEntries(
Object.entries(fn.internalVars)
.filter(([, {binding}]) => !binding?.isFunction)
.map(([varName, {binding, trails}]) => [varName, binding?.isBehindWith ? [] : trails])
);

// Create JSON function info string
const {children} = fn;
let argNames;
Expand All @@ -738,7 +746,7 @@ function createFunctionInfoFunction(fn, scopes, astJson, fnInfoVarNode, state) {
containsEval: fn.containsEval || undefined,
containsImport: fn.containsImport || undefined,
argNames,
internalVars: fn.internalVars,
internalVars,
globalVarNames: fn.globalVarNames.size !== 0 ? [...fn.globalVarNames] : undefined,
amendments: fn.amendments.length !== 0
? fn.amendments.map(({type, blockId, trail}) => [type, blockId, ...trail]).reverse()
Expand Down
15 changes: 10 additions & 5 deletions lib/instrument/visitors/identifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ module.exports = {

// Imports
const visitEval = require('./eval.js'),
{getOrCreateExternalVar, activateBlock, activateBinding, createBlockTempVar} = require('../blocks.js'),
{
getOrCreateExternalVar, activateBlock, activateBinding, createBlockTempVar, createInternalVar
} = require('../blocks.js'),
{checkInternalVarNameClash} = require('../internalVars.js'),
{createArrayOrPush} = require('../../shared/functions.js'),
{
CONST_VIOLATION_CONST, CONST_VIOLATION_FUNCTION_THROWING, CONST_VIOLATION_FUNCTION_SILENT
} = require('../../shared/constants.js');
Expand Down Expand Up @@ -83,7 +84,7 @@ function ThisExpression(node, state) {
if (block.id < fn.id) {
recordExternalVar(binding, block, 'this', fn, [...state.trail], true, false, false, state);
} else if (fn.hasSuperClass) {
createArrayOrPush(fn.internalVars, 'this', [...state.trail]);
createInternalVar(fn, 'this', binding, [...state.trail]);
}
}

Expand Down Expand Up @@ -192,6 +193,9 @@ function resolveIdentifier(
return;
}

// Flag binding as behind `with` if it is
if (isBehindWith) binding.isBehindWith = true;

// Record if internal var
if (block.id >= fn.id) {
if (isWithBinding) {
Expand All @@ -202,8 +206,9 @@ function resolveIdentifier(
return;
}

// TODO: Freeze var name if behind `with () {}`
if (!binding.isFunction && !binding.argNames) createArrayOrPush(fn.internalVars, varName, trail);
if (!binding.isFunction && !binding.argNames) {
createInternalVar(fn, varName, binding, [...state.trail]);
}
return;
}

Expand Down
6 changes: 3 additions & 3 deletions lib/instrument/visitors/super.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ module.exports = {

// Imports
const {
createBindingWithoutNameCheck, getOrCreateExternalVar, activateBlock, createBlockTempVar
createBindingWithoutNameCheck, getOrCreateExternalVar, activateBlock, createBlockTempVar,
createInternalVar
} = require('../blocks.js'),
{getProp} = require('../../shared/functions.js'),
{SUPER_CALL, SUPER_EXPRESSION} = require('../../shared/constants.js');
Expand Down Expand Up @@ -146,8 +147,7 @@ function activateSuperBinding(superBlock, state) {
* @returns {undefined}
*/
function createInternalVarForThis(fn) {
const {internalVars} = fn;
if (!internalVars.this) internalVars.this = [];
createInternalVar(fn, 'this', null, null);
}

/**
Expand Down

0 comments on commit 9998902

Please sign in to comment.