Files
reverse-skills-max/vendor-new/xbsReverseSkill/ast-deobfuscation/scripts/patterns/pattern-utils.js

1070 lines
29 KiB
JavaScript

const vm = require("vm");
const generator = require("@babel/generator").default;
const { clone, t, traverse } = require("../shared");
function generateCode(node) {
return generator(node, {
compact: true,
comments: false,
jsescOption: { minimal: true }
}).code;
}
function getPropertyKey(node) {
if (t.isIdentifier(node)) {
return node.name;
}
if (t.isStringLiteral(node) || t.isNumericLiteral(node)) {
return String(node.value);
}
return null;
}
function resolvePropertyKeyNode(node, options = {}) {
const directKey = getPropertyKey(node);
if (directKey !== null) {
return directKey;
}
if (!options.sandbox) {
return null;
}
const result = evaluateExpression(generateCode(node), options.sandbox, options.timeoutMs || 500);
if (!result.ok) {
return null;
}
if (typeof result.value === "string" || typeof result.value === "number") {
return String(result.value);
}
return null;
}
function isSafeMemberReplacement(path) {
const parent = path.parentPath;
if (!parent) {
return false;
}
if ((parent.isAssignmentExpression() || parent.isAssignmentPattern()) && path.key === "left") {
return false;
}
if (parent.isUpdateExpression() && path.key === "argument") {
return false;
}
if ((parent.isForInStatement() || parent.isForOfStatement()) && path.key === "left") {
return false;
}
if (parent.isUnaryExpression({ operator: "delete" }) && path.key === "argument") {
return false;
}
return true;
}
function createSandbox(extra = {}) {
const sandbox = {
Array,
Boolean,
Buffer,
Date,
Error,
Function,
JSON,
Map,
Math,
Number,
Object,
Proxy,
Reflect,
RegExp,
Set,
String,
Symbol,
TypeError,
WeakMap,
WeakSet,
atob,
btoa,
clearInterval,
clearTimeout,
console,
decodeURIComponent,
encodeURIComponent,
escape,
isFinite,
isNaN,
parseFloat,
parseInt,
setInterval,
setTimeout,
unescape,
Uint8Array,
...extra
};
sandbox.global = sandbox;
sandbox.globalThis = sandbox;
sandbox.self = sandbox;
sandbox.window = sandbox;
return sandbox;
}
function isPrimitiveValue(value) {
return value === null || ["string", "number", "boolean", "undefined"].includes(typeof value);
}
function isSerializableValue(value, depth = 0) {
if (depth > 4) {
return false;
}
if (isPrimitiveValue(value)) {
return true;
}
if (Array.isArray(value)) {
return value.every((item) => isSerializableValue(item, depth + 1));
}
if (value && typeof value === "object") {
const proto = Object.getPrototypeOf(value);
if (proto !== Object.prototype && proto !== null) {
return false;
}
return Object.keys(value).every((key) => isSerializableValue(value[key], depth + 1));
}
return false;
}
function literalNodeFromValue(value) {
if (!isSerializableValue(value)) {
return null;
}
if (typeof value === "undefined") {
return t.identifier("undefined");
}
return t.valueToNode(value);
}
function getBindingAliasInitPath(binding) {
if (!binding || !binding.path) {
return null;
}
if (binding.path.isVariableDeclarator()) {
const initPath = binding.path.get("init");
if (initPath && initPath.node) {
return initPath;
}
}
if (binding.path.parentPath && binding.path.parentPath.isVariableDeclarator()) {
const initPath = binding.path.parentPath.get("init");
if (initPath && initPath.node) {
return initPath;
}
}
if (binding.constantViolations.length === 1) {
const violationPath = binding.constantViolations[0];
if (
violationPath &&
violationPath.isAssignmentExpression() &&
violationPath.get("left").isIdentifier() &&
violationPath.get("right").isIdentifier()
) {
return violationPath.get("right");
}
}
return null;
}
function evaluateNodes(nodes, sandbox, timeoutMs = 1000) {
try {
const code = nodes.map(generateCode).join(";\n");
vm.runInNewContext(code, sandbox, { timeout: timeoutMs });
return true;
} catch (error) {
return false;
}
}
function evaluateExpression(code, sandbox, timeoutMs = 1000) {
try {
return {
ok: true,
value: vm.runInNewContext(code, sandbox, { timeout: timeoutMs })
};
} catch (error) {
return {
ok: false,
error
};
}
}
function shouldEvaluateTopLevelStatement(node) {
if (t.isFunctionDeclaration(node)) {
return true;
}
if (t.isVariableDeclaration(node)) {
return true;
}
if (!t.isExpressionStatement(node)) {
return false;
}
const expr = node.expression;
if (t.isAssignmentExpression(expr)) {
return true;
}
if (t.isCallExpression(expr)) {
if (t.isFunctionExpression(expr.callee) || t.isArrowFunctionExpression(expr.callee)) {
return expr.callee.body && (!t.isBlockStatement(expr.callee.body) || expr.callee.body.body.length <= 40);
}
return t.isIdentifier(expr.callee);
}
if (t.isUnaryExpression(expr) && t.isCallExpression(expr.argument)) {
const call = expr.argument;
if (t.isFunctionExpression(call.callee) || t.isArrowFunctionExpression(call.callee)) {
return call.callee.body && (!t.isBlockStatement(call.callee.body) || call.callee.body.body.length <= 40);
}
}
return false;
}
function evaluatePreludeStatements(ast, options = {}) {
const maxStatements = options.maxStatements || 10;
const timeoutMs = options.timeoutMs || 1000;
const sandbox = options.sandbox || createSandbox();
const executed = [];
for (const node of ast.program.body) {
if (executed.length >= maxStatements) {
break;
}
if (!shouldEvaluateTopLevelStatement(node)) {
break;
}
if (!evaluateNodes([node], sandbox, timeoutMs)) {
break;
}
executed.push(node);
}
return {
sandbox,
executedCount: executed.length
};
}
function seedSandboxFromAliases(ast, sandbox) {
let changed = false;
traverse(ast, {
VariableDeclarator(path) {
if (!path.get("id").isIdentifier() || !path.get("init").isIdentifier()) {
return;
}
const aliasName = path.node.id.name;
const sourceName = path.node.init.name;
if (!Object.prototype.hasOwnProperty.call(sandbox, sourceName) || Object.prototype.hasOwnProperty.call(sandbox, aliasName)) {
return;
}
sandbox[aliasName] = sandbox[sourceName];
changed = true;
},
AssignmentExpression(path) {
if (!path.get("left").isIdentifier() || !path.get("right").isIdentifier()) {
return;
}
const aliasName = path.node.left.name;
const sourceName = path.node.right.name;
if (!Object.prototype.hasOwnProperty.call(sandbox, sourceName) || Object.prototype.hasOwnProperty.call(sandbox, aliasName)) {
return;
}
sandbox[aliasName] = sandbox[sourceName];
changed = true;
}
});
return changed;
}
function replacePrimitiveIdentifiers(ast, sandbox, options = {}) {
const includeGlobals = options.includeGlobals === true;
const globalAllowlist = new Set(options.globalAllowlist || []);
const bindingLiterals = new Map();
let changed = false;
traverse(ast, {
VariableDeclarator(path) {
if (!path.get("id").isIdentifier()) {
return;
}
const name = path.node.id.name;
const binding = path.scope.getBinding(name);
if (!Object.prototype.hasOwnProperty.call(sandbox, name)) {
return;
}
const node = literalNodeFromValue(sandbox[name]);
if (binding && binding.constant && node) {
bindingLiterals.set(binding, node);
}
},
Identifier(path) {
if (!includeGlobals) {
return;
}
if (!path.isReferencedIdentifier()) {
return;
}
if (globalAllowlist.size > 0 && !globalAllowlist.has(path.node.name)) {
return;
}
const binding = path.scope.getBinding(path.node.name);
if (binding) {
return;
}
if (!Object.prototype.hasOwnProperty.call(sandbox, path.node.name)) {
return;
}
const node = literalNodeFromValue(sandbox[path.node.name]);
if (!node || t.isIdentifier(node, { name: path.node.name })) {
return;
}
path.replaceWith(clone(node));
changed = true;
}
});
if (bindingLiterals.size > 0) {
traverse(ast, {
Identifier(path) {
if (!path.isReferencedIdentifier()) {
return;
}
const binding = path.scope.getBinding(path.node.name);
const node = binding ? bindingLiterals.get(binding) : null;
if (!node || t.isIdentifier(node, { name: path.node.name })) {
return;
}
path.replaceWith(clone(node));
changed = true;
}
});
for (const [binding, node] of bindingLiterals.entries()) {
if (binding.path && !binding.path.removed) {
if (binding.path.isVariableDeclarator()) {
const initPath = binding.path.get("init");
if (initPath && initPath.node) {
initPath.replaceWith(clone(node));
}
}
binding.path.remove();
changed = true;
}
}
}
return changed;
}
function resolveSandboxCallableName(path, sandbox, seenBindings = new Set()) {
if (!path || !path.node || !path.isIdentifier()) {
return null;
}
const directName = path.node.name;
const binding = path.scope.getBinding(directName);
const programScope = path.scope.getProgramParent();
if (!binding) {
return typeof sandbox[directName] === "function" ? directName : null;
}
if (binding.scope === programScope && typeof sandbox[directName] === "function") {
return directName;
}
if (seenBindings.has(binding)) {
return null;
}
seenBindings.add(binding);
const initPath = getBindingAliasInitPath(binding);
if (!initPath || !initPath.node || !initPath.isIdentifier()) {
return null;
}
return resolveSandboxCallableName(initPath, sandbox, seenBindings);
}
function replaceCallsFromSandbox(ast, sandbox, options = {}) {
const allowPredicate = options.allowPredicate || (() => true);
let changed = false;
traverse(ast, {
CallExpression(path) {
const calleeName = resolveSandboxCallableName(path.get("callee"), sandbox);
if (!calleeName || !allowPredicate(path, calleeName)) {
return;
}
const argValues = [];
for (const argPath of path.get("arguments")) {
const evaluated = evaluateExpression(generateCode(argPath.node), sandbox, options.timeoutMs || 1000);
if (!evaluated.ok) {
return;
}
argValues.push(evaluated.value);
}
let value;
try {
value = sandbox[calleeName](...argValues);
} catch (error) {
const result = evaluateExpression(generateCode(path.node), sandbox, options.timeoutMs || 1000);
if (!result.ok) {
return;
}
value = result.value;
}
const node = literalNodeFromValue(value);
if (!node) {
return;
}
path.replaceWith(node);
changed = true;
}
});
return changed;
}
function resolveSandboxValueName(path, sandbox, seenBindings = new Set()) {
if (!path || !path.node || !path.isIdentifier()) {
return null;
}
const directName = path.node.name;
const binding = path.scope.getBinding(directName);
const programScope = path.scope.getProgramParent();
if (!binding) {
return Object.prototype.hasOwnProperty.call(sandbox, directName) ? directName : null;
}
if (binding.scope === programScope && Object.prototype.hasOwnProperty.call(sandbox, directName)) {
return directName;
}
if (seenBindings.has(binding)) {
return null;
}
seenBindings.add(binding);
const initPath = getBindingAliasInitPath(binding);
if (!initPath || !initPath.node || !initPath.isIdentifier()) {
return null;
}
return resolveSandboxValueName(initPath, sandbox, seenBindings);
}
function replaceMembersFromSandbox(ast, sandbox) {
let changed = false;
traverse(ast, {
MemberExpression(path) {
if (!isSafeMemberReplacement(path)) {
return;
}
const key = path.node.computed ? resolvePropertyKeyNode(path.node.property, { sandbox }) : resolvePropertyKeyNode(path.node.property, { sandbox });
if (key === null) {
return;
}
const objectName = resolveSandboxValueName(path.get("object"), sandbox);
if (!objectName) {
return;
}
const container = sandbox[objectName];
if (!container || (typeof container !== "object" && typeof container !== "function")) {
return;
}
if (!Object.prototype.hasOwnProperty.call(container, key)) {
return;
}
const node = literalNodeFromValue(container[key]);
if (!node) {
return;
}
path.replaceWith(node);
changed = true;
}
});
return changed;
}
function inlineStringMethodCalls(ast) {
let changed = false;
function resolveString(path, seen = new Set()) {
if (!path || !path.node) {
return null;
}
if (path.isStringLiteral()) {
return path.node.value;
}
if (path.isTemplateLiteral() && path.node.expressions.length === 0) {
return path.node.quasis[0].value.cooked;
}
if (path.isIdentifier()) {
const binding = path.scope.getBinding(path.node.name);
if (!binding || seen.has(binding) || !binding.path.parentPath || !binding.path.parentPath.isVariableDeclarator()) {
return null;
}
seen.add(binding);
return resolveString(binding.path.parentPath.get("init"), seen);
}
return null;
}
traverse(ast, {
CallExpression(path) {
if (!path.get("callee").isMemberExpression()) {
return;
}
const callee = path.get("callee");
const property = callee.node.computed ? getPropertyKey(callee.node.property) : getPropertyKey(callee.node.property);
if (!["substring", "substr", "slice", "split"].includes(property)) {
return;
}
const source = resolveString(callee.get("object"));
if (typeof source !== "string") {
return;
}
const argValues = [];
for (const argPath of path.get("arguments")) {
const evaluated = argPath.evaluate();
if (!evaluated.confident || !isPrimitiveValue(evaluated.value)) {
return;
}
argValues.push(evaluated.value);
}
let value;
try {
value = source[property](...argValues);
} catch (error) {
return;
}
const node = literalNodeFromValue(value);
if (!node) {
return;
}
path.replaceWith(node);
changed = true;
}
});
return changed;
}
function descriptorForValue(node, options = {}) {
if (t.isStringLiteral(node) || t.isNumericLiteral(node) || t.isBooleanLiteral(node) || t.isNullLiteral(node)) {
return { type: "literal", node: clone(node) };
}
if (t.isIdentifier(node, { name: "undefined" })) {
return { type: "literal", node: t.identifier("undefined") };
}
if (collectReferencedIdentifierNames(node).size === 0) {
const sandbox = options.sandbox || createSandbox();
const evaluated = evaluateExpression(generateCode(node), sandbox, options.timeoutMs || 500);
if (evaluated.ok) {
const literalNode = literalNodeFromValue(evaluated.value);
if (literalNode) {
return { type: "literal", node: literalNode };
}
}
}
return null;
}
function isHarmlessWrapperPreludeInit(node) {
return (
node == null ||
t.isIdentifier(node) ||
t.isMemberExpression(node) ||
t.isStringLiteral(node) ||
t.isNumericLiteral(node) ||
t.isBooleanLiteral(node) ||
t.isNullLiteral(node)
);
}
function collectReferencedIdentifierNames(node) {
const names = new Set();
if (!node) {
return names;
}
traverse(t.file(t.program([t.expressionStatement(clone(node))])), {
Identifier(path) {
if (path.isReferencedIdentifier()) {
names.add(path.node.name);
}
}
});
return names;
}
function getWrapperReturnArgument(fnNode) {
if (!t.isFunctionExpression(fnNode) || fnNode.body.body.length === 0) {
return null;
}
const body = fnNode.body.body;
const last = body[body.length - 1];
if (!t.isReturnStatement(last) || !last.argument) {
return null;
}
const referenced = collectReferencedIdentifierNames(last.argument);
for (const stmt of body.slice(0, -1)) {
if (!t.isVariableDeclaration(stmt) || stmt.kind !== "var") {
return null;
}
for (const declaration of stmt.declarations) {
if (!t.isIdentifier(declaration.id) || referenced.has(declaration.id.name) || !isHarmlessWrapperPreludeInit(declaration.init)) {
return null;
}
}
}
return last.argument;
}
function collectWrapperDescriptor(node, wrapperMap, options = {}) {
const literal = descriptorForValue(node, options);
if (literal) {
return literal;
}
if (t.isMemberExpression(node) && t.isIdentifier(node.object)) {
const key = node.computed ? resolvePropertyKeyNode(node.property, options) : resolvePropertyKeyNode(node.property, options);
if (key !== null) {
return { type: "member", objectName: node.object.name, key };
}
}
if (t.isFunctionExpression(node)) {
const argument = getWrapperReturnArgument(node);
if (!argument) {
return null;
}
if (t.isBinaryExpression(argument) && t.isIdentifier(argument.left) && t.isIdentifier(argument.right)) {
return { type: "binary", operator: argument.operator };
}
if (t.isLogicalExpression(argument) && t.isIdentifier(argument.left) && t.isIdentifier(argument.right)) {
return { type: "logical", operator: argument.operator };
}
if (t.isUnaryExpression(argument) && t.isIdentifier(argument.argument)) {
return { type: "unary", operator: argument.operator, prefix: argument.prefix !== false };
}
if (t.isCallExpression(argument) && t.isIdentifier(argument.callee)) {
return { type: "call" };
}
if (t.isCallExpression(argument) && t.isMemberExpression(argument.callee) && t.isIdentifier(argument.callee.object)) {
const key = argument.callee.computed ? resolvePropertyKeyNode(argument.callee.property, options) : resolvePropertyKeyNode(argument.callee.property, options);
if (key !== null) {
return resolveDescriptor(wrapperMap, { type: "member", objectName: argument.callee.object.name, key });
}
}
}
return null;
}
function resolveDescriptor(wrapperMap, descriptor, seen = new Set()) {
if (!descriptor) {
return null;
}
if (descriptor.type !== "member") {
return descriptor;
}
const token = `${descriptor.objectName}:${descriptor.key}`;
if (seen.has(token)) {
return null;
}
seen.add(token);
const next = wrapperMap[descriptor.objectName] && wrapperMap[descriptor.objectName][descriptor.key];
if (!next) {
return null;
}
return resolveDescriptor(wrapperMap, next, seen) || next;
}
function collectWrapperObjects(ast, options = {}) {
const wrapperMap = Object.create(null);
const removableEntries = [];
traverse(ast, {
"VariableDeclarator|AssignmentExpression"(path) {
const idPath = path.isVariableDeclarator() ? path.get("id") : path.get("left");
const valuePath = path.isVariableDeclarator() ? path.get("init") : path.get("right");
if (!idPath.isIdentifier() || !valuePath.isObjectExpression()) {
return;
}
const name = idPath.node.name;
const collected = wrapperMap[name] || Object.create(null);
let localChanged = false;
let supportedPropertyCount = 0;
let collectedPropertyCount = 0;
const propertyEntries = [];
valuePath.get("properties").forEach((propertyPath) => {
if (!propertyPath.isObjectProperty()) {
return;
}
supportedPropertyCount += 1;
const key = resolvePropertyKeyNode(propertyPath.node.key, options);
if (key === null) {
return;
}
const descriptor = collectWrapperDescriptor(propertyPath.node.value, wrapperMap, options);
if (descriptor) {
collected[key] = descriptor;
localChanged = true;
collectedPropertyCount += 1;
propertyEntries.push({ key, path: propertyPath });
}
});
if (localChanged) {
wrapperMap[name] = collected;
removableEntries.push({
path,
objectName: name,
propertyEntries,
removable: supportedPropertyCount > 0 && supportedPropertyCount === collectedPropertyCount
});
}
}
});
return {
wrapperMap,
removableEntries
};
}
function applyWrapperObjects(ast, wrapperMap, options = {}) {
let changed = false;
traverse(ast, {
CallExpression(path) {
if (!path.get("callee").isMemberExpression() || !path.get("callee.object").isIdentifier()) {
return;
}
const objectName = path.node.callee.object.name;
const key = path.node.callee.computed ? resolvePropertyKeyNode(path.node.callee.property, options) : resolvePropertyKeyNode(path.node.callee.property, options);
const resolved = resolveDescriptor(wrapperMap, { type: "member", objectName, key });
if (!resolved) {
return;
}
const args = path.node.arguments.map((arg) => clone(arg));
if (resolved.type === "binary" && args.length >= 2) {
path.replaceWith(t.binaryExpression(resolved.operator, args[0], args[1]));
changed = true;
} else if (resolved.type === "logical" && args.length >= 2) {
path.replaceWith(t.logicalExpression(resolved.operator, args[0], args[1]));
changed = true;
} else if (resolved.type === "unary" && args.length >= 1) {
path.replaceWith(t.unaryExpression(resolved.operator, args[0], resolved.prefix !== false));
changed = true;
} else if (resolved.type === "call" && args.length >= 1) {
path.replaceWith(t.callExpression(args[0], args.slice(1)));
changed = true;
}
},
MemberExpression(path) {
if (!path.get("object").isIdentifier() || !isSafeMemberReplacement(path)) {
return;
}
const objectName = path.node.object.name;
const key = path.node.computed ? resolvePropertyKeyNode(path.node.property, options) : resolvePropertyKeyNode(path.node.property, options);
const resolved = resolveDescriptor(wrapperMap, { type: "member", objectName, key });
if (!resolved || resolved.type !== "literal") {
return;
}
path.replaceWith(clone(resolved.node));
changed = true;
}
});
return changed;
}
function removeWrapperOwnerPath(path) {
if (!path || !path.node || path.removed) {
return false;
}
if (path.isVariableDeclarator()) {
const declarationPath = path.parentPath;
path.remove();
if (declarationPath && declarationPath.node && declarationPath.get("declarations").length === 0) {
declarationPath.remove();
}
return true;
}
if (path.isAssignmentExpression()) {
if (path.parentPath && path.parentPath.isExpressionStatement()) {
path.parentPath.remove();
} else {
path.remove();
}
return true;
}
path.remove();
return true;
}
function collectLiveWrapperPropertyUsage(ast, wrapperNames, options = {}) {
const liveKeys = new Set();
const unsafeObjects = new Set();
const ignoreDynamicUsage = options.ignoreDynamicUsage === true;
traverse(ast, {
Identifier(path) {
if (!path.isReferencedIdentifier() || !wrapperNames.has(path.node.name)) {
return;
}
const parent = path.parentPath;
if (
parent &&
parent.isMemberExpression() &&
path.key === "object" &&
parent.get("object") === path
) {
return;
}
unsafeObjects.add(path.node.name);
},
MemberExpression(path) {
if (!path.get("object").isIdentifier()) {
return;
}
const objectName = path.get("object").node.name;
if (!wrapperNames.has(objectName)) {
return;
}
const key = path.node.computed ? resolvePropertyKeyNode(path.node.property, options) : resolvePropertyKeyNode(path.node.property, options);
if (key === null || !isSafeMemberReplacement(path)) {
if (!ignoreDynamicUsage) {
unsafeObjects.add(objectName);
}
return;
}
liveKeys.add(`${objectName}:${key}`);
}
});
return {
liveKeys,
unsafeObjects
};
}
function pruneResolvedWrapperProperties(ast, removableEntries, options = {}) {
if (removableEntries.length === 0) {
return false;
}
const wrapperNames = new Set(removableEntries.map((entry) => entry.objectName));
const { liveKeys, unsafeObjects } = collectLiveWrapperPropertyUsage(ast, wrapperNames, options);
let changed = false;
removableEntries.forEach(({ path, objectName, propertyEntries, removable }) => {
if (!path || !path.node || path.removed || unsafeObjects.has(objectName)) {
return;
}
propertyEntries.forEach(({ key, path: propertyPath }) => {
if (!propertyPath || !propertyPath.node || propertyPath.removed) {
return;
}
if (liveKeys.has(`${objectName}:${key}`)) {
return;
}
propertyPath.remove();
changed = true;
});
const valuePath = path.isVariableDeclarator() ? path.get("init") : path.get("right");
if (!valuePath || !valuePath.node || !valuePath.isObjectExpression()) {
return;
}
const remainingProperties = valuePath.get("properties");
if (remainingProperties.length > 0) {
return;
}
if (removable || propertyEntries.length > 0) {
if (removeWrapperOwnerPath(path)) {
changed = true;
}
}
});
return changed;
}
function getWrapperOwnerBinding(entry) {
if (!entry || !entry.path || !entry.path.node) {
return null;
}
if (entry.path.isVariableDeclarator()) {
const idPath = entry.path.get("id");
return idPath.isIdentifier() ? entry.path.scope.getBinding(idPath.node.name) : null;
}
if (entry.path.isAssignmentExpression()) {
const leftPath = entry.path.get("left");
return leftPath.isIdentifier() ? entry.path.scope.getBinding(leftPath.node.name) : null;
}
return null;
}
function pruneUnusedWrapperOwners(removableEntries) {
let changed = false;
removableEntries.forEach((entry) => {
if (!entry.removable || !entry.path || !entry.path.node || entry.path.removed) {
return;
}
const binding = getWrapperOwnerBinding(entry);
if (!binding || binding.referencePaths.length !== 0) {
return;
}
if (removeWrapperOwnerPath(entry.path)) {
changed = true;
}
});
return changed;
}
function foldWrapperObjects(ast, options = {}) {
const { wrapperMap, removableEntries } = collectWrapperObjects(ast, options);
const applied = applyWrapperObjects(ast, wrapperMap, options);
let changed = applied;
if (pruneResolvedWrapperProperties(ast, removableEntries, options)) {
changed = true;
}
if (applied) {
removableEntries.forEach(({ path, removable }) => {
if (removable && removeWrapperOwnerPath(path)) {
changed = true;
}
});
}
if (pruneUnusedWrapperOwners(removableEntries)) {
changed = true;
}
return { ast, changed };
}
function getTopLevelIifeInfo(programPath) {
const first = programPath.get("body.0");
if (!first || !first.isExpressionStatement()) {
return null;
}
let exprPath = first.get("expression");
if (exprPath.isUnaryExpression()) {
exprPath = exprPath.get("argument");
}
if (!exprPath.isCallExpression()) {
return null;
}
let calleePath = exprPath.get("callee");
if (calleePath.isUnaryExpression()) {
calleePath = calleePath.get("argument");
}
if (!calleePath.isFunctionExpression()) {
return null;
}
return {
statementPath: first,
callPath: exprPath,
calleePath,
argPaths: exprPath.get("arguments")
};
}
module.exports = {
createSandbox,
evaluateExpression,
evaluatePreludeStatements,
evaluateNodes,
foldWrapperObjects,
generateCode,
getPropertyKey,
getTopLevelIifeInfo,
inlineStringMethodCalls,
isSafeMemberReplacement,
literalNodeFromValue,
replaceCallsFromSandbox,
replaceMembersFromSandbox,
replacePrimitiveIdentifiers,
seedSandboxFromAliases,
resolveDescriptor,
traverse,
t
};