__forceinline: Decided against it for this function because although it's only called by one caller, testing shows that it wastes stack space (room for its automatic variables would be unconditionally reserved in the stack of its caller). Also, the performance benefit of inlining this is too slight. Here's a simple way to verify wasted stack space in a caller that calls an inlined function: DWORD
| 51 | // _asm mov stack, esp |
| 52 | // MsgBox(stack); |
| 53 | LPTSTR Line::ExpandExpression(int aArgIndex, ResultType &aResult, ResultToken *aResultToken |
| 54 | , LPTSTR &aTarget, LPTSTR &aDerefBuf, size_t &aDerefBufSize, LPTSTR aArgDeref[], size_t aExtraSize) |
| 55 | // Caller should ignore aResult unless this function returns NULL. |
| 56 | // Returns a pointer to this expression's result, which can be one of the following: |
| 57 | // 1) NULL, in which case aResult will be either FAIL or EARLY_EXIT to indicate the means by which the current |
| 58 | // quasi-thread was terminated as a result of a function call. |
| 59 | // 2) The constant empty string (""), in which case we do not alter aTarget for our caller. |
| 60 | // 3) Some persistent location not in aDerefBuf, namely the mContents of a variable or a literal string/number, |
| 61 | // such as a function-call that returns "abc", 123, or a variable. |
| 62 | // 4) At position aTarget inside aDerefBuf (note that aDerefBuf might have been reallocated by us). |
| 63 | // aTarget is left unchanged except in case #4, in which case aTarget has been adjusted to the position after our |
| 64 | // result-string's terminator. In addition, in case #4, aDerefBuf, aDerefBufSize, and aArgDeref[] have been adjusted |
| 65 | // for our caller if aDerefBuf was too small and needed to be enlarged. |
| 66 | // |
| 67 | // Thanks to Joost Mulders for providing the expression evaluation code upon which this function is based. |
| 68 | { |
| 69 | LPTSTR target = aTarget; // "target" is used to track our usage (current position) within the aTarget buffer. |
| 70 | |
| 71 | // The following must be defined early so that to_free_count is initialized and the array is guaranteed to be |
| 72 | // "in scope" in case of early "goto" (goto substantially boosts performance and reduces code size here). |
| 73 | ExprTokenType **to_free = (ExprTokenType **)_alloca(mArg[aArgIndex].max_alloc * sizeof(ExprTokenType *)); |
| 74 | int to_free_count = 0; // The actual number of items in use in the above array. |
| 75 | LPTSTR result_to_return = _T(""); // By contrast, NULL is used to tell the caller to abort the current thread. |
| 76 | LPCTSTR error_msg = ERR_EXPR_EVAL, error_info = _T(""); |
| 77 | ExprTokenType *error_value; |
| 78 | Var *output_var = (mActionType == ACT_ASSIGNEXPR) ? VAR(mArg[0]) : NULL; // Resolve early because it's similar in usage/scope to the above. |
| 79 | |
| 80 | ExprTokenType **stack = (ExprTokenType **)_alloca(mArg[aArgIndex].max_stack * sizeof(ExprTokenType *)); |
| 81 | int stack_count = 0; |
| 82 | ExprTokenType *&postfix = mArg[aArgIndex].postfix; |
| 83 | |
| 84 | /////////////////////////////// |
| 85 | // EVALUATE POSTFIX EXPRESSION |
| 86 | /////////////////////////////// |
| 87 | int i, delta; |
| 88 | SymbolType right_is_number, left_is_number, right_is_pure_number, left_is_pure_number, result_symbol; |
| 89 | double right_double, left_double; |
| 90 | __int64 right_int64, left_int64; |
| 91 | LPTSTR right_string, left_string; |
| 92 | size_t right_length, left_length; |
| 93 | TCHAR left_buf[max(MAX_NUMBER_SIZE, _f_retval_buf_size)]; |
| 94 | TCHAR right_buf[MAX_NUMBER_SIZE]; |
| 95 | LPTSTR result; // "result" is used for return values and also the final result. |
| 96 | VarSizeType result_length; |
| 97 | size_t result_size, alloca_usage = 0; // v1.0.45: Track amount of alloca mem to avoid stress on stack from extreme expressions (mostly theoretical). |
| 98 | BOOL done, done_and_have_an_output_var, left_branch_is_true |
| 99 | , left_was_negative, is_pre_op; // BOOL vs. bool benchmarks slightly faster, and is slightly smaller in code size (or maybe it's cp1's int vs. char that shrunk it). |
| 100 | ExprTokenType *this_postfix, *p_postfix; |
| 101 | Var *sym_assign_var, *temp_var; |
| 102 | |
| 103 | // v1.0.44.06: EXPR_SMALL_MEM_LIMIT is the means by which _alloca() is used to boost performance a |
| 104 | // little by avoiding the overhead of malloc+free for small strings. The limit should be something |
| 105 | // small enough that the chance that even 10 of them would cause stack overflow is vanishingly small. |
| 106 | #define EXPR_SMALL_MEM_LIMIT 4097 // The maximum size allowed for an item to qualify for alloca. |
| 107 | #define EXPR_ALLOCA_LIMIT 40000 // The maximum amount of alloca memory for all items. v1.0.45: An extra precaution against stack stress in extreme/theoretical cases. |
| 108 | #define EXPR_IS_DONE (!stack_count && this_postfix[1].symbol == SYM_INVALID) // True if we've used up the last of the operators & operands. Non-zero stack_count combined with SYM_INVALID would indicate an error (an exception will be thrown later, so don't take any shortcuts). |
| 109 | |
| 110 | // For each item in the postfix array: if it's an operand, push it onto stack; if it's an operator or |
nothing calls this directly
no test coverage detected