| 7155 | |
| 7156 | |
| 7157 | Var *Script::FindUpVar(LPCTSTR aVarName, UserFunc &aInner, ResultType *aDisplayError) |
| 7158 | { |
| 7159 | // Do not search outer if inner is *explicitly* assume-global, since in that case a |
| 7160 | // global var should be returned in preference to anything defined by outer. |
| 7161 | if (aInner.mDefaultVarType == VAR_DECLARE_GLOBAL) |
| 7162 | return nullptr; |
| 7163 | auto &outer = *aInner.mOuterFunc; |
| 7164 | Var *outer_var; |
| 7165 | if ( (outer_var = outer.mStaticVars.Find(aVarName)) ) |
| 7166 | return outer_var; |
| 7167 | if ( !(outer_var = outer.mVars.Find(aVarName)) |
| 7168 | && !(outer.mOuterFunc && (outer_var = FindUpVar(aVarName, outer, aDisplayError))) ) |
| 7169 | return nullptr; |
| 7170 | // At this point, all var refs used in declarations, assignments or &var in the outer |
| 7171 | // function should have already been parsed, while it's possible that some read-refs |
| 7172 | // have not. Ignore all variables that lack an assignment, &var or declaration. |
| 7173 | // - Avoids an issue where only read-refs ABOVE the nested function are accessible |
| 7174 | // (which could also be fixed by changing PreparseVarRefs to parse one function |
| 7175 | // at a time, parsing outer in its entirety before inner). |
| 7176 | // - Avoids inconsistency between read-refs and write-refs: when the outer var is created |
| 7177 | // by a read-ref, closures which only read would capture it, while closures which write |
| 7178 | // would create their own local. |
| 7179 | // - Avoids having behaviour depend on whether a global exists: when outer and inner both |
| 7180 | // only use read-refs (but maybe assign dynamically), whether the nested function becomes |
| 7181 | // a closure might depend on whether the var is global or local to outer. |
| 7182 | // - Having a non-dynamic assignment (and no global declaration) makes it easier to see |
| 7183 | // that the var is local by looking at just the outer function, and avoids warnings. |
| 7184 | // - A clear rule is easier to remember. |
| 7185 | if ( !(outer_var->IsAssignedSomewhere() || outer_var->IsDeclared()) ) |
| 7186 | return nullptr; |
| 7187 | // Since this local variable is not static, things need to be set up so that the inner |
| 7188 | // function will capture it as an upvar at the appropriate time. |
| 7189 | if (mIsReadyToExecute) |
| 7190 | { |
| 7191 | // This dynamic reference resolved to a variable that hasn't been captured by the |
| 7192 | // current function, so is not safe to return. |
| 7193 | if (aDisplayError) |
| 7194 | *aDisplayError = RuntimeError(ERR_DYNAMIC_UPVAR, aVarName); |
| 7195 | return nullptr; |
| 7196 | } |
| 7197 | ASSERT(!outer.mDownVar && !aInner.mUpVar); // PreprocessLocalVars has not yet been called. |
| 7198 | if (!(outer_var->Scope() & VAR_DOWNVAR)) |
| 7199 | { |
| 7200 | outer.mDownVarCount++; |
| 7201 | outer_var->Scope() |= VAR_DOWNVAR; |
| 7202 | } |
| 7203 | // At this point aInner doesn't have a variable by this name, so create one: |
| 7204 | int insert_pos; |
| 7205 | aInner.mVars.Find(aVarName, &insert_pos); |
| 7206 | Var *inner_var = AddVar(aVarName, 0, &aInner.mVars, insert_pos, VAR_LOCAL | VAR_DECLARED); // VAR_DECLARED suppresses checking for a global for #Warn LocalSameAsGlobal or when #Warn UseUnset is triggered. |
| 7207 | if (!inner_var) |
| 7208 | { |
| 7209 | if (aDisplayError) |
| 7210 | *aDisplayError = FAIL; // Error already displayed. |
| 7211 | return nullptr; |
| 7212 | } |
| 7213 | ASSERT(outer_var->Type() != VAR_CONSTANT || outer_var->HasObject() && dynamic_cast<UserFunc*>(outer_var->Object())); |
| 7214 | bool indefinite = outer_var->Type() == VAR_CONSTANT && !((UserFunc *)outer_var->Object())->mUpVarCount; |
nothing calls this directly
no test coverage detected