tdf#85371 - grant write access to the method used as a variable

During the creation of the parameter list of a method, explicitly grant
write access to the method which will used as a variable. Otherwise, the
name of the method can't be used in certain statements, i.e., index in a
for loop or as a dimension in ReDim.

Change-Id: I3e4c49c21fd3345d5ddd69bc31a5823b5de5b8e7
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/104696
Tested-by: Jenkins
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
diff --git a/basic/qa/basic_coverage/test_method_name_variable.vb b/basic/qa/basic_coverage/test_method_name_variable.vb
new file mode 100644
index 0000000..ea45366
--- /dev/null
+++ b/basic/qa/basic_coverage/test_method_name_variable.vb
@@ -0,0 +1,35 @@
' This file is part of the LibreOffice project.
'
' This Source Code Form is subject to the terms of the Mozilla Public
' License, v. 2.0. If a copy of the MPL was not distributed with this
' file, You can obtain one at http://mozilla.org/MPL/2.0/.
'

Function assignVarToMethod() As Integer

    ' method name used as dimension specifier
    Dim fieldOfLongs() As Long
    ReDim fieldOfLongs(assignVarToMethod) As Long

    ' method name used as loop index
    Dim sum As Integer
    For assignVarToMethod = 1 To 3
        sum = sum + assignVarToMethod
    Next assignVarToMethod
    assignVarToMethod = sum

End Function

Function doUnitTest() As Integer

    doUnitTest = 0

    ' tdf#85371 - check if the name of the method can be used as a variable in certain statements
    If (assignVarToMethod() <> 6) Then Exit Function
    ' tdf#85371 - check if an assignment to the function fails outside of the function itself
    assignVarToMethod = 0
    If (assignVarToMethod() <> 6) Then Exit Function

    doUnitTest = 1

End Function
diff --git a/basic/source/runtime/runtime.cxx b/basic/source/runtime/runtime.cxx
index 498c716..0606b78 100644
--- a/basic/source/runtime/runtime.cxx
+++ b/basic/source/runtime/runtime.cxx
@@ -83,6 +83,34 @@ using namespace ::com::sun::star;
static void lcl_clearImpl( SbxVariableRef const & refVar, SbxDataType const & eType );
static void lcl_eraseImpl( SbxVariableRef const & refVar, bool bVBAEnabled );

namespace
{
class ScopedWritableGuard
{
public:
    ScopedWritableGuard(const SbxVariableRef& rVar, bool bMakeWritable)
        : m_rVar(rVar)
        , m_bReset(bMakeWritable && !rVar->CanWrite())
    {
        if (m_bReset)
        {
            m_rVar->SetFlag(SbxFlagBits::Write);
        }
    }
    ~ScopedWritableGuard()
    {
        if (m_bReset)
        {
            m_rVar->ResetFlag(SbxFlagBits::Write);
        }
    }

private:
    SbxVariableRef m_rVar;
    bool m_bReset;
};
}

bool SbiRuntime::isVBAEnabled()
{
    bool bResult = false;
@@ -1131,6 +1159,9 @@ void SbiRuntime::PushFor()
    p->refEnd = PopVar();
    SbxVariableRef xBgn = PopVar();
    p->refVar = PopVar();
    // tdf#85371 - grant explicitly write access to the index variable
    // since it could be the name of a method itself used in the next statement.
    ScopedWritableGuard aGuard(p->refVar, p->refVar.get() == pMeth);
    *(p->refVar) = *xBgn;
    nForLvl++;
}
@@ -2583,6 +2614,9 @@ void SbiRuntime::StepNEXT()
        StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
        return;
    }
    // tdf#85371 - grant explicitly write access to the index variable
    // since it could be the name of a method itself used in the next statement.
    ScopedWritableGuard aGuard(pForStk->refVar, pForStk->refVar.get() == pMeth);
    pForStk->refVar->Compute( SbxPLUS, *pForStk->refInc );
}

@@ -3360,7 +3394,13 @@ void SbiRuntime::StepBASED( sal_uInt32 nOp1 )
    sal_uInt16 uBase = static_cast<sal_uInt16>(nOp1 & 1);       // Can only be 0 or 1
    p1->PutInteger( uBase );
    if( !bCompatible )
    {
        // tdf#85371 - grant explicitly write access to the dimension variable
        // since in Star/OpenOffice Basic the upper index border is affected,
        // and the dimension variable could be the name of the method itself.
        ScopedWritableGuard aGuard(x2, x2.get() == pMeth);
        x2->Compute( SbxPLUS, *p1 );
    }
    PushVar( x2.get() );  // first the Expr
    PushVar( p1 );  // then the Base
}