let rec inline_expr_replace depth numargs repl_expr expr =
        let rec loop = function
                | RVariable(LocalVar(uid, d, ind)) when d = depth && ind <= numargs ->
                                List.nth repl_expr (ind - 1)
                | RVariable(_) | RValue(_) | RVarArg(_) as e -> e
                | RBinaryOp(expr1, op, expr2) -> RBinaryOp(loop expr1, op, loop expr2)
                | RCompOp(e1, op, e2) -> RCompOp(loop e1, op, loop e2)
                | RNot(e) -> RNot(loop e)
                | RTernaryCond(e1, e2, e3) -> RTernaryCond(loop e1, loop e2, loop e3)
                | RDeclaration(e1, e2) -> RDeclaration(loop e1, loop e2)
                | RAssignment(e1, e2) -> RAssignment(loop e1, loop e2)
                | RMapExpr(props) -> RMapExpr(List.map (fun p -> let (name, e) = p in (name, loop e)) props)
                | RArrayExpr(elist) -> RArrayExpr(List.map(fun e -> loop e) elist)
                | RFunctionCall(e, elist) -> RFunctionCall(loop e, List.map(fun e -> loop e) elist)
                | RMemberExpr(e1, e2) -> RMemberExpr(loop e1, loop e2)
                | RPostFixSum(e, i) -> RPostFixSum(loop e, i)
        in loop expr
and
(** Replace non modified variables with their declared value @param env analysis environment @param inline_uids list of inlined functions to avoid recursively inlining recursive inlinable functions @param expression expression to process @return an expression with constant variables replaced by their value *)

replace_constant env inline_uids = function
        |        RVariable(loc) ->
                        let uid = uid_from_loc loc
                        in if Environment.is_constant env uid then
                                try
                                        match (Environment.get_constant_value env uid) with
                                        | RFunctionValue(_) -> RVariable(loc)
                                        | value -> RValue(value)
                                with Not_found -> RVariable(loc)
                        else RVariable(loc)
        | RNot(expr) -> RNot(replace_constant env inline_uids expr)
        | RBinaryOp(expr1, op, expr2) ->
                        let (e1, e2) = (replace_constant env inline_uids expr1, replace_constant env inline_uids expr2)
                        in (try match (e1, e2) with
                                | (RValue(v1), RValue(v2)) -> RValue(Expression.evaluate_op v1 v2 op)
                                | _ -> RBinaryOp(e1, op, e2)
                        with _ -> RBinaryOp(e1, op, e2))
        | RCompOp(expr1, op, expr2) ->
                        RCompOp(replace_constant env inline_uids expr1, op, replace_constant env inline_uids expr2)
        | RValue(RFunctionValue(locals, depth, args, vararg, stmts, closvars, inline)) ->
                        RValue(RFunctionValue(locals, depth, args, vararg, List.map(fun stmt -> pass2 env stmt) stmts, closvars, inline))
        | RValue(_) | RPostFixSum(_) | RVarArg(_) as value -> value
        | RFunctionCall(expr, expr_list) ->
                        let e = replace_constant env inline_uids expr
                        in let e_list = List.map(fun e -> replace_constant env inline_uids e) expr_list
                        in (match e with
                                | RVariable(GlobalVar(uid, _))
                                | RVariable(LocalVar(uid, _, _)) when is_constant env uid ->
                                                (match get_constant_value env uid with
                                                        | RFunctionValue(_, depth, numargs, false, _, NoneSome expr) ->
                                                                        if List.exists (fun i -> i == uid) inline_uids then
                                                                                RFunctionCall(e, e_list)
                                                                        else
                                                                                replace_constant env (uid:: inline_uids) (inline_expr_replace depth numargs e_list expr)
                                                        | _ -> RFunctionCall(e, e_list))
                                | _ -> RFunctionCall(e, e_list))
        | RAssignment(expr1, expr2) -> RAssignment(expr1, replace_constant env inline_uids expr2)
        | RDeclaration(expr1, expr2) ->
                        let expr2 = replace_constant env inline_uids expr2
                        in (match (expr1, expr2) with
                                | (RVariable(loc), RValue(value)) ->
                                                let uid = uid_from_loc loc
                                                in if is_constant env uid then
                                                        match value with
                                                        | RFunctionValue(_) -> RDeclaration(expr1, expr2)
                                                        | _ -> (Environment.set_constant_value env uid value; RValue(RUndefined))
                                                else
                                                        RDeclaration(expr1, expr2)
                                | _ -> RDeclaration(expr1, expr2))
        | RMemberExpr(expr1, expr2) ->
                        RMemberExpr(replace_constant env inline_uids expr1, replace_constant env inline_uids expr2)
        | RArrayExpr(expr_list) ->
                        RArrayExpr(List.map(fun e -> replace_constant env inline_uids e) expr_list)
        | RMapExpr(prop_list) ->
                        RMapExpr(List.map (fun prop -> let (name, e) = prop in (name, replace_constant env inline_uids e)) prop_list)
        | RTernaryCond(expr1, expr2, expr3) ->
                        RTernaryCond(replace_constant env inline_uids expr1, replace_constant env inline_uids expr2,
                                replace_constant env inline_uids expr3)
(** Looks for expressions where constants can be substituted @param env analysis environment @param stmt statement *)

and pass2 env = function
        | RProgram(stmts) -> RProgram(List.map (fun stmt -> pass2 env stmt) stmts)
        | RStatementBlock(stmts) ->        RStatementBlock(List.map (fun stmt -> pass2 env stmt) stmts)
        | RThrow(expr, cloc) -> RThrow(replace_constant env [] expr, cloc)
        | RCase(Some expr, cloc) -> RCase(Some (replace_constant env [] expr), cloc)
        | RReturn(expr, cloc) -> RReturn(replace_constant env [] expr, cloc)
        | RContinue(_) | RBreak(_) | RCase(None, _) | RNoop | RFastIterator _ as stmt -> stmt
        | RExpressionStatement(expr, cloc) ->
                        (match replace_constant env [] expr with
                                | RValue(RUndefined-> RNoop
                                | expr -> RExpressionStatement(expr, cloc))
        | RFor(expr1, expr2, expr3, stmt, cloc) ->
                        let expr1 = replace_constant env [] expr1
                        in let expr2 = replace_constant env [] expr2
                        in let expr3 = replace_constant env [] expr3
                        in let stmt = pass2 env stmt
                        in (match (expr1, expr2, expr3) with
                                | (RDeclaration(RVariable(vloc1), RValue(RIntegerValue(start))),
                                RCompOp(RVariable(vloc2), LessThanRValue(RIntegerValue(max))),
                                RPostFixSum(RVariable(vloc3), inc)) when vloc1 = vloc2 && vloc1 = vloc3 ->
                                                RFastIterator(vloc1, start , max , inc, stmt , cloc)
                                | (RDeclaration(RVariable(vloc1), RValue(RIntegerValue(start))),
                                RCompOp(RVariable(vloc2), LessThanRValue(RIntegerValue(max))),
                                RAssignment(RVariable(vloc3), RBinaryOp(RVariable(vloc4), PlusRValue(RIntegerValue(inc)))))
                                when vloc1 = vloc2 && vloc1 = vloc3 & vloc1 = vloc4 ->
                                                RFastIterator(vloc1, start , max , inc, stmt , cloc)
                                | _ -> RFor(expr1, expr2 , expr3 , stmt , cloc))
        | RIf(expr, stmt1, stmt2, cloc) -> RIf(replace_constant env [] expr, pass2 env stmt1, pass2 env stmt2, cloc)
        | RTryFinally(stmt1, stmt2, cloc) -> RTryFinally(pass2 env stmt1, pass2 env stmt2, cloc)
        | RTryCatch(stmt1, v, stmt2, cloc) -> RTryCatch(pass2 env stmt1, v, pass2 env stmt2, cloc)
        | RSwitch(expr, stmts, cloc) -> RSwitch(replace_constant env [] expr,
                                (List.map (fun stmt -> pass2 env stmt) stmts), cloc)
        | RForEach(v, expr, stmt, cloc) -> RForEach(v, replace_constant env [] expr, pass2 env stmt, cloc)