Related: tdf#135993 Destroy temporary OpCodeMap when reading config

Initialized at FormulaCompiler base class instance it lacks AddIn
mapping and a still existing OpCodeMap prevents necessary
reinitialization from derived ScCompiler instance later.

Change-Id: I0c2db41dd45829abfb8460730264f097ab76ab2e
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137881
Tested-by: Jenkins
Reviewed-by: Eike Rathke <erack@redhat.com>
diff --git a/formula/source/core/api/FormulaCompiler.cxx b/formula/source/core/api/FormulaCompiler.cxx
index be926e7..2b0db73 100644
--- a/formula/source/core/api/FormulaCompiler.cxx
+++ b/formula/source/core/api/FormulaCompiler.cxx
@@ -823,37 +823,37 @@ FormulaCompiler::OpCodeMapPtr FormulaCompiler::GetOpCodeMap( const sal_Int32 nLa
    {
        case FormulaLanguage::ODFF :
            if (!mxSymbolsODFF)
                InitSymbolsODFF();
                InitSymbolsODFF( InitSymbols::INIT);
            xMap = mxSymbolsODFF;
            break;
        case FormulaLanguage::ODF_11 :
            if (!mxSymbolsPODF)
                InitSymbolsPODF();
                InitSymbolsPODF( InitSymbols::INIT);
            xMap = mxSymbolsPODF;
            break;
        case FormulaLanguage::ENGLISH :
            if (!mxSymbolsEnglish)
                InitSymbolsEnglish();
                InitSymbolsEnglish( InitSymbols::INIT);
            xMap = mxSymbolsEnglish;
            break;
        case FormulaLanguage::NATIVE :
            if (!mxSymbolsNative)
                InitSymbolsNative();
                InitSymbolsNative( InitSymbols::INIT);
            xMap = mxSymbolsNative;
            break;
        case FormulaLanguage::XL_ENGLISH:
            if (!mxSymbolsEnglishXL)
                InitSymbolsEnglishXL();
                InitSymbolsEnglishXL( InitSymbols::INIT);
            xMap = mxSymbolsEnglishXL;
            break;
        case FormulaLanguage::OOXML:
            if (!mxSymbolsOOXML)
                InitSymbolsOOXML();
                InitSymbolsOOXML( InitSymbols::INIT);
            xMap = mxSymbolsOOXML;
            break;
        case FormulaLanguage::API :
            if (!mxSymbolsAPI)
                InitSymbolsAPI();
                InitSymbolsAPI( InitSymbols::INIT);
            xMap = mxSymbolsAPI;
            break;
        default:
@@ -862,6 +862,62 @@ FormulaCompiler::OpCodeMapPtr FormulaCompiler::GetOpCodeMap( const sal_Int32 nLa
    return xMap;
}

void FormulaCompiler::DestroyOpCodeMap( const sal_Int32 nLanguage )
{
    using namespace sheet;
    switch (nLanguage)
    {
        case FormulaLanguage::ODFF :
            InitSymbolsODFF( InitSymbols::DESTROY);
            break;
        case FormulaLanguage::ODF_11 :
            InitSymbolsPODF( InitSymbols::DESTROY);
            break;
        case FormulaLanguage::ENGLISH :
            InitSymbolsEnglish( InitSymbols::DESTROY);
            break;
        case FormulaLanguage::NATIVE :
            InitSymbolsNative( InitSymbols::DESTROY);
            break;
        case FormulaLanguage::XL_ENGLISH:
            InitSymbolsEnglishXL( InitSymbols::DESTROY);
            break;
        case FormulaLanguage::OOXML:
            InitSymbolsOOXML( InitSymbols::DESTROY);
            break;
        case FormulaLanguage::API :
            InitSymbolsAPI( InitSymbols::DESTROY);
            break;
        default:
            ;   // nothing
    }
}

bool FormulaCompiler::HasOpCodeMap( const sal_Int32 nLanguage ) const
{
    using namespace sheet;
    switch (nLanguage)
    {
        case FormulaLanguage::ODFF :
            return InitSymbolsODFF( InitSymbols::ASK);
        case FormulaLanguage::ODF_11 :
            return InitSymbolsPODF( InitSymbols::ASK);
        case FormulaLanguage::ENGLISH :
            return InitSymbolsEnglish( InitSymbols::ASK);
        case FormulaLanguage::NATIVE :
            return InitSymbolsNative( InitSymbols::ASK);
        case FormulaLanguage::XL_ENGLISH:
            return InitSymbolsEnglishXL( InitSymbols::ASK);
        case FormulaLanguage::OOXML:
            return InitSymbolsOOXML( InitSymbols::ASK);
        case FormulaLanguage::API :
            return InitSymbolsAPI( InitSymbols::ASK);
        default:
            ;   // nothing
    }
    return false;
}

OUString FormulaCompiler::FindAddInFunction( const OUString& /*rUpperName*/, bool /*bLocalFirst*/ ) const
{
    return OUString();
@@ -898,12 +954,16 @@ FormulaCompiler::OpCodeMapPtr FormulaCompiler::CreateOpCodeMap(
    return xMap;
}

static void lcl_fillNativeSymbols( FormulaCompiler::NonConstOpCodeMapPtr& xMap, bool bDestroy = false )
static bool lcl_fillNativeSymbols( FormulaCompiler::NonConstOpCodeMapPtr& xMap, FormulaCompiler::InitSymbols eWhat = FormulaCompiler::InitSymbols::INIT )
{
    static OpCodeMapData aSymbolMap;
    osl::MutexGuard aGuard(&aSymbolMap.maMtx);

    if ( bDestroy )
    if (eWhat == FormulaCompiler::InitSymbols::ASK)
    {
        return bool(aSymbolMap.mxSymbolMap);
    }
    else if (eWhat == FormulaCompiler::InitSymbols::DESTROY)
    {
        aSymbolMap.mxSymbolMap.reset();
    }
@@ -919,6 +979,8 @@ static void lcl_fillNativeSymbols( FormulaCompiler::NonConstOpCodeMapPtr& xMap, 
    }

    xMap = aSymbolMap.mxSymbolMap;

    return true;
}

const OUString& FormulaCompiler::GetNativeSymbol( OpCode eOp )
@@ -933,54 +995,80 @@ sal_Unicode FormulaCompiler::GetNativeSymbolChar( OpCode eOp )
    return GetNativeSymbol(eOp)[0];
}

void FormulaCompiler::InitSymbolsNative() const
bool FormulaCompiler::InitSymbolsNative( FormulaCompiler::InitSymbols eWhat ) const
{
    lcl_fillNativeSymbols( mxSymbolsNative);
    return lcl_fillNativeSymbols( mxSymbolsNative, eWhat);
}

void FormulaCompiler::InitSymbolsEnglish() const
bool FormulaCompiler::InitSymbolsEnglish( FormulaCompiler::InitSymbols eWhat ) const
{
    static OpCodeMapData aMap;
    osl::MutexGuard aGuard(&aMap.maMtx);
    if (!aMap.mxSymbolMap)
    if (eWhat == InitSymbols::ASK)
        return bool(aMap.mxSymbolMap);
    else if (eWhat == InitSymbols::DESTROY)
        aMap.mxSymbolMap.reset();
    else if (!aMap.mxSymbolMap)
        loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH, FormulaGrammar::GRAM_ENGLISH, aMap.mxSymbolMap);
    mxSymbolsEnglish = aMap.mxSymbolMap;
    return true;
}

void FormulaCompiler::InitSymbolsPODF() const
bool FormulaCompiler::InitSymbolsPODF( FormulaCompiler::InitSymbols eWhat ) const
{
    static OpCodeMapData aMap;
    osl::MutexGuard aGuard(&aMap.maMtx);
    if (!aMap.mxSymbolMap)
    if (eWhat == InitSymbols::ASK)
        return bool(aMap.mxSymbolMap);
    else if (eWhat == InitSymbols::DESTROY)
        aMap.mxSymbolMap.reset();
    else if (!aMap.mxSymbolMap)
        loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_PODF, FormulaGrammar::GRAM_PODF, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
    mxSymbolsPODF = aMap.mxSymbolMap;
    return true;
}

void FormulaCompiler::InitSymbolsAPI() const
bool FormulaCompiler::InitSymbolsAPI( FormulaCompiler::InitSymbols eWhat ) const
{
    static OpCodeMapData aMap;
    osl::MutexGuard aGuard(&aMap.maMtx);
    if (!aMap.mxSymbolMap)
    if (eWhat == InitSymbols::ASK)
        return bool(aMap.mxSymbolMap);
    else if (eWhat == InitSymbols::DESTROY)
        aMap.mxSymbolMap.reset();
    else if (!aMap.mxSymbolMap)
        loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_API, FormulaGrammar::GRAM_API, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
    mxSymbolsAPI = aMap.mxSymbolMap;
    return true;
}

void FormulaCompiler::InitSymbolsODFF() const
bool FormulaCompiler::InitSymbolsODFF( FormulaCompiler::InitSymbols eWhat ) const
{
    static OpCodeMapData aMap;
    osl::MutexGuard aGuard(&aMap.maMtx);
    if (!aMap.mxSymbolMap)
    if (eWhat == InitSymbols::ASK)
        return bool(aMap.mxSymbolMap);
    else if (eWhat == InitSymbols::DESTROY)
        aMap.mxSymbolMap.reset();
    else if (!aMap.mxSymbolMap)
        loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF, FormulaGrammar::GRAM_ODFF, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
    mxSymbolsODFF = aMap.mxSymbolMap;
    return true;
}

void FormulaCompiler::InitSymbolsEnglishXL() const
bool FormulaCompiler::InitSymbolsEnglishXL( FormulaCompiler::InitSymbols eWhat ) const
{
    static OpCodeMapData aMap;
    osl::MutexGuard aGuard(&aMap.maMtx);
    if (!aMap.mxSymbolMap)
    if (eWhat == InitSymbols::ASK)
        return bool(aMap.mxSymbolMap);
    else if (eWhat == InitSymbols::DESTROY)
        aMap.mxSymbolMap.reset();
    else if (!aMap.mxSymbolMap)
        loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH, FormulaGrammar::GRAM_ENGLISH, aMap.mxSymbolMap);
    mxSymbolsEnglishXL = aMap.mxSymbolMap;
    if (eWhat != InitSymbols::INIT)
        return true;

    // TODO: For now, just replace the separators to the Excel English
    // variants. Later, if we want to properly map Excel functions with Calc
@@ -988,15 +1076,22 @@ void FormulaCompiler::InitSymbolsEnglishXL() const
    mxSymbolsEnglishXL->putOpCode( OUString(','), ocSep, nullptr);
    mxSymbolsEnglishXL->putOpCode( OUString(','), ocArrayColSep, nullptr);
    mxSymbolsEnglishXL->putOpCode( OUString(';'), ocArrayRowSep, nullptr);

    return true;
}

void FormulaCompiler::InitSymbolsOOXML() const
bool FormulaCompiler::InitSymbolsOOXML( FormulaCompiler::InitSymbols eWhat ) const
{
    static OpCodeMapData aMap;
    osl::MutexGuard aGuard(&aMap.maMtx);
    if (!aMap.mxSymbolMap)
    if (eWhat == InitSymbols::ASK)
        return bool(aMap.mxSymbolMap);
    else if (eWhat == InitSymbols::DESTROY)
        aMap.mxSymbolMap.reset();
    else if (!aMap.mxSymbolMap)
        loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML, FormulaGrammar::GRAM_OOXML, aMap.mxSymbolMap, SeparatorType::RESOURCE_BASE);
    mxSymbolsOOXML = aMap.mxSymbolMap;
    return true;
}


@@ -2588,7 +2683,7 @@ void FormulaCompiler::UpdateSeparatorsNative(
void FormulaCompiler::ResetNativeSymbols()
{
    NonConstOpCodeMapPtr xSymbolsNative;
    lcl_fillNativeSymbols( xSymbolsNative, true);
    lcl_fillNativeSymbols( xSymbolsNative, InitSymbols::DESTROY);
    lcl_fillNativeSymbols( xSymbolsNative);
}

diff --git a/include/formula/FormulaCompiler.hxx b/include/formula/FormulaCompiler.hxx
index 5544ff8..3663203 100644
--- a/include/formula/FormulaCompiler.hxx
+++ b/include/formula/FormulaCompiler.hxx
@@ -203,6 +203,26 @@ public:
     */
    OpCodeMapPtr GetOpCodeMap( const sal_Int32 nLanguage ) const;

    /** Destroy the singleton OpCodeMap for formula language.

        This unconditionally destroys the underlying singleton instance of the
        map to be reinitialized again later on the next GetOpCodeMap() call.
        Use if the base class FormulaCompiler::GetOpCodeMap() was called and
        created the map (i.e. HasOpCodeMap() before returned false) and later a
        derived class like ScCompiler shall initialize it including AddIns.

        @param nLanguage
            One of css::sheet::FormulaLanguage constants.
     */
    void DestroyOpCodeMap( const sal_Int32 nLanguage );

    /** Whether the singleton OpCodeMap for formula language exists already.

        @param nLanguage
            One of css::sheet::FormulaLanguage constants.
     */
    bool HasOpCodeMap( const sal_Int32 nLanguage ) const;

    /** Create an internal symbol map from API mapping.
        @param bEnglish
            Use English number parser / formatter instead of native.
@@ -370,14 +390,22 @@ protected:
    bool mbComputeII;  // whether to attempt computing implicit intersection ranges while building the RPN array.
    bool mbMatrixFlag; // whether the formula is a matrix formula (needed for II computation)

public:
    enum InitSymbols
    {
        ASK = 0,
        INIT,
        DESTROY
    };

private:
    void InitSymbolsNative() const;    /// only SymbolsNative, on first document creation
    void InitSymbolsEnglish() const;   /// only SymbolsEnglish, maybe later
    void InitSymbolsPODF() const;      /// only SymbolsPODF, on demand
    void InitSymbolsAPI() const;       /// only SymbolsAPI, on demand
    void InitSymbolsODFF() const;      /// only SymbolsODFF, on demand
    void InitSymbolsEnglishXL() const; /// only SymbolsEnglishXL, on demand
    void InitSymbolsOOXML() const;     /// only SymbolsOOXML, on demand
    bool InitSymbolsNative( InitSymbols ) const;    /// only SymbolsNative, on first document creation
    bool InitSymbolsEnglish( InitSymbols ) const;   /// only SymbolsEnglish, maybe later
    bool InitSymbolsPODF( InitSymbols ) const;      /// only SymbolsPODF, on demand
    bool InitSymbolsAPI( InitSymbols ) const;       /// only SymbolsAPI, on demand
    bool InitSymbolsODFF( InitSymbols ) const;      /// only SymbolsODFF, on demand
    bool InitSymbolsEnglishXL( InitSymbols ) const; /// only SymbolsEnglishXL, on demand
    bool InitSymbolsOOXML( InitSymbols ) const;     /// only SymbolsOOXML, on demand

    void loadSymbols(const std::pair<const char*, int>* pSymbols, FormulaGrammar::Grammar eGrammar, NonConstOpCodeMapPtr& rxMap,
            SeparatorType eSepType = SeparatorType::SEMICOLON_BASE) const;
diff --git a/sc/source/core/tool/calcconfig.cxx b/sc/source/core/tool/calcconfig.cxx
index f1673e0..2783670 100644
--- a/sc/source/core/tool/calcconfig.cxx
+++ b/sc/source/core/tool/calcconfig.cxx
@@ -187,7 +187,11 @@ bool ScCalcConfig::operator!= (const ScCalcConfig& r) const
OUString ScOpCodeSetToSymbolicString(const ScCalcConfig::OpCodeSet& rOpCodes)
{
    OUStringBuffer result(256);
    // If GetOpCodeMap() initializes the map at base class
    // formula::FormulaCompiler before any ScCompiler did, all AddIn mapping
    // would be missing also in future. So if it didn't exist destroy it again.
    formula::FormulaCompiler aCompiler;
    const bool bTemporary = !aCompiler.HasOpCodeMap(css::sheet::FormulaLanguage::ENGLISH);
    formula::FormulaCompiler::OpCodeMapPtr pOpCodeMap(aCompiler.GetOpCodeMap(css::sheet::FormulaLanguage::ENGLISH));

    for (auto i = rOpCodes->begin(); i != rOpCodes->end(); ++i)
@@ -197,13 +201,18 @@ OUString ScOpCodeSetToSymbolicString(const ScCalcConfig::OpCodeSet& rOpCodes)
        result.append(pOpCodeMap->getSymbol(*i));
    }

    if (bTemporary)
        aCompiler.DestroyOpCodeMap(css::sheet::FormulaLanguage::ENGLISH);

    return result.makeStringAndClear();
}

ScCalcConfig::OpCodeSet ScStringToOpCodeSet(std::u16string_view rOpCodes)
{
    ScCalcConfig::OpCodeSet result = std::make_shared<o3tl::sorted_vector< OpCode >>();
    // Same as above.
    formula::FormulaCompiler aCompiler;
    const bool bTemporary = !aCompiler.HasOpCodeMap(css::sheet::FormulaLanguage::ENGLISH);
    formula::FormulaCompiler::OpCodeMapPtr pOpCodeMap(aCompiler.GetOpCodeMap(css::sheet::FormulaLanguage::ENGLISH));

    const formula::OpCodeHashMap& rHashMap(pOpCodeMap->getHashMap());
@@ -234,6 +243,10 @@ ScCalcConfig::OpCodeSet ScStringToOpCodeSet(std::u16string_view rOpCodes)
    // HACK: Both unary and binary minus have the same string but different opcodes.
    if( result->find( ocSub ) != result->end())
        result->insert( ocNegSub );

    if (bTemporary)
        aCompiler.DestroyOpCodeMap(css::sheet::FormulaLanguage::ENGLISH);

    return result;
}