133 lines
4.9 KiB
JavaScript
Executable file
133 lines
4.9 KiB
JavaScript
Executable file
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.validateUsage = validateUsage;
|
|
var _core = require("@babel/core");
|
|
function validateUsage(path, state, tdzEnabled) {
|
|
const dynamicTDZNames = [];
|
|
for (const name of Object.keys(path.getBindingIdentifiers())) {
|
|
const binding = path.scope.getBinding(name);
|
|
if (!binding) continue;
|
|
if (tdzEnabled) {
|
|
if (injectTDZChecks(binding, state)) dynamicTDZNames.push(name);
|
|
}
|
|
if (path.node.kind === "const") {
|
|
disallowConstantViolations(name, binding, state);
|
|
}
|
|
}
|
|
return dynamicTDZNames;
|
|
}
|
|
function disallowConstantViolations(name, binding, state) {
|
|
for (const violation of binding.constantViolations) {
|
|
const readOnlyError = state.addHelper("readOnlyError");
|
|
const throwNode = _core.types.callExpression(readOnlyError, [_core.types.stringLiteral(name)]);
|
|
if (violation.isAssignmentExpression()) {
|
|
const {
|
|
operator,
|
|
left,
|
|
right
|
|
} = violation.node;
|
|
if (operator === "=") {
|
|
const exprs = [right];
|
|
exprs.push(throwNode);
|
|
violation.replaceWith(_core.types.sequenceExpression(exprs));
|
|
} else if (["&&=", "||=", "??="].includes(operator)) {
|
|
violation.replaceWith(_core.types.logicalExpression(operator.slice(0, -1), left, _core.types.sequenceExpression([right, throwNode])));
|
|
} else {
|
|
violation.replaceWith(_core.types.sequenceExpression([_core.types.binaryExpression(operator.slice(0, -1), left, right), throwNode]));
|
|
}
|
|
} else if (violation.isUpdateExpression()) {
|
|
violation.replaceWith(_core.types.sequenceExpression([_core.types.unaryExpression("+", violation.get("argument").node), throwNode]));
|
|
} else if (violation.isForXStatement()) {
|
|
violation.ensureBlock();
|
|
violation.get("left").replaceWith(_core.types.variableDeclaration("var", [_core.types.variableDeclarator(violation.scope.generateUidIdentifier(name))]));
|
|
violation.node.body.body.unshift(_core.types.expressionStatement(throwNode));
|
|
}
|
|
}
|
|
}
|
|
function getTDZStatus(refPath, bindingPath) {
|
|
const executionStatus = bindingPath._guessExecutionStatusRelativeTo(refPath);
|
|
if (executionStatus === "before") {
|
|
return "outside";
|
|
} else if (executionStatus === "after") {
|
|
return "inside";
|
|
} else {
|
|
return "maybe";
|
|
}
|
|
}
|
|
const skipTDZChecks = new WeakSet();
|
|
function buildTDZAssert(status, node, state) {
|
|
if (status === "maybe") {
|
|
const clone = _core.types.cloneNode(node);
|
|
skipTDZChecks.add(clone);
|
|
return _core.types.callExpression(state.addHelper("temporalRef"), [clone, _core.types.stringLiteral(node.name)]);
|
|
} else {
|
|
return _core.types.callExpression(state.addHelper("tdz"), [_core.types.stringLiteral(node.name)]);
|
|
}
|
|
}
|
|
function getTDZReplacement(path, state, id = path.node) {
|
|
var _path$scope$getBindin;
|
|
if (skipTDZChecks.has(id)) return;
|
|
skipTDZChecks.add(id);
|
|
const bindingPath = (_path$scope$getBindin = path.scope.getBinding(id.name)) == null ? void 0 : _path$scope$getBindin.path;
|
|
if (!bindingPath || bindingPath.isFunctionDeclaration()) return;
|
|
const status = getTDZStatus(path, bindingPath);
|
|
if (status === "outside") return;
|
|
if (status === "maybe") {
|
|
bindingPath.parent._tdzThis = true;
|
|
}
|
|
return {
|
|
status,
|
|
node: buildTDZAssert(status, id, state)
|
|
};
|
|
}
|
|
function injectTDZChecks(binding, state) {
|
|
const allUsages = new Set(binding.referencePaths);
|
|
binding.constantViolations.forEach(allUsages.add, allUsages);
|
|
let dynamicTdz = false;
|
|
for (const path of binding.constantViolations) {
|
|
const {
|
|
node
|
|
} = path;
|
|
if (skipTDZChecks.has(node)) continue;
|
|
skipTDZChecks.add(node);
|
|
if (path.isUpdateExpression()) {
|
|
const arg = path.get("argument");
|
|
const replacement = getTDZReplacement(path, state, arg.node);
|
|
if (!replacement) continue;
|
|
if (replacement.status === "maybe") {
|
|
dynamicTdz = true;
|
|
path.insertBefore(replacement.node);
|
|
} else {
|
|
path.replaceWith(replacement.node);
|
|
}
|
|
} else if (path.isAssignmentExpression()) {
|
|
const nodes = [];
|
|
const ids = path.getBindingIdentifiers();
|
|
for (const name of Object.keys(ids)) {
|
|
const replacement = getTDZReplacement(path, state, ids[name]);
|
|
if (replacement) {
|
|
nodes.push(_core.types.expressionStatement(replacement.node));
|
|
if (replacement.status === "inside") break;
|
|
if (replacement.status === "maybe") dynamicTdz = true;
|
|
}
|
|
}
|
|
if (nodes.length > 0) path.insertBefore(nodes);
|
|
}
|
|
}
|
|
for (const path of binding.referencePaths) {
|
|
if (path.parentPath.isUpdateExpression()) continue;
|
|
if (path.parentPath.isFor({
|
|
left: path.node
|
|
})) continue;
|
|
const replacement = getTDZReplacement(path, state);
|
|
if (!replacement) continue;
|
|
if (replacement.status === "maybe") dynamicTdz = true;
|
|
path.replaceWith(replacement.node);
|
|
}
|
|
return dynamicTdz;
|
|
}
|
|
|
|
//# sourceMappingURL=validation.js.map
|