"use strict";
// ---------------------------------------------------------------------------
// Pure Function Inliner
//
// Analyzes function declarations for inlining eligibility and inlines
// simple pure functions during constant propagation.
// ---------------------------------------------------------------------------
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isEligibleForInlining = isEligibleForInlining;
exports.inlineFunction = inlineFunction;
const typescript_1 = __importDefault(require("typescript"));
const lattice_js_1 = require("./lattice.js");
const expressionEvaluator_js_1 = require("./expressionEvaluator.js");
// ---------------------------------------------------------------------------
// Eligibility check
// ---------------------------------------------------------------------------
/**
 * Check if a function declaration is eligible for constant-propagation inlining.
 *
 * Eligible: single return statement (possibly preceded by const declarations),
 * no side effects, no await, no loops, no try/catch, not a generator.
 */
function isEligibleForInlining(decl) {
    // Must not be async or generator
    if (decl.modifiers?.some(m => m.kind === typescript_1.default.SyntaxKind.AsyncKeyword))
        return false;
    if (decl.asteriskToken)
        return false;
    const body = decl.body;
    if (!body)
        return false;
    // Arrow with expression body: always eligible (it's a single return)
    if (!typescript_1.default.isBlock(body))
        return true;
    // Block body: must contain only const declarations and exactly one return
    let hasReturn = false;
    for (const stmt of body.statements) {
        if (typescript_1.default.isReturnStatement(stmt)) {
            if (hasReturn)
                return false; // multiple returns
            if (!stmt.expression)
                return false; // void return
            hasReturn = true;
        }
        else if (typescript_1.default.isVariableStatement(stmt)) {
            // Only const declarations allowed
            if (!(stmt.declarationList.flags & typescript_1.default.NodeFlags.Const))
                return false;
            // Check no destructuring with initializers that have side effects
            for (const decl of stmt.declarationList.declarations) {
                if (!decl.initializer)
                    return false;
                if (containsSideEffects(decl.initializer))
                    return false;
            }
        }
        else {
            // Any other statement type (if, for, while, try, etc.) → not eligible
            return false;
        }
    }
    return hasReturn;
}
/**
 * Quick check for obvious side effects in an expression.
 * Not exhaustive — just catches the obvious cases.
 */
function containsSideEffects(expr) {
    let hasSideEffects = false;
    function visit(node) {
        if (hasSideEffects)
            return;
        // await
        if (typescript_1.default.isAwaitExpression(node)) {
            hasSideEffects = true;
            return;
        }
        // yield
        if (typescript_1.default.isYieldExpression(node)) {
            hasSideEffects = true;
            return;
        }
        // assignment
        if (typescript_1.default.isBinaryExpression(node) && node.operatorToken.kind === typescript_1.default.SyntaxKind.EqualsToken) {
            hasSideEffects = true;
            return;
        }
        // delete
        if (typescript_1.default.isDeleteExpression(node)) {
            hasSideEffects = true;
            return;
        }
        // Prefix/postfix ++ or --
        if (typescript_1.default.isPrefixUnaryExpression(node) && (node.operator === typescript_1.default.SyntaxKind.PlusPlusToken ||
            node.operator === typescript_1.default.SyntaxKind.MinusMinusToken)) {
            hasSideEffects = true;
            return;
        }
        if (typescript_1.default.isPostfixUnaryExpression(node) && (node.operator === typescript_1.default.SyntaxKind.PlusPlusToken ||
            node.operator === typescript_1.default.SyntaxKind.MinusMinusToken)) {
            hasSideEffects = true;
            return;
        }
        typescript_1.default.forEachChild(node, visit);
    }
    visit(expr);
    return hasSideEffects;
}
// ---------------------------------------------------------------------------
// Inlining
// ---------------------------------------------------------------------------
/**
 * Inline a function call by binding parameters to argument values
 * and evaluating the body.
 *
 * @param decl The function declaration to inline
 * @param argValues Evaluated constant values for each argument
 * @param checker Type checker for symbol resolution
 * @param parentResolver The parent module's symbol resolver
 */
function inlineFunction(decl, argValues, checker, parentResolver) {
    const body = decl.body;
    if (!body)
        return (0, lattice_js_1.bottom)('Function has no body');
    // Build a local scope binding params → arg values
    const localBindings = new Map();
    const params = decl.parameters;
    for (let i = 0; i < params.length; i++) {
        const param = params[i];
        if (typescript_1.default.isIdentifier(param.name)) {
            const sym = checker.getSymbolAtLocation(param.name);
            if (sym) {
                localBindings.set(sym, i < argValues.length ? argValues[i] : (0, lattice_js_1.constant)(undefined));
            }
        }
        // Destructuring parameters not supported for inlining
        if (typescript_1.default.isObjectBindingPattern(param.name) || typescript_1.default.isArrayBindingPattern(param.name)) {
            return (0, lattice_js_1.bottom)('Function has destructuring parameters — too complex to inline');
        }
    }
    // Resolver that checks local bindings first, then falls back to parent
    const resolver = (sym) => {
        const local = localBindings.get(sym);
        if (local !== undefined)
            return local;
        return parentResolver(sym);
    };
    // Create evaluator with local scope (no call resolver — prevent nested inlining)
    const evaluator = new expressionEvaluator_js_1.ExpressionEvaluator(checker, resolver);
    // Arrow with expression body
    if (!typescript_1.default.isBlock(body)) {
        return evaluator.evaluate(body);
    }
    // Block body: process const declarations, then evaluate the return expression
    for (const stmt of body.statements) {
        if (typescript_1.default.isVariableStatement(stmt)) {
            for (const varDecl of stmt.declarationList.declarations) {
                if (typescript_1.default.isIdentifier(varDecl.name) && varDecl.initializer) {
                    const sym = checker.getSymbolAtLocation(varDecl.name);
                    if (sym) {
                        localBindings.set(sym, evaluator.evaluate(varDecl.initializer));
                    }
                }
            }
        }
        else if (typescript_1.default.isReturnStatement(stmt) && stmt.expression) {
            return evaluator.evaluate(stmt.expression);
        }
    }
    return (0, lattice_js_1.bottom)('Function does not return a value');
}
//# sourceMappingURL=pureFunctionInliner.js.map