tdf#129256: don't create objects only to destroy when moving preserved array

Restructure the StepDCREATE_IMPL code to do the preservation step before
creating the objects for the rest of array.

Change-Id: I4e4ba718af2da035b08397522e726eb131f813a4
Reviewed-on: https://gerrit.libreoffice.org/84706
Tested-by: Jenkins
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
(cherry picked from commit bc1a8c682f889e488a7961faa0708568c480438a)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/84739
Reviewed-by: Xisco FaulĂ­ <xiscofauli@libreoffice.org>
diff --git a/basic/source/runtime/runtime.cxx b/basic/source/runtime/runtime.cxx
index faf2b1d..9ab8d58 100644
--- a/basic/source/runtime/runtime.cxx
+++ b/basic/source/runtime/runtime.cxx
@@ -4310,94 +4310,83 @@
    SbxDimArray* pArray = dynamic_cast<SbxDimArray*>(xObj.get());
    if (pArray)
    {
        short nDims = pArray->GetDims();
        sal_Int32 nTotalSize = 0;
        const short nDims = pArray->GetDims();
        sal_Int32 nTotalSize = nDims > 0 ? 1 : 0;

        // must be a one-dimensional array
        sal_Int32 nLower, nUpper;
        for( sal_Int32 i = 0 ; i < nDims ; ++i )
        {
            pArray->GetDim32( i+1, nLower, nUpper );
            sal_Int32 nSize = nUpper - nLower + 1;
            if( i == 0 )
            const sal_Int32 nSize = nUpper - nLower + 1;
            nTotalSize *= nSize;
        }

        // First, fill those parts of the array that are preserved
        SbxDimArray* const pOldArray = static_cast<SbxDimArray*>(refRedimpArray.get());
        if (nTotalSize && pOldArray)
        {
            const short nDimsOld = pOldArray->GetDims();

            if (nDimsOld != nDims)
            {
                nTotalSize = nSize;
                StarBASIC::Error(ERRCODE_BASIC_OUT_OF_RANGE);
                nTotalSize = 0; // don't create objects on error
            }
            else
            {
                nTotalSize *= nSize;
                std::unique_ptr<sal_Int32[]> pLowerBounds(new sal_Int32[nDims]);
                std::unique_ptr<sal_Int32[]> pUpperBounds(new sal_Int32[nDims]);
                std::unique_ptr<sal_Int32[]> pActualIndices(new sal_Int32[nDims]);

                // Compare bounds
                for (short i = 1; i <= nDims; i++)
                {
                    sal_Int32 lBoundNew, uBoundNew;
                    sal_Int32 lBoundOld, uBoundOld;
                    pArray->GetDim32(i, lBoundNew, uBoundNew);
                    pOldArray->GetDim32(i, lBoundOld, uBoundOld);

                    lBoundNew = std::max(lBoundNew, lBoundOld);
                    uBoundNew = std::min(uBoundNew, uBoundOld);
                    short j = i - 1;
                    pActualIndices[j] = pLowerBounds[j] = lBoundNew;
                    pUpperBounds[j] = uBoundNew;
                }

                // Copy data from old array by going recursively through all dimensions
                // (It would be faster to work on the flat internal data array of an
                // SbyArray but this solution is clearer and easier)
                implCopyDimArray_DCREATE(pArray, pOldArray, nDims - 1,
                    0, pActualIndices.get(), pLowerBounds.get(), pUpperBounds.get());
            }
            refRedimpArray.clear();
        }
        // pOldArray points to destroyed object now, and only used as "ReDim Preserve" flag below

        // create objects and insert them into the array
        OUString aClass( pImg->GetString( static_cast<short>( nOp2 ) ) );
        for( sal_Int32 i = 0 ; i < nTotalSize ; ++i )
        {
            SbxObject *pClassObj = SbxBase::CreateObject( aClass );
            if( !pClassObj )
            if (!pOldArray || !pArray->SbxArray::GetRef32(i)) // For those left unset after preserve
            {
                Error( ERRCODE_BASIC_INVALID_OBJECT );
                break;
            }
            else
            {
                OUString aName( pImg->GetString( static_cast<short>( nOp1 ) ) );
                pClassObj->SetName( aName );
                // the object must be able to call the basic
                pClassObj->SetParent( &rBasic );
                pArray->SbxArray::Put32( pClassObj, i );
                SbxObject* pClassObj = SbxBase::CreateObject(aClass);
                if (!pClassObj)
                {
                    Error(ERRCODE_BASIC_INVALID_OBJECT);
                    break;
                }
                else
                {
                    OUString aName(pImg->GetString(static_cast<short>(nOp1)));
                    pClassObj->SetName(aName);
                    // the object must be able to call the basic
                    pClassObj->SetParent(&rBasic);
                    pArray->SbxArray::Put32(pClassObj, i);
                }
            }
        }
    }

    SbxDimArray* pOldArray = static_cast<SbxDimArray*>(refRedimpArray.get());
    if( pArray && pOldArray )
    {
        short nDimsNew = pArray->GetDims();
        short nDimsOld = pOldArray->GetDims();
        short nDims = nDimsNew;
        bool bRangeError = false;

        // Store dims to use them for copying later
        std::unique_ptr<sal_Int32[]> pLowerBounds(new sal_Int32[nDims]);
        std::unique_ptr<sal_Int32[]> pUpperBounds(new sal_Int32[nDims]);
        std::unique_ptr<sal_Int32[]> pActualIndices(new sal_Int32[nDims]);
        if( nDimsOld != nDimsNew )
        {
            bRangeError = true;
        }
        else
        {
            // Compare bounds
            for( short i = 1 ; i <= nDims ; i++ )
            {
                sal_Int32 lBoundNew, uBoundNew;
                sal_Int32 lBoundOld, uBoundOld;
                pArray->GetDim32( i, lBoundNew, uBoundNew );
                pOldArray->GetDim32( i, lBoundOld, uBoundOld );

                lBoundNew = std::max( lBoundNew, lBoundOld );
                uBoundNew = std::min( uBoundNew, uBoundOld );
                short j = i - 1;
                pActualIndices[j] = pLowerBounds[j] = lBoundNew;
                pUpperBounds[j] = uBoundNew;
            }
        }

        if( bRangeError )
        {
            StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE );
        }
        else
        {
            // Copy data from old array by going recursively through all dimensions
            // (It would be faster to work on the flat internal data array of an
            // SbyArray but this solution is clearer and easier)
            implCopyDimArray_DCREATE( pArray, pOldArray, nDims - 1,
                                      0, pActualIndices.get(), pLowerBounds.get(), pUpperBounds.get() );
        }
        refRedimpArray = nullptr;
    }
}

void SbiRuntime::StepTCREATE( sal_uInt32 nOp1, sal_uInt32 nOp2 )