| 13538 | |
| 13539 | |
| 13540 | ResultType Script::PreparseVarRefs() |
| 13541 | { |
| 13542 | // AddLine() and ExpressionToPostfix() currently leave validation of output variables |
| 13543 | // and lvalues to this function, to reduce code size. Search for "VarIsReadOnlyError". |
| 13544 | for (Line *line = mFirstLine; line; line = line->mNextLine) |
| 13545 | { |
| 13546 | switch (line->mActionType) |
| 13547 | { // Establish context for FindOrAddVar: |
| 13548 | case ACT_BLOCK_BEGIN: if (line->mAttribute) g->CurrentFunc = (UserFunc *)line->mAttribute; break; |
| 13549 | case ACT_BLOCK_END: if (line->mAttribute) g->CurrentFunc = g->CurrentFunc->mOuterFunc; break; |
| 13550 | } |
| 13551 | |
| 13552 | for (int a = 0; a < line->mArgc; ++a) |
| 13553 | { |
| 13554 | ArgStruct &arg = line->mArg[a]; |
| 13555 | if (!arg.is_expression) |
| 13556 | continue; |
| 13557 | for (ExprTokenType *token = arg.postfix; token->symbol != SYM_INVALID; ++token) |
| 13558 | { |
| 13559 | if (token->symbol != SYM_VAR // Not a var. |
| 13560 | || VARREF_IS_WRITE(token->var_usage)) // Already resolved by ExpressionToPostfix. |
| 13561 | continue; |
| 13562 | if ( !(token->var = FindOrAddVar(token->var_deref->marker, token->var_deref->length, FINDVAR_FOR_READ)) ) |
| 13563 | return FAIL; |
| 13564 | if (token->var->IsAlias()) // Upvar. |
| 13565 | continue; |
| 13566 | switch (token->var->Type()) |
| 13567 | { |
| 13568 | case VAR_CONSTANT: |
| 13569 | // Resolve constants to their values, except in cases like IsSet(SomeClass), or when |
| 13570 | // the constant is a closure (which may change each time the outer function is called). |
| 13571 | if (!token->var->IsLocal() && token->var_usage == VARREF_READ && !token->var->IsUninitialized()) |
| 13572 | token->var->ToToken(*token); |
| 13573 | continue; |
| 13574 | case VAR_VIRTUAL: |
| 13575 | if (token->var_usage == VARREF_READ) |
| 13576 | ++arg.max_alloc; // Reserve a to_free[] slot for it in ExpandExpression(). |
| 13577 | break; |
| 13578 | default: |
| 13579 | // Suppress any VarUnset warnings for IsSet(var) so that it can be used to determine if an |
| 13580 | // optionally-set global variable has been set, such as a class in an optional #Include. |
| 13581 | // MarkAssignedSomewhere() isn't used for this because PreprocessLocalVars() hasn't been |
| 13582 | // called yet, and it relies on the attribute being accurate. |
| 13583 | if (token->var_usage == VARREF_ISSET) |
| 13584 | token->var->MarkAlreadyWarned(); |
| 13585 | // It's too early to show VarUnset warnings, since not all VARREF_ISSET references have been marked. |
| 13586 | // The effect of IsSet(v) for suppressing the warning shouldn't be positional since it's feasible for |
| 13587 | // a check in one function to guard evaluation of a VARREF_READ in some other function. |
| 13588 | } |
| 13589 | } |
| 13590 | if (arg.type == ARG_TYPE_INPUT_VAR) |
| 13591 | { |
| 13592 | if (arg.postfix->symbol != SYM_VAR || arg.postfix->var->Type() != VAR_NORMAL) |
| 13593 | { |
| 13594 | // Can't be ARG_TYPE_INPUT_VAR after all, as VAR_VIRTUAL and VAR_CONSTANT require ExpandExpression |
| 13595 | // (unless it's a constant which was converted to SYM_OBJECT above). |
| 13596 | arg.type = ARG_TYPE_NORMAL; |
| 13597 | } |
nothing calls this directly
no test coverage detected