/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * 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/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace com::sun::star; using namespace formula; using ::std::unique_ptr; #define ADDIN_MAXSTRLEN 256 thread_local std::unique_ptr ScInterpreter::pGlobalStack; thread_local bool ScInterpreter::bGlobalStackInUse = false; // document access functions void ScInterpreter::ReplaceCell( ScAddress& rPos ) { size_t ListSize = mrDoc.m_TableOpList.size(); for ( size_t i = 0; i < ListSize; ++i ) { ScInterpreterTableOpParams *const pTOp = mrDoc.m_TableOpList[ i ]; if ( rPos == pTOp->aOld1 ) { rPos = pTOp->aNew1; return ; } else if ( rPos == pTOp->aOld2 ) { rPos = pTOp->aNew2; return ; } } } bool ScInterpreter::IsTableOpInRange( const ScRange& rRange ) { if ( rRange.aStart == rRange.aEnd ) return false; // not considered to be a range in TableOp sense // we can't replace a single cell in a range size_t ListSize = mrDoc.m_TableOpList.size(); for ( size_t i = 0; i < ListSize; ++i ) { ScInterpreterTableOpParams *const pTOp = mrDoc.m_TableOpList[ i ]; if ( rRange.In( pTOp->aOld1 ) ) return true; if ( rRange.In( pTOp->aOld2 ) ) return true; } return false; } sal_uInt32 ScInterpreter::GetCellNumberFormat( const ScAddress& rPos, ScRefCellValue& rCell ) { sal_uInt32 nFormat; FormulaError nErr; if (rCell.isEmpty()) { nFormat = mrDoc.GetNumberFormat( mrContext, rPos ); nErr = FormulaError::NONE; } else { if (rCell.meType == CELLTYPE_FORMULA) nErr = rCell.mpFormula->GetErrCode(); else nErr = FormulaError::NONE; nFormat = mrDoc.GetNumberFormat( mrContext, rPos ); } SetError(nErr); return nFormat; } /// Only ValueCell, formula cells already store the result rounded. double ScInterpreter::GetValueCellValue( const ScAddress& rPos, double fOrig ) { if ( bCalcAsShown && fOrig != 0.0 ) { sal_uInt32 nFormat = mrDoc.GetNumberFormat( mrContext, rPos ); fOrig = mrDoc.RoundValueAsShown( fOrig, nFormat, &mrContext ); } return fOrig; } FormulaError ScInterpreter::GetCellErrCode( const ScRefCellValue& rCell ) { return rCell.meType == CELLTYPE_FORMULA ? rCell.mpFormula->GetErrCode() : FormulaError::NONE; } double ScInterpreter::ConvertStringToValue( const OUString& rStr ) { FormulaError nError = FormulaError::NONE; double fValue = ScGlobal::ConvertStringToValue( rStr, maCalcConfig, nError, mnStringNoValueError, pFormatter, nCurFmtType); if (nError != FormulaError::NONE) SetError(nError); return fValue; } double ScInterpreter::ConvertStringToValue( const OUString& rStr, FormulaError& rError, SvNumFormatType& rCurFmtType ) { return ScGlobal::ConvertStringToValue( rStr, maCalcConfig, rError, mnStringNoValueError, pFormatter, rCurFmtType); } double ScInterpreter::GetCellValue( const ScAddress& rPos, ScRefCellValue& rCell ) { FormulaError nErr = nGlobalError; nGlobalError = FormulaError::NONE; double nVal = GetCellValueOrZero(rPos, rCell); if ( nGlobalError == FormulaError::NONE || nGlobalError == FormulaError::CellNoValue ) nGlobalError = nErr; return nVal; } double ScInterpreter::GetCellValueOrZero( const ScAddress& rPos, ScRefCellValue& rCell ) { double fValue = 0.0; CellType eType = rCell.meType; switch (eType) { case CELLTYPE_FORMULA: { ScFormulaCell* pFCell = rCell.mpFormula; FormulaError nErr = pFCell->GetErrCode(); if( nErr == FormulaError::NONE ) { if (pFCell->IsValue()) { fValue = pFCell->GetValue(); mrDoc.GetNumberFormatInfo( mrContext, nCurFmtType, nCurFmtIndex, rPos ); } else { fValue = ConvertStringToValue(pFCell->GetString().getString()); } } else { fValue = 0.0; SetError(nErr); } } break; case CELLTYPE_VALUE: { fValue = rCell.mfValue; nCurFmtIndex = mrDoc.GetNumberFormat( mrContext, rPos ); nCurFmtType = mrContext.GetNumberFormatType( nCurFmtIndex ); if ( bCalcAsShown && fValue != 0.0 ) fValue = mrDoc.RoundValueAsShown( fValue, nCurFmtIndex, &mrContext ); } break; case CELLTYPE_STRING: case CELLTYPE_EDIT: { // SUM(A1:A2) differs from A1+A2. No good. But people insist on // it ... #i5658# OUString aStr = rCell.getString(&mrDoc); fValue = ConvertStringToValue( aStr ); } break; case CELLTYPE_NONE: fValue = 0.0; // empty or broadcaster cell break; } return fValue; } void ScInterpreter::GetCellString( svl::SharedString& rStr, ScRefCellValue& rCell ) { FormulaError nErr = FormulaError::NONE; switch (rCell.meType) { case CELLTYPE_STRING: case CELLTYPE_EDIT: rStr = mrStrPool.intern(rCell.getString(&mrDoc)); break; case CELLTYPE_FORMULA: { ScFormulaCell* pFCell = rCell.mpFormula; nErr = pFCell->GetErrCode(); if (pFCell->IsValue()) { rStr = GetStringFromDouble( pFCell->GetValue() ); } else rStr = pFCell->GetString(); } break; case CELLTYPE_VALUE: { rStr = GetStringFromDouble( rCell.mfValue ); } break; default: rStr = svl::SharedString::getEmptyString(); break; } SetError(nErr); } bool ScInterpreter::CreateDoubleArr(SCCOL nCol1, SCROW nRow1, SCTAB nTab1, SCCOL nCol2, SCROW nRow2, SCTAB nTab2, sal_uInt8* pCellArr) { // Old Add-Ins are hard limited to sal_uInt16 values. static_assert(MAXCOLCOUNT <= SAL_MAX_UINT16, "Add check for columns > SAL_MAX_UINT16!"); if (nRow1 > SAL_MAX_UINT16 || nRow2 > SAL_MAX_UINT16) return false; sal_uInt16 nCount = 0; sal_uInt16* p = reinterpret_cast(pCellArr); *p++ = static_cast(nCol1); *p++ = static_cast(nRow1); *p++ = static_cast(nTab1); *p++ = static_cast(nCol2); *p++ = static_cast(nRow2); *p++ = static_cast(nTab2); sal_uInt16* pCount = p; *p++ = 0; sal_uInt16 nPos = 14; SCTAB nTab = nTab1; ScAddress aAdr; while (nTab <= nTab2) { aAdr.SetTab( nTab ); SCROW nRow = nRow1; while (nRow <= nRow2) { aAdr.SetRow( nRow ); SCCOL nCol = nCol1; while (nCol <= nCol2) { aAdr.SetCol( nCol ); ScRefCellValue aCell(mrDoc, aAdr); if (!aCell.isEmpty()) { FormulaError nErr = FormulaError::NONE; double nVal = 0.0; bool bOk = true; switch (aCell.meType) { case CELLTYPE_VALUE : nVal = GetValueCellValue(aAdr, aCell.mfValue); break; case CELLTYPE_FORMULA : if (aCell.mpFormula->IsValue()) { nErr = aCell.mpFormula->GetErrCode(); nVal = aCell.mpFormula->GetValue(); } else bOk = false; break; default : bOk = false; break; } if (bOk) { if ((nPos + (4 * sizeof(sal_uInt16)) + sizeof(double)) > MAXARRSIZE) return false; *p++ = static_cast(nCol); *p++ = static_cast(nRow); *p++ = static_cast(nTab); *p++ = static_cast(nErr); memcpy( p, &nVal, sizeof(double)); nPos += 8 + sizeof(double); p = reinterpret_cast( pCellArr + nPos ); nCount++; } } nCol++; } nRow++; } nTab++; } *pCount = nCount; return true; } bool ScInterpreter::CreateStringArr(SCCOL nCol1, SCROW nRow1, SCTAB nTab1, SCCOL nCol2, SCROW nRow2, SCTAB nTab2, sal_uInt8* pCellArr) { // Old Add-Ins are hard limited to sal_uInt16 values. static_assert(MAXCOLCOUNT <= SAL_MAX_UINT16, "Add check for columns > SAL_MAX_UINT16!"); if (nRow1 > SAL_MAX_UINT16 || nRow2 > SAL_MAX_UINT16) return false; sal_uInt16 nCount = 0; sal_uInt16* p = reinterpret_cast(pCellArr); *p++ = static_cast(nCol1); *p++ = static_cast(nRow1); *p++ = static_cast(nTab1); *p++ = static_cast(nCol2); *p++ = static_cast(nRow2); *p++ = static_cast(nTab2); sal_uInt16* pCount = p; *p++ = 0; sal_uInt16 nPos = 14; SCTAB nTab = nTab1; while (nTab <= nTab2) { SCROW nRow = nRow1; while (nRow <= nRow2) { SCCOL nCol = nCol1; while (nCol <= nCol2) { ScRefCellValue aCell(mrDoc, ScAddress(nCol, nRow, nTab)); if (!aCell.isEmpty()) { OUString aStr; FormulaError nErr = FormulaError::NONE; bool bOk = true; switch (aCell.meType) { case CELLTYPE_STRING: case CELLTYPE_EDIT: aStr = aCell.getString(&mrDoc); break; case CELLTYPE_FORMULA: if (!aCell.mpFormula->IsValue()) { nErr = aCell.mpFormula->GetErrCode(); aStr = aCell.mpFormula->GetString().getString(); } else bOk = false; break; default : bOk = false; break; } if (bOk) { OString aTmp(OUStringToOString(aStr, osl_getThreadTextEncoding())); // Old Add-Ins are limited to sal_uInt16 string // lengths, and room for pad byte check. if ( aTmp.getLength() > SAL_MAX_UINT16 - 2 ) return false; // Append a 0-pad-byte if string length is odd // MUST be sal_uInt16 sal_uInt16 nStrLen = static_cast(aTmp.getLength()); sal_uInt16 nLen = ( nStrLen + 2 ) & ~1; if ((static_cast(nPos) + (5 * sizeof(sal_uInt16)) + nLen) > MAXARRSIZE) return false; *p++ = static_cast(nCol); *p++ = static_cast(nRow); *p++ = static_cast(nTab); *p++ = static_cast(nErr); *p++ = nLen; memcpy( p, aTmp.getStr(), nStrLen + 1); nPos += 10 + nStrLen + 1; sal_uInt8* q = pCellArr + nPos; if( (nStrLen & 1) == 0 ) { *q++ = 0; nPos++; } p = reinterpret_cast( pCellArr + nPos ); nCount++; } } nCol++; } nRow++; } nTab++; } *pCount = nCount; return true; } bool ScInterpreter::CreateCellArr(SCCOL nCol1, SCROW nRow1, SCTAB nTab1, SCCOL nCol2, SCROW nRow2, SCTAB nTab2, sal_uInt8* pCellArr) { // Old Add-Ins are hard limited to sal_uInt16 values. static_assert(MAXCOLCOUNT <= SAL_MAX_UINT16, "Add check for columns > SAL_MAX_UINT16!"); if (nRow1 > SAL_MAX_UINT16 || nRow2 > SAL_MAX_UINT16) return false; sal_uInt16 nCount = 0; sal_uInt16* p = reinterpret_cast(pCellArr); *p++ = static_cast(nCol1); *p++ = static_cast(nRow1); *p++ = static_cast(nTab1); *p++ = static_cast(nCol2); *p++ = static_cast(nRow2); *p++ = static_cast(nTab2); sal_uInt16* pCount = p; *p++ = 0; sal_uInt16 nPos = 14; SCTAB nTab = nTab1; ScAddress aAdr; while (nTab <= nTab2) { aAdr.SetTab( nTab ); SCROW nRow = nRow1; while (nRow <= nRow2) { aAdr.SetRow( nRow ); SCCOL nCol = nCol1; while (nCol <= nCol2) { aAdr.SetCol( nCol ); ScRefCellValue aCell(mrDoc, aAdr); if (!aCell.isEmpty()) { FormulaError nErr = FormulaError::NONE; sal_uInt16 nType = 0; // 0 = number; 1 = string double nVal = 0.0; OUString aStr; bool bOk = true; switch (aCell.meType) { case CELLTYPE_STRING : case CELLTYPE_EDIT : aStr = aCell.getString(&mrDoc); nType = 1; break; case CELLTYPE_VALUE : nVal = GetValueCellValue(aAdr, aCell.mfValue); break; case CELLTYPE_FORMULA : nErr = aCell.mpFormula->GetErrCode(); if (aCell.mpFormula->IsValue()) nVal = aCell.mpFormula->GetValue(); else aStr = aCell.mpFormula->GetString().getString(); break; default : bOk = false; break; } if (bOk) { if ((nPos + (5 * sizeof(sal_uInt16))) > MAXARRSIZE) return false; *p++ = static_cast(nCol); *p++ = static_cast(nRow); *p++ = static_cast(nTab); *p++ = static_cast(nErr); *p++ = nType; nPos += 10; if (nType == 0) { if ((nPos + sizeof(double)) > MAXARRSIZE) return false; memcpy( p, &nVal, sizeof(double)); nPos += sizeof(double); } else { OString aTmp(OUStringToOString(aStr, osl_getThreadTextEncoding())); // Old Add-Ins are limited to sal_uInt16 string // lengths, and room for pad byte check. if ( aTmp.getLength() > SAL_MAX_UINT16 - 2 ) return false; // Append a 0-pad-byte if string length is odd // MUST be sal_uInt16 sal_uInt16 nStrLen = static_cast(aTmp.getLength()); sal_uInt16 nLen = ( nStrLen + 2 ) & ~1; if ( (static_cast(nPos) + 2 + nLen) > MAXARRSIZE) return false; *p++ = nLen; memcpy( p, aTmp.getStr(), nStrLen + 1); nPos += 2 + nStrLen + 1; sal_uInt8* q = pCellArr + nPos; if( (nStrLen & 1) == 0 ) { *q++ = 0; nPos++; } } nCount++; p = reinterpret_cast( pCellArr + nPos ); } } nCol++; } nRow++; } nTab++; } *pCount = nCount; return true; } // Stack operations // Also releases a TempToken if appropriate. void ScInterpreter::PushWithoutError( const FormulaToken& r ) { if ( sp >= MAXSTACK ) SetError( FormulaError::StackOverflow ); else { r.IncRef(); if( sp >= maxsp ) maxsp = sp + 1; else pStack[ sp ]->DecRef(); pStack[ sp ] = &r; ++sp; } } void ScInterpreter::Push( const FormulaToken& r ) { if ( sp >= MAXSTACK ) SetError( FormulaError::StackOverflow ); else { if (nGlobalError != FormulaError::NONE) { if (r.GetType() == svError) PushWithoutError( r); else PushTempTokenWithoutError( new FormulaErrorToken( nGlobalError)); } else PushWithoutError( r); } } void ScInterpreter::PushTempToken( FormulaToken* p ) { if ( sp >= MAXSTACK ) { SetError( FormulaError::StackOverflow ); // p may be a dangling pointer hereafter! p->DeleteIfZeroRef(); } else { if (nGlobalError != FormulaError::NONE) { if (p->GetType() == svError) { p->SetError( nGlobalError); PushTempTokenWithoutError( p); } else { // p may be a dangling pointer hereafter! p->DeleteIfZeroRef(); PushTempTokenWithoutError( new FormulaErrorToken( nGlobalError)); } } else PushTempTokenWithoutError( p); } } void ScInterpreter::PushTempTokenWithoutError( const FormulaToken* p ) { p->IncRef(); if ( sp >= MAXSTACK ) { SetError( FormulaError::StackOverflow ); // p may be a dangling pointer hereafter! p->DecRef(); } else { if( sp >= maxsp ) maxsp = sp + 1; else pStack[ sp ]->DecRef(); pStack[ sp ] = p; ++sp; } } void ScInterpreter::PushTokenRef( const formula::FormulaConstTokenRef& x ) { if ( sp >= MAXSTACK ) { SetError( FormulaError::StackOverflow ); } else { if (nGlobalError != FormulaError::NONE) { if (x->GetType() == svError && x->GetError() == nGlobalError) PushTempTokenWithoutError( x.get()); else PushTempTokenWithoutError( new FormulaErrorToken( nGlobalError)); } else PushTempTokenWithoutError( x.get()); } } void ScInterpreter::PushCellResultToken( bool bDisplayEmptyAsString, const ScAddress & rAddress, SvNumFormatType * pRetTypeExpr, sal_uInt32 * pRetIndexExpr, bool bFinalResult ) { ScRefCellValue aCell(mrDoc, rAddress); if (aCell.hasEmptyValue()) { bool bInherited = (aCell.meType == CELLTYPE_FORMULA); if (pRetTypeExpr && pRetIndexExpr) mrDoc.GetNumberFormatInfo(mrContext, *pRetTypeExpr, *pRetIndexExpr, rAddress); PushTempToken( new ScEmptyCellToken( bInherited, bDisplayEmptyAsString)); return; } FormulaError nErr = FormulaError::NONE; if (aCell.meType == CELLTYPE_FORMULA) nErr = aCell.mpFormula->GetErrCode(); if (nErr != FormulaError::NONE) { PushError( nErr); if (pRetTypeExpr) *pRetTypeExpr = SvNumFormatType::UNDEFINED; if (pRetIndexExpr) *pRetIndexExpr = 0; } else if (aCell.hasString()) { svl::SharedString aRes; GetCellString( aRes, aCell); PushString( aRes); if (pRetTypeExpr) *pRetTypeExpr = SvNumFormatType::TEXT; if (pRetIndexExpr) *pRetIndexExpr = 0; } else { double fVal = GetCellValue(rAddress, aCell); if (bFinalResult) { TreatDoubleError( fVal); if (!IfErrorPushError()) PushTempTokenWithoutError( CreateFormulaDoubleToken( fVal)); } else { PushDouble( fVal); } if (pRetTypeExpr) *pRetTypeExpr = nCurFmtType; if (pRetIndexExpr) *pRetIndexExpr = nCurFmtIndex; } } // Simply throw away TOS. void ScInterpreter::Pop() { if( sp ) sp--; else SetError(FormulaError::UnknownStackVariable); } // Simply throw away TOS and set error code, used with ocIsError et al. void ScInterpreter::PopError() { if( sp ) { sp--; if (pStack[sp]->GetType() == svError) nGlobalError = pStack[sp]->GetError(); } else SetError(FormulaError::UnknownStackVariable); } FormulaConstTokenRef ScInterpreter::PopToken() { if (sp) { sp--; const FormulaToken* p = pStack[ sp ]; if (p->GetType() == svError) nGlobalError = p->GetError(); return p; } else SetError(FormulaError::UnknownStackVariable); return nullptr; } double ScInterpreter::PopDouble() { nCurFmtType = SvNumFormatType::NUMBER; nCurFmtIndex = 0; if( sp ) { --sp; const FormulaToken* p = pStack[ sp ]; switch (p->GetType()) { case svError: nGlobalError = p->GetError(); break; case svDouble: { SvNumFormatType nType = static_cast(p->GetDoubleType()); if (nType != SvNumFormatType::ALL && nType != SvNumFormatType::UNDEFINED) nCurFmtType = nType; return p->GetDouble(); } case svEmptyCell: case svMissing: return 0.0; default: SetError( FormulaError::IllegalArgument); } } else SetError( FormulaError::UnknownStackVariable); return 0.0; } svl::SharedString ScInterpreter::PopString() { nCurFmtType = SvNumFormatType::TEXT; nCurFmtIndex = 0; if( sp ) { --sp; const FormulaToken* p = pStack[ sp ]; switch (p->GetType()) { case svError: nGlobalError = p->GetError(); break; case svString: return p->GetString(); case svEmptyCell: case svMissing: return svl::SharedString::getEmptyString(); default: SetError( FormulaError::IllegalArgument); } } else SetError( FormulaError::UnknownStackVariable); return svl::SharedString::getEmptyString(); } void ScInterpreter::ValidateRef( const ScSingleRefData & rRef ) { SCCOL nCol; SCROW nRow; SCTAB nTab; SingleRefToVars( rRef, nCol, nRow, nTab); } void ScInterpreter::ValidateRef( const ScComplexRefData & rRef ) { ValidateRef( rRef.Ref1); ValidateRef( rRef.Ref2); } void ScInterpreter::ValidateRef( const ScRefList & rRefList ) { for (const auto& rRef : rRefList) { ValidateRef( rRef); } } void ScInterpreter::SingleRefToVars( const ScSingleRefData & rRef, SCCOL & rCol, SCROW & rRow, SCTAB & rTab ) { if ( rRef.IsColRel() ) rCol = aPos.Col() + rRef.Col(); else rCol = rRef.Col(); if ( rRef.IsRowRel() ) rRow = aPos.Row() + rRef.Row(); else rRow = rRef.Row(); if ( rRef.IsTabRel() ) rTab = aPos.Tab() + rRef.Tab(); else rTab = rRef.Tab(); if( !mrDoc.ValidCol( rCol) || rRef.IsColDeleted() ) { SetError( FormulaError::NoRef ); rCol = 0; } if( !mrDoc.ValidRow( rRow) || rRef.IsRowDeleted() ) { SetError( FormulaError::NoRef ); rRow = 0; } if( !ValidTab( rTab, mrDoc.GetTableCount() - 1) || rRef.IsTabDeleted() ) { SetError( FormulaError::NoRef ); rTab = 0; } } void ScInterpreter::PopSingleRef(SCCOL& rCol, SCROW &rRow, SCTAB& rTab) { ScAddress aAddr(rCol, rRow, rTab); PopSingleRef(aAddr); rCol = aAddr.Col(); rRow = aAddr.Row(); rTab = aAddr.Tab(); } void ScInterpreter::PopSingleRef( ScAddress& rAdr ) { if( sp ) { --sp; const FormulaToken* p = pStack[ sp ]; switch (p->GetType()) { case svError: nGlobalError = p->GetError(); break; case svSingleRef: { const ScSingleRefData* pRefData = p->GetSingleRef(); if (pRefData->IsDeleted()) { SetError( FormulaError::NoRef); break; } SCCOL nCol; SCROW nRow; SCTAB nTab; SingleRefToVars( *pRefData, nCol, nRow, nTab); rAdr.Set( nCol, nRow, nTab ); if (!mrDoc.m_TableOpList.empty()) ReplaceCell( rAdr ); } break; default: SetError( FormulaError::IllegalParameter); } } else SetError( FormulaError::UnknownStackVariable); } void ScInterpreter::DoubleRefToVars( const formula::FormulaToken* p, SCCOL& rCol1, SCROW &rRow1, SCTAB& rTab1, SCCOL& rCol2, SCROW &rRow2, SCTAB& rTab2 ) { const ScComplexRefData& rCRef = *p->GetDoubleRef(); SingleRefToVars( rCRef.Ref1, rCol1, rRow1, rTab1); SingleRefToVars( rCRef.Ref2, rCol2, rRow2, rTab2); if (rCol2 < rCol1) std::swap( rCol2, rCol1); if (rRow2 < rRow1) std::swap( rRow2, rRow1); if (rTab2 < rTab1) std::swap( rTab2, rTab1); if (!mrDoc.m_TableOpList.empty()) { ScRange aRange( rCol1, rRow1, rTab1, rCol2, rRow2, rTab2 ); if ( IsTableOpInRange( aRange ) ) SetError( FormulaError::IllegalParameter ); } } ScDBRangeBase* ScInterpreter::PopDBDoubleRef() { StackVar eType = GetStackType(); switch (eType) { case svUnknown: SetError(FormulaError::UnknownStackVariable); break; case svError: PopError(); break; case svDoubleRef: { SCCOL nCol1, nCol2; SCROW nRow1, nRow2; SCTAB nTab1, nTab2; PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); if (nGlobalError != FormulaError::NONE) break; return new ScDBInternalRange(&mrDoc, ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2)); } case svMatrix: case svExternalDoubleRef: { ScMatrixRef pMat; if (eType == svMatrix) pMat = PopMatrix(); else PopExternalDoubleRef(pMat); if (nGlobalError != FormulaError::NONE) break; return new ScDBExternalRange(&mrDoc, pMat); } default: SetError( FormulaError::IllegalParameter); } return nullptr; } void ScInterpreter::PopDoubleRef(SCCOL& rCol1, SCROW &rRow1, SCTAB& rTab1, SCCOL& rCol2, SCROW &rRow2, SCTAB& rTab2) { if( sp ) { --sp; const FormulaToken* p = pStack[ sp ]; switch (p->GetType()) { case svError: nGlobalError = p->GetError(); break; case svDoubleRef: DoubleRefToVars( p, rCol1, rRow1, rTab1, rCol2, rRow2, rTab2); break; default: SetError( FormulaError::IllegalParameter); } } else SetError( FormulaError::UnknownStackVariable); } void ScInterpreter::DoubleRefToRange( const ScComplexRefData & rCRef, ScRange & rRange, bool bDontCheckForTableOp ) { SCCOL nCol; SCROW nRow; SCTAB nTab; SingleRefToVars( rCRef.Ref1, nCol, nRow, nTab); rRange.aStart.Set( nCol, nRow, nTab ); SingleRefToVars( rCRef.Ref2, nCol, nRow, nTab); rRange.aEnd.Set( nCol, nRow, nTab ); rRange.PutInOrder(); if (!mrDoc.m_TableOpList.empty() && !bDontCheckForTableOp) { if ( IsTableOpInRange( rRange ) ) SetError( FormulaError::IllegalParameter ); } } void ScInterpreter::PopDoubleRef( ScRange & rRange, short & rParam, size_t & rRefInList ) { if (sp) { const formula::FormulaToken* pToken = pStack[ sp-1 ]; switch (pToken->GetType()) { case svError: nGlobalError = pToken->GetError(); break; case svDoubleRef: { --sp; const ScComplexRefData* pRefData = pToken->GetDoubleRef(); if (pRefData->IsDeleted()) { SetError( FormulaError::NoRef); break; } DoubleRefToRange( *pRefData, rRange); break; } case svRefList: { const ScRefList* pList = pToken->GetRefList(); if (rRefInList < pList->size()) { DoubleRefToRange( (*pList)[rRefInList], rRange); if (++rRefInList < pList->size()) ++rParam; else { --sp; rRefInList = 0; } } else { --sp; rRefInList = 0; SetError( FormulaError::IllegalParameter); } } break; default: SetError( FormulaError::IllegalParameter); } } else SetError( FormulaError::UnknownStackVariable); } void ScInterpreter::PopDoubleRef( ScRange& rRange, bool bDontCheckForTableOp ) { if( sp ) { --sp; const FormulaToken* p = pStack[ sp ]; switch (p->GetType()) { case svError: nGlobalError = p->GetError(); break; case svDoubleRef: DoubleRefToRange( *p->GetDoubleRef(), rRange, bDontCheckForTableOp); break; default: SetError( FormulaError::IllegalParameter); } } else SetError( FormulaError::UnknownStackVariable); } void ScInterpreter::PopExternalSingleRef(sal_uInt16& rFileId, OUString& rTabName, ScSingleRefData& rRef) { if (!sp) { SetError(FormulaError::UnknownStackVariable); return; } --sp; const FormulaToken* p = pStack[sp]; StackVar eType = p->GetType(); if (eType == svError) { nGlobalError = p->GetError(); return; } if (eType != svExternalSingleRef) { SetError( FormulaError::IllegalParameter); return; } rFileId = p->GetIndex(); rTabName = p->GetString().getString(); rRef = *p->GetSingleRef(); } void ScInterpreter::PopExternalSingleRef(ScExternalRefCache::TokenRef& rToken, ScExternalRefCache::CellFormat* pFmt) { sal_uInt16 nFileId; OUString aTabName; ScSingleRefData aData; PopExternalSingleRef(nFileId, aTabName, aData, rToken, pFmt); } void ScInterpreter::PopExternalSingleRef( sal_uInt16& rFileId, OUString& rTabName, ScSingleRefData& rRef, ScExternalRefCache::TokenRef& rToken, ScExternalRefCache::CellFormat* pFmt) { PopExternalSingleRef(rFileId, rTabName, rRef); if (nGlobalError != FormulaError::NONE) return; ScExternalRefManager* pRefMgr = mrDoc.GetExternalRefManager(); const OUString* pFile = pRefMgr->getExternalFileName(rFileId); if (!pFile) { SetError(FormulaError::NoName); return; } if (rRef.IsTabRel()) { OSL_FAIL("ScCompiler::GetToken: external single reference must have an absolute table reference!"); SetError(FormulaError::NoRef); return; } ScAddress aAddr = rRef.toAbs(mrDoc, aPos); ScExternalRefCache::CellFormat aFmt; ScExternalRefCache::TokenRef xNew = pRefMgr->getSingleRefToken( rFileId, rTabName, aAddr, &aPos, nullptr, &aFmt); if (!xNew) { SetError(FormulaError::NoRef); return; } if (xNew->GetType() == svError) SetError( xNew->GetError()); rToken = xNew; if (pFmt) *pFmt = aFmt; } void ScInterpreter::PopExternalDoubleRef(sal_uInt16& rFileId, OUString& rTabName, ScComplexRefData& rRef) { if (!sp) { SetError(FormulaError::UnknownStackVariable); return; } --sp; const FormulaToken* p = pStack[sp]; StackVar eType = p->GetType(); if (eType == svError) { nGlobalError = p->GetError(); return; } if (eType != svExternalDoubleRef) { SetError( FormulaError::IllegalParameter); return; } rFileId = p->GetIndex(); rTabName = p->GetString().getString(); rRef = *p->GetDoubleRef(); } void ScInterpreter::PopExternalDoubleRef(ScExternalRefCache::TokenArrayRef& rArray) { sal_uInt16 nFileId; OUString aTabName; ScComplexRefData aData; PopExternalDoubleRef(nFileId, aTabName, aData); if (nGlobalError != FormulaError::NONE) return; GetExternalDoubleRef(nFileId, aTabName, aData, rArray); if (nGlobalError != FormulaError::NONE) return; } void ScInterpreter::PopExternalDoubleRef(ScMatrixRef& rMat) { ScExternalRefCache::TokenArrayRef pArray; PopExternalDoubleRef(pArray); if (nGlobalError != FormulaError::NONE) return; // For now, we only support single range data for external // references, which means the array should only contain a // single matrix token. formula::FormulaToken* p = pArray->FirstToken(); if (!p || p->GetType() != svMatrix) SetError( FormulaError::IllegalParameter); else { rMat = p->GetMatrix(); if (!rMat) SetError( FormulaError::UnknownVariable); } } void ScInterpreter::GetExternalDoubleRef( sal_uInt16 nFileId, const OUString& rTabName, const ScComplexRefData& rData, ScExternalRefCache::TokenArrayRef& rArray) { ScExternalRefManager* pRefMgr = mrDoc.GetExternalRefManager(); const OUString* pFile = pRefMgr->getExternalFileName(nFileId); if (!pFile) { SetError(FormulaError::NoName); return; } if (rData.Ref1.IsTabRel() || rData.Ref2.IsTabRel()) { OSL_FAIL("ScCompiler::GetToken: external double reference must have an absolute table reference!"); SetError(FormulaError::NoRef); return; } ScComplexRefData aData(rData); ScRange aRange = aData.toAbs(mrDoc, aPos); if (!mrDoc.ValidColRow(aRange.aStart.Col(), aRange.aStart.Row()) || !mrDoc.ValidColRow(aRange.aEnd.Col(), aRange.aEnd.Row())) { SetError(FormulaError::NoRef); return; } ScExternalRefCache::TokenArrayRef pArray = pRefMgr->getDoubleRefTokens( nFileId, rTabName, aRange, &aPos); if (!pArray) { SetError(FormulaError::IllegalArgument); return; } formula::FormulaTokenArrayPlainIterator aIter(*pArray); formula::FormulaToken* pToken = aIter.First(); assert(pToken); if (pToken->GetType() == svError) { SetError( pToken->GetError()); return; } if (pToken->GetType() != svMatrix) { SetError(FormulaError::IllegalArgument); return; } if (aIter.Next()) { // Can't handle more than one matrix per parameter. SetError( FormulaError::IllegalArgument); return; } rArray = pArray; } bool ScInterpreter::PopDoubleRefOrSingleRef( ScAddress& rAdr ) { switch ( GetStackType() ) { case svDoubleRef : { ScRange aRange; PopDoubleRef( aRange, true ); return DoubleRefToPosSingleRef( aRange, rAdr ); } case svSingleRef : { PopSingleRef( rAdr ); return true; } default: PopError(); SetError( FormulaError::NoRef ); } return false; } void ScInterpreter::PopDoubleRefPushMatrix() { if ( GetStackType() == svDoubleRef ) { ScMatrixRef pMat = GetMatrix(); if ( pMat ) PushMatrix( pMat ); else PushIllegalParameter(); } else SetError( FormulaError::NoRef ); } void ScInterpreter::PopRefListPushMatrixOrRef() { if ( GetStackType() == svRefList ) { FormulaConstTokenRef xTok = pStack[sp-1]; const std::vector* pv = xTok->GetRefList(); if (pv) { const size_t nEntries = pv->size(); if (nEntries == 1) { --sp; PushTempTokenWithoutError( new ScDoubleRefToken( mrDoc.GetSheetLimits(), (*pv)[0] )); } else if (bMatrixFormula) { // Only single cells can be stuffed into a column vector. // XXX NOTE: Excel doesn't do this but returns #VALUE! instead. // Though there's no compelling reason not to... for (const auto & rRef : *pv) { if (rRef.Ref1 != rRef.Ref2) return; } ScMatrixRef xMat = GetNewMat( 1, nEntries, true); // init empty if (!xMat) return; for (size_t i=0; i < nEntries; ++i) { SCCOL nCol; SCROW nRow; SCTAB nTab; SingleRefToVars( (*pv)[i].Ref1, nCol, nRow, nTab); if (nGlobalError == FormulaError::NONE) { ScAddress aAdr( nCol, nRow, nTab); ScRefCellValue aCell(mrDoc, aAdr); if (aCell.hasError()) xMat->PutError( aCell.mpFormula->GetErrCode(), 0, i); else if (aCell.hasEmptyValue()) xMat->PutEmpty( 0, i); else if (aCell.hasString()) xMat->PutString( mrStrPool.intern( aCell.getString(&mrDoc)), 0, i); else xMat->PutDouble( aCell.getValue(), 0, i); } else { xMat->PutError( nGlobalError, 0, i); nGlobalError = FormulaError::NONE; } } --sp; PushMatrix( xMat); } } // else: keep token on stack, something will handle the error } else SetError( FormulaError::NoRef ); } void ScInterpreter::ConvertMatrixJumpConditionToMatrix() { StackVar eStackType = GetStackType(); if (eStackType == svUnknown) return; // can't do anything, some caller will catch that if (eStackType == svMatrix) return; // already matrix, nothing to do if (eStackType != svDoubleRef && GetStackType(2) != svJumpMatrix) return; // always convert svDoubleRef, others only in JumpMatrix context GetTokenMatrixMap(); // make sure it exists, create if not. ScMatrixRef pMat = GetMatrix(); if ( pMat ) PushMatrix( pMat ); else PushIllegalParameter(); } std::unique_ptr ScInterpreter::CreateTokenMatrixMap() { return std::make_unique(); } bool ScInterpreter::ConvertMatrixParameters() { sal_uInt16 nParams = pCur->GetParamCount(); OSL_ENSURE( nParams <= sp, "ConvertMatrixParameters: stack/param count mismatch"); SCSIZE nJumpCols = 0, nJumpRows = 0; for ( sal_uInt16 i=1; i <= nParams && i <= sp; ++i ) { const FormulaToken* p = pStack[ sp - i ]; if ( p->GetOpCode() != ocPush && p->GetOpCode() != ocMissing) { OSL_FAIL( "ConvertMatrixParameters: not a push"); } else { switch ( p->GetType() ) { case svDouble: case svString: case svSingleRef: case svExternalSingleRef: case svMissing: case svError: case svEmptyCell: // nothing to do break; case svMatrix: { if ( ScParameterClassification::GetParameterType( pCur, nParams - i) == formula::ParamClass::Value ) { // only if single value expected ScConstMatrixRef pMat = p->GetMatrix(); if ( !pMat ) SetError( FormulaError::UnknownVariable); else { SCSIZE nCols, nRows; pMat->GetDimensions( nCols, nRows); if ( nJumpCols < nCols ) nJumpCols = nCols; if ( nJumpRows < nRows ) nJumpRows = nRows; } } } break; case svDoubleRef: { formula::ParamClass eType = ScParameterClassification::GetParameterType( pCur, nParams - i); if ( eType != formula::ParamClass::Reference && eType != formula::ParamClass::ReferenceOrRefArray && eType != formula::ParamClass::ReferenceOrForceArray && // For scalar Value: convert to Array/JumpMatrix // only if in array formula context, else (function // has ForceArray or ReferenceOrForceArray // parameter *somewhere else*) pick a normal // position dependent implicit intersection later. (eType != formula::ParamClass::Value || IsInArrayContext())) { SCCOL nCol1, nCol2; SCROW nRow1, nRow2; SCTAB nTab1, nTab2; DoubleRefToVars( p, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); // Make sure the map exists, created if not. GetTokenMatrixMap(); ScMatrixRef pMat = CreateMatrixFromDoubleRef( p, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); if (pMat) { if ( eType == formula::ParamClass::Value ) { // only if single value expected if ( nJumpCols < o3tl::make_unsigned(nCol2 - nCol1 + 1) ) nJumpCols = static_cast(nCol2 - nCol1 + 1); if ( nJumpRows < o3tl::make_unsigned(nRow2 - nRow1 + 1) ) nJumpRows = static_cast(nRow2 - nRow1 + 1); } formula::FormulaToken* pNew = new ScMatrixToken( pMat); pNew->IncRef(); pStack[ sp - i ] = pNew; p->DecRef(); // p may be dead now! } } } break; case svExternalDoubleRef: { formula::ParamClass eType = ScParameterClassification::GetParameterType( pCur, nParams - i); if (eType == formula::ParamClass::Value || eType == formula::ParamClass::Array) { sal_uInt16 nFileId = p->GetIndex(); OUString aTabName = p->GetString().getString(); const ScComplexRefData& rRef = *p->GetDoubleRef(); ScExternalRefCache::TokenArrayRef pArray; GetExternalDoubleRef(nFileId, aTabName, rRef, pArray); if (nGlobalError != FormulaError::NONE || !pArray) break; formula::FormulaToken* pTemp = pArray->FirstToken(); if (!pTemp) break; ScMatrixRef pMat = pTemp->GetMatrix(); if (pMat) { if (eType == formula::ParamClass::Value) { // only if single value expected SCSIZE nC, nR; pMat->GetDimensions( nC, nR); if (nJumpCols < nC) nJumpCols = nC; if (nJumpRows < nR) nJumpRows = nR; } formula::FormulaToken* pNew = new ScMatrixToken( pMat); pNew->IncRef(); pStack[ sp - i ] = pNew; p->DecRef(); // p may be dead now! } } } break; case svRefList: { formula::ParamClass eType = ScParameterClassification::GetParameterType( pCur, nParams - i); if ( eType != formula::ParamClass::Reference && eType != formula::ParamClass::ReferenceOrRefArray && eType != formula::ParamClass::ReferenceOrForceArray && eType != formula::ParamClass::ForceArray) { // can't convert to matrix SetError( FormulaError::NoValue); } // else: the consuming function has to decide if and how to // handle a reference list argument in array context. } break; default: OSL_FAIL( "ConvertMatrixParameters: unknown parameter type"); } } } if( nJumpCols && nJumpRows ) { short nPC = aCode.GetPC(); short nStart = nPC - 1; // restart on current code (-1) short nNext = nPC; // next instruction after subroutine short nStop = nPC + 1; // stop subroutine before reaching that FormulaConstTokenRef xNew; ScTokenMatrixMap::const_iterator aMapIter; if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find( pCur)) != pTokenMatrixMap->end())) xNew = (*aMapIter).second; else { auto pJumpMat = std::make_shared( pCur->GetOpCode(), nJumpCols, nJumpRows); pJumpMat->SetAllJumps( 1.0, nStart, nNext, nStop); // pop parameters and store in ScJumpMatrix, push in JumpMatrix() ScTokenVec aParams(nParams); for ( sal_uInt16 i=1; i <= nParams && sp > 0; ++i ) { const FormulaToken* p = pStack[ --sp ]; p->IncRef(); // store in reverse order such that a push may simply iterate aParams[ nParams - i ] = p; } pJumpMat->SetJumpParameters( std::move(aParams) ); xNew = new ScJumpMatrixToken( std::move(pJumpMat) ); GetTokenMatrixMap().emplace(pCur, xNew); } PushTempTokenWithoutError( xNew.get()); // set continuation point of path for main code line aCode.Jump( nNext, nNext); return true; } return false; } ScMatrixRef ScInterpreter::PopMatrix() { if( sp ) { --sp; const FormulaToken* p = pStack[ sp ]; switch (p->GetType()) { case svError: nGlobalError = p->GetError(); break; case svMatrix: { // ScMatrix itself maintains an im/mutable flag that should // be obeyed where necessary... so we can return ScMatrixRef // here instead of ScConstMatrixRef. ScMatrix* pMat = const_cast(p)->GetMatrix(); if ( pMat ) pMat->SetErrorInterpreter( this); else SetError( FormulaError::UnknownVariable); return pMat; } default: SetError( FormulaError::IllegalParameter); } } else SetError( FormulaError::UnknownStackVariable); return nullptr; } sc::RangeMatrix ScInterpreter::PopRangeMatrix() { sc::RangeMatrix aRet; if (sp) { switch (pStack[sp-1]->GetType()) { case svMatrix: { --sp; const FormulaToken* p = pStack[sp]; aRet.mpMat = const_cast(p)->GetMatrix(); if (aRet.mpMat) { aRet.mpMat->SetErrorInterpreter(this); if (p->GetByte() == MATRIX_TOKEN_HAS_RANGE) { const ScComplexRefData& rRef = *p->GetDoubleRef(); if (!rRef.Ref1.IsColRel() && !rRef.Ref1.IsRowRel() && !rRef.Ref2.IsColRel() && !rRef.Ref2.IsRowRel()) { aRet.mnCol1 = rRef.Ref1.Col(); aRet.mnRow1 = rRef.Ref1.Row(); aRet.mnTab1 = rRef.Ref1.Tab(); aRet.mnCol2 = rRef.Ref2.Col(); aRet.mnRow2 = rRef.Ref2.Row(); aRet.mnTab2 = rRef.Ref2.Tab(); } } } else SetError( FormulaError::UnknownVariable); } break; default: aRet.mpMat = PopMatrix(); } } return aRet; } void ScInterpreter::QueryMatrixType(const ScMatrixRef& xMat, SvNumFormatType& rRetTypeExpr, sal_uInt32& rRetIndexExpr) { if (xMat) { SCSIZE nCols, nRows; xMat->GetDimensions(nCols, nRows); ScMatrixValue nMatVal = xMat->Get(0, 0); ScMatValType nMatValType = nMatVal.nType; if (ScMatrix::IsNonValueType( nMatValType)) { if ( xMat->IsEmptyPath( 0, 0)) { // result of empty FALSE jump path FormulaTokenRef xRes = CreateFormulaDoubleToken( 0.0); PushTempToken( new ScMatrixFormulaCellToken(nCols, nRows, xMat, xRes.get())); rRetTypeExpr = SvNumFormatType::LOGICAL; } else if ( xMat->IsEmptyResult( 0, 0)) { // empty formula result FormulaTokenRef xRes = new ScEmptyCellToken( true, true); // inherited, display empty PushTempToken( new ScMatrixFormulaCellToken(nCols, nRows, xMat, xRes.get())); } else if ( xMat->IsEmpty( 0, 0)) { // empty or empty cell FormulaTokenRef xRes = new ScEmptyCellToken( false, true); // not inherited, display empty PushTempToken( new ScMatrixFormulaCellToken(nCols, nRows, xMat, xRes.get())); } else { FormulaTokenRef xRes = new FormulaStringToken( nMatVal.GetString() ); PushTempToken( new ScMatrixFormulaCellToken(nCols, nRows, xMat, xRes.get())); rRetTypeExpr = SvNumFormatType::TEXT; } } else { FormulaError nErr = GetDoubleErrorValue( nMatVal.fVal); FormulaTokenRef xRes; if (nErr != FormulaError::NONE) xRes = new FormulaErrorToken( nErr); else xRes = CreateFormulaDoubleToken( nMatVal.fVal); PushTempToken( new ScMatrixFormulaCellToken(nCols, nRows, xMat, xRes.get())); if ( rRetTypeExpr != SvNumFormatType::LOGICAL ) rRetTypeExpr = SvNumFormatType::NUMBER; } rRetIndexExpr = 0; xMat->SetErrorInterpreter( nullptr); } else SetError( FormulaError::UnknownStackVariable); } formula::FormulaToken* ScInterpreter::CreateFormulaDoubleToken( double fVal, SvNumFormatType nFmt ) { assert( mrContext.maTokens.size() == TOKEN_CACHE_SIZE ); // Find a spare token for ( auto p : mrContext.maTokens ) { if (p && p->GetRef() == 1) { p->GetDoubleAsReference() = fVal; p->SetDoubleType( static_cast(nFmt) ); return p; } } // Allocate a new token auto p = new FormulaTypedDoubleToken( fVal, static_cast(nFmt) ); if ( mrContext.maTokens[mrContext.mnTokenCachePos] ) mrContext.maTokens[mrContext.mnTokenCachePos]->DecRef(); mrContext.maTokens[mrContext.mnTokenCachePos] = p; p->IncRef(); mrContext.mnTokenCachePos = (mrContext.mnTokenCachePos + 1) % TOKEN_CACHE_SIZE; return p; } formula::FormulaToken* ScInterpreter::CreateDoubleOrTypedToken( double fVal ) { // NumberFormat::NUMBER is the default untyped double. if (nFuncFmtType != SvNumFormatType::ALL && nFuncFmtType != SvNumFormatType::NUMBER && nFuncFmtType != SvNumFormatType::UNDEFINED) return CreateFormulaDoubleToken( fVal, nFuncFmtType); else return CreateFormulaDoubleToken( fVal); } void ScInterpreter::PushDouble(double nVal) { TreatDoubleError( nVal ); if (!IfErrorPushError()) PushTempTokenWithoutError( CreateDoubleOrTypedToken( nVal)); } void ScInterpreter::PushInt(int nVal) { if (!IfErrorPushError()) PushTempTokenWithoutError( CreateDoubleOrTypedToken( nVal)); } void ScInterpreter::PushStringBuffer( const sal_Unicode* pString ) { if ( pString ) { svl::SharedString aSS = mrDoc.GetSharedStringPool().intern(OUString(pString)); PushString(aSS); } else PushString(svl::SharedString::getEmptyString()); } void ScInterpreter::PushString( const OUString& rStr ) { PushString(mrDoc.GetSharedStringPool().intern(rStr)); } void ScInterpreter::PushString( const svl::SharedString& rString ) { if (!IfErrorPushError()) PushTempTokenWithoutError( new FormulaStringToken( rString ) ); } void ScInterpreter::PushSingleRef(SCCOL nCol, SCROW nRow, SCTAB nTab) { if (!IfErrorPushError()) { ScSingleRefData aRef; aRef.InitAddress(ScAddress(nCol,nRow,nTab)); PushTempTokenWithoutError( new ScSingleRefToken( mrDoc.GetSheetLimits(), aRef ) ); } } void ScInterpreter::PushDoubleRef(SCCOL nCol1, SCROW nRow1, SCTAB nTab1, SCCOL nCol2, SCROW nRow2, SCTAB nTab2) { if (!IfErrorPushError()) { ScComplexRefData aRef; aRef.InitRange(ScRange(nCol1,nRow1,nTab1,nCol2,nRow2,nTab2)); PushTempTokenWithoutError( new ScDoubleRefToken( mrDoc.GetSheetLimits(), aRef ) ); } } void ScInterpreter::PushExternalSingleRef( sal_uInt16 nFileId, const OUString& rTabName, SCCOL nCol, SCROW nRow, SCTAB nTab) { if (!IfErrorPushError()) { ScSingleRefData aRef; aRef.InitAddress(ScAddress(nCol,nRow,nTab)); PushTempTokenWithoutError( new ScExternalSingleRefToken(nFileId, mrDoc.GetSharedStringPool().intern( rTabName), aRef)) ; } } void ScInterpreter::PushExternalDoubleRef( sal_uInt16 nFileId, const OUString& rTabName, SCCOL nCol1, SCROW nRow1, SCTAB nTab1, SCCOL nCol2, SCROW nRow2, SCTAB nTab2) { if (!IfErrorPushError()) { ScComplexRefData aRef; aRef.InitRange(ScRange(nCol1,nRow1,nTab1,nCol2,nRow2,nTab2)); PushTempTokenWithoutError( new ScExternalDoubleRefToken(nFileId, mrDoc.GetSharedStringPool().intern( rTabName), aRef) ); } } void ScInterpreter::PushSingleRef( const ScRefAddress& rRef ) { if (!IfErrorPushError()) { ScSingleRefData aRef; aRef.InitFromRefAddress( mrDoc, rRef, aPos); PushTempTokenWithoutError( new ScSingleRefToken( mrDoc.GetSheetLimits(), aRef ) ); } } void ScInterpreter::PushDoubleRef( const ScRefAddress& rRef1, const ScRefAddress& rRef2 ) { if (!IfErrorPushError()) { ScComplexRefData aRef; aRef.InitFromRefAddresses( mrDoc, rRef1, rRef2, aPos); PushTempTokenWithoutError( new ScDoubleRefToken( mrDoc.GetSheetLimits(), aRef ) ); } } void ScInterpreter::PushMatrix( const sc::RangeMatrix& rMat ) { if (!rMat.isRangeValid()) { // Just push the matrix part only. PushMatrix(rMat.mpMat); return; } rMat.mpMat->SetErrorInterpreter(nullptr); nGlobalError = FormulaError::NONE; PushTempTokenWithoutError(new ScMatrixRangeToken(rMat)); } void ScInterpreter::PushMatrix(const ScMatrixRef& pMat) { pMat->SetErrorInterpreter( nullptr); // No if (!IfErrorPushError()) because ScMatrix stores errors itself, // but with notifying ScInterpreter via nGlobalError, substituting it would // mean to inherit the error on all array elements in all following // operations. nGlobalError = FormulaError::NONE; PushTempTokenWithoutError( new ScMatrixToken( pMat ) ); } void ScInterpreter::PushError( FormulaError nError ) { SetError( nError ); // only sets error if not already set PushTempTokenWithoutError( new FormulaErrorToken( nGlobalError)); } void ScInterpreter::PushParameterExpected() { PushError( FormulaError::ParameterExpected); } void ScInterpreter::PushIllegalParameter() { PushError( FormulaError::IllegalParameter); } void ScInterpreter::PushIllegalArgument() { PushError( FormulaError::IllegalArgument); } void ScInterpreter::PushNA() { PushError( FormulaError::NotAvailable); } void ScInterpreter::PushNoValue() { PushError( FormulaError::NoValue); } bool ScInterpreter::IsMissing() const { return sp && pStack[sp - 1]->GetType() == svMissing; } StackVar ScInterpreter::GetRawStackType() { StackVar eRes; if( sp ) { eRes = pStack[sp - 1]->GetType(); } else { SetError(FormulaError::UnknownStackVariable); eRes = svUnknown; } return eRes; } StackVar ScInterpreter::GetStackType() { StackVar eRes; if( sp ) { eRes = pStack[sp - 1]->GetType(); if( eRes == svMissing || eRes == svEmptyCell ) eRes = svDouble; // default! } else { SetError(FormulaError::UnknownStackVariable); eRes = svUnknown; } return eRes; } StackVar ScInterpreter::GetStackType( sal_uInt8 nParam ) { StackVar eRes; if( sp > nParam-1 ) { eRes = pStack[sp - nParam]->GetType(); if( eRes == svMissing || eRes == svEmptyCell ) eRes = svDouble; // default! } else eRes = svUnknown; return eRes; } void ScInterpreter::ReverseStack( sal_uInt8 nParamCount ) { //reverse order of parameter stack assert( sp >= nParamCount && " less stack elements than parameters"); sal_uInt16 nStackParams = std::min( sp, nParamCount); std::reverse( pStack+(sp-nStackParams), pStack+sp ); } bool ScInterpreter::DoubleRefToPosSingleRef( const ScRange& rRange, ScAddress& rAdr ) { // Check for a singleton first - no implicit intersection for them. if( rRange.aStart == rRange.aEnd ) { rAdr = rRange.aStart; return true; } bool bOk = false; if ( pJumpMatrix ) { bOk = rRange.aStart.Tab() == rRange.aEnd.Tab(); if ( !bOk ) SetError( FormulaError::IllegalArgument); else { SCSIZE nC, nR; pJumpMatrix->GetPos( nC, nR); rAdr.SetCol( sal::static_int_cast( rRange.aStart.Col() + nC ) ); rAdr.SetRow( sal::static_int_cast( rRange.aStart.Row() + nR ) ); rAdr.SetTab( rRange.aStart.Tab()); bOk = rRange.aStart.Col() <= rAdr.Col() && rAdr.Col() <= rRange.aEnd.Col() && rRange.aStart.Row() <= rAdr.Row() && rAdr.Row() <= rRange.aEnd.Row(); if ( !bOk ) SetError( FormulaError::NoValue); } return bOk; } bOk = ScCompiler::DoubleRefToPosSingleRefScalarCase(rRange, rAdr, aPos); if ( !bOk ) SetError( FormulaError::NoValue ); return bOk; } double ScInterpreter::GetDoubleFromMatrix(const ScMatrixRef& pMat) { if (!pMat) return 0.0; if ( !pJumpMatrix ) { double fVal = pMat->GetDoubleWithStringConversion( 0, 0); FormulaError nErr = GetDoubleErrorValue( fVal); if (nErr != FormulaError::NONE) { // Do not propagate the coded double error, but set nGlobalError in // case the matrix did not have an error interpreter set. SetError( nErr); fVal = 0.0; } return fVal; } SCSIZE nCols, nRows, nC, nR; pMat->GetDimensions( nCols, nRows); pJumpMatrix->GetPos( nC, nR); // Use vector replication for single row/column arrays. if ( (nC < nCols || nCols == 1) && (nR < nRows || nRows == 1) ) { double fVal = pMat->GetDoubleWithStringConversion( nC, nR); FormulaError nErr = GetDoubleErrorValue( fVal); if (nErr != FormulaError::NONE) { // Do not propagate the coded double error, but set nGlobalError in // case the matrix did not have an error interpreter set. SetError( nErr); fVal = 0.0; } return fVal; } SetError( FormulaError::NoValue); return 0.0; } double ScInterpreter::GetDouble() { double nVal(0.0); switch( GetRawStackType() ) { case svDouble: nVal = PopDouble(); break; case svString: nVal = ConvertStringToValue( PopString().getString()); break; case svSingleRef: { ScAddress aAdr; PopSingleRef( aAdr ); ScRefCellValue aCell(mrDoc, aAdr); nVal = GetCellValue(aAdr, aCell); } break; case svDoubleRef: { // generate position dependent SingleRef ScRange aRange; PopDoubleRef( aRange ); ScAddress aAdr; if ( nGlobalError == FormulaError::NONE && DoubleRefToPosSingleRef( aRange, aAdr ) ) { ScRefCellValue aCell(mrDoc, aAdr); nVal = GetCellValue(aAdr, aCell); } else nVal = 0.0; } break; case svExternalSingleRef: { ScExternalRefCache::TokenRef pToken; PopExternalSingleRef(pToken); if (nGlobalError == FormulaError::NONE) { if (pToken->GetType() == svDouble || pToken->GetType() == svEmptyCell) nVal = pToken->GetDouble(); else nVal = ConvertStringToValue( pToken->GetString().getString()); } } break; case svExternalDoubleRef: { ScMatrixRef pMat; PopExternalDoubleRef(pMat); if (nGlobalError != FormulaError::NONE) break; nVal = GetDoubleFromMatrix(pMat); } break; case svMatrix: { ScMatrixRef pMat = PopMatrix(); nVal = GetDoubleFromMatrix(pMat); } break; case svError: PopError(); nVal = 0.0; break; case svEmptyCell: case svMissing: Pop(); nVal = 0.0; break; default: PopError(); SetError( FormulaError::IllegalParameter); nVal = 0.0; } if ( nFuncFmtType == nCurFmtType ) nFuncFmtIndex = nCurFmtIndex; return nVal; } double ScInterpreter::GetDoubleWithDefault(double nDefault) { bool bMissing = IsMissing(); double nResultVal = GetDouble(); if ( bMissing ) nResultVal = nDefault; return nResultVal; } sal_Int32 ScInterpreter::double_to_int32(double fVal) { if (!std::isfinite(fVal)) { SetError( GetDoubleErrorValue( fVal)); return SAL_MAX_INT32; } if (fVal > 0.0) { fVal = rtl::math::approxFloor( fVal); if (fVal > SAL_MAX_INT32) { SetError( FormulaError::IllegalArgument); return SAL_MAX_INT32; } } else if (fVal < 0.0) { fVal = rtl::math::approxCeil( fVal); if (fVal < SAL_MIN_INT32) { SetError( FormulaError::IllegalArgument); return SAL_MAX_INT32; } } return static_cast(fVal); } sal_Int32 ScInterpreter::GetInt32() { return double_to_int32(GetDouble()); } sal_Int32 ScInterpreter::GetInt32WithDefault( sal_Int32 nDefault ) { bool bMissing = IsMissing(); double fVal = GetDouble(); if ( bMissing ) return nDefault; return double_to_int32(fVal); } sal_Int16 ScInterpreter::GetInt16() { double fVal = GetDouble(); if (!std::isfinite(fVal)) { SetError( GetDoubleErrorValue( fVal)); return SAL_MAX_INT16; } if (fVal > 0.0) { fVal = rtl::math::approxFloor( fVal); if (fVal > SAL_MAX_INT16) { SetError( FormulaError::IllegalArgument); return SAL_MAX_INT16; } } else if (fVal < 0.0) { fVal = rtl::math::approxCeil( fVal); if (fVal < SAL_MIN_INT16) { SetError( FormulaError::IllegalArgument); return SAL_MAX_INT16; } } return static_cast(fVal); } sal_uInt32 ScInterpreter::GetUInt32() { double fVal = rtl::math::approxFloor( GetDouble()); if (!std::isfinite(fVal)) { SetError( GetDoubleErrorValue( fVal)); return SAL_MAX_UINT32; } if (fVal < 0.0 || fVal > SAL_MAX_UINT32) { SetError( FormulaError::IllegalArgument); return SAL_MAX_UINT32; } return static_cast(fVal); } bool ScInterpreter::GetDoubleOrString( double& rDouble, svl::SharedString& rString ) { bool bDouble = true; switch( GetRawStackType() ) { case svDouble: rDouble = PopDouble(); break; case svString: rString = PopString(); bDouble = false; break; case svDoubleRef : case svSingleRef : { ScAddress aAdr; if (!PopDoubleRefOrSingleRef( aAdr)) { rDouble = 0.0; return true; // caller needs to check nGlobalError } ScRefCellValue aCell( mrDoc, aAdr); if (aCell.hasNumeric()) { rDouble = GetCellValue( aAdr, aCell); } else { GetCellString( rString, aCell); bDouble = false; } } break; case svExternalSingleRef: case svExternalDoubleRef: case svMatrix: { ScMatValType nType = GetDoubleOrStringFromMatrix( rDouble, rString); bDouble = ScMatrix::IsValueType( nType); } break; case svError: PopError(); rDouble = 0.0; break; case svEmptyCell: case svMissing: Pop(); rDouble = 0.0; break; default: PopError(); SetError( FormulaError::IllegalParameter); rDouble = 0.0; } if ( nFuncFmtType == nCurFmtType ) nFuncFmtIndex = nCurFmtIndex; return bDouble; } svl::SharedString ScInterpreter::GetString() { switch (GetRawStackType()) { case svError: PopError(); return svl::SharedString::getEmptyString(); case svMissing: case svEmptyCell: Pop(); return svl::SharedString::getEmptyString(); case svDouble: { return GetStringFromDouble( PopDouble() ); } case svString: return PopString(); case svSingleRef: { ScAddress aAdr; PopSingleRef( aAdr ); if (nGlobalError == FormulaError::NONE) { ScRefCellValue aCell(mrDoc, aAdr); svl::SharedString aSS; GetCellString(aSS, aCell); return aSS; } else return svl::SharedString::getEmptyString(); } case svDoubleRef: { // generate position dependent SingleRef ScRange aRange; PopDoubleRef( aRange ); ScAddress aAdr; if ( nGlobalError == FormulaError::NONE && DoubleRefToPosSingleRef( aRange, aAdr ) ) { ScRefCellValue aCell(mrDoc, aAdr); svl::SharedString aSS; GetCellString(aSS, aCell); return aSS; } else return svl::SharedString::getEmptyString(); } case svExternalSingleRef: { ScExternalRefCache::TokenRef pToken; PopExternalSingleRef(pToken); if (nGlobalError != FormulaError::NONE) return svl::SharedString::getEmptyString(); if (pToken->GetType() == svDouble) { return GetStringFromDouble( pToken->GetDouble() ); } else // svString or svEmpty return pToken->GetString(); } case svExternalDoubleRef: { ScMatrixRef pMat; PopExternalDoubleRef(pMat); return GetStringFromMatrix(pMat); } case svMatrix: { ScMatrixRef pMat = PopMatrix(); return GetStringFromMatrix(pMat); } break; default: PopError(); SetError( FormulaError::IllegalArgument); } return svl::SharedString::getEmptyString(); } svl::SharedString ScInterpreter::GetStringFromMatrix(const ScMatrixRef& pMat) { if ( !pMat ) ; // nothing else if ( !pJumpMatrix ) { return pMat->GetString( *pFormatter, 0, 0); } else { SCSIZE nCols, nRows, nC, nR; pMat->GetDimensions( nCols, nRows); pJumpMatrix->GetPos( nC, nR); // Use vector replication for single row/column arrays. if ( (nC < nCols || nCols == 1) && (nR < nRows || nRows == 1) ) return pMat->GetString( *pFormatter, nC, nR); SetError( FormulaError::NoValue); } return svl::SharedString::getEmptyString(); } ScMatValType ScInterpreter::GetDoubleOrStringFromMatrix( double& rDouble, svl::SharedString& rString ) { rDouble = 0.0; rString = svl::SharedString::getEmptyString(); ScMatValType nMatValType = ScMatValType::Empty; ScMatrixRef pMat; StackVar eType = GetStackType(); if (eType == svExternalDoubleRef || eType == svExternalSingleRef || eType == svMatrix) { pMat = GetMatrix(); } else { PopError(); SetError( FormulaError::IllegalParameter); return nMatValType; } ScMatrixValue nMatVal; if (!pMat) { // nothing } else if (!pJumpMatrix) { nMatVal = pMat->Get(0, 0); nMatValType = nMatVal.nType; } else { SCSIZE nCols, nRows, nC, nR; pMat->GetDimensions( nCols, nRows); pJumpMatrix->GetPos( nC, nR); // Use vector replication for single row/column arrays. if ( (nC < nCols || nCols == 1) && (nR < nRows || nRows == 1) ) { nMatVal = pMat->Get( nC, nR); nMatValType = nMatVal.nType; } else SetError( FormulaError::NoValue); } if (ScMatrix::IsValueType( nMatValType)) { rDouble = nMatVal.fVal; FormulaError nError = nMatVal.GetError(); if (nError != FormulaError::NONE) SetError( nError); } else { rString = nMatVal.GetString(); } return nMatValType; } svl::SharedString ScInterpreter::GetStringFromDouble( double fVal ) { sal_uLong nIndex = pFormatter->GetStandardFormat( SvNumFormatType::NUMBER, ScGlobal::eLnge); OUString aStr; pFormatter->GetInputLineString(fVal, nIndex, aStr); return mrStrPool.intern(aStr); } void ScInterpreter::ScDBGet() { bool bMissingField = false; unique_ptr pQueryParam( GetDBParams(bMissingField) ); if (!pQueryParam) { // Failed to create query param. PushIllegalParameter(); return; } pQueryParam->mbSkipString = false; ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam)); ScDBQueryDataIterator::Value aValue; if (!aValIter.GetFirst(aValue) || aValue.mnError != FormulaError::NONE) { // No match found. PushNoValue(); return; } ScDBQueryDataIterator::Value aValNext; if (aValIter.GetNext(aValNext) && aValNext.mnError == FormulaError::NONE) { // There should be only one unique match. PushIllegalArgument(); return; } if (aValue.mbIsNumber) PushDouble(aValue.mfValue); else PushString(aValue.maString); } void ScInterpreter::ScExternal() { sal_uInt8 nParamCount = GetByte(); OUString aUnoName; OUString aFuncName( ScGlobal::getCharClassPtr()->uppercase( pCur->GetExternal() ) ); LegacyFuncData* pLegacyFuncData = ScGlobal::GetLegacyFuncCollection()->findByName(aFuncName); if (pLegacyFuncData) { // Old binary non-UNO add-in function. // NOTE: parameter count is 1-based with the 0th "parameter" being the // return value, included in pLegacyFuncDatat->GetParamCount() if (nParamCount < MAXFUNCPARAM && nParamCount == pLegacyFuncData->GetParamCount() - 1) { ParamType eParamType[MAXFUNCPARAM]; void* ppParam[MAXFUNCPARAM]; double nVal[MAXFUNCPARAM]; char* pStr[MAXFUNCPARAM]; sal_uInt8* pCellArr[MAXFUNCPARAM]; short i; for (i = 0; i < MAXFUNCPARAM; i++) { eParamType[i] = pLegacyFuncData->GetParamType(i); ppParam[i] = nullptr; nVal[i] = 0.0; pStr[i] = nullptr; pCellArr[i] = nullptr; } for (i = nParamCount; (i > 0) && (nGlobalError == FormulaError::NONE); i--) { if (IsMissing()) { // Old binary Add-In can't distinguish between missing // omitted argument and 0 (or any other value). Force // error. SetError( FormulaError::ParameterExpected); break; // for } switch (eParamType[i]) { case ParamType::PTR_DOUBLE : { nVal[i-1] = GetDouble(); ppParam[i] = &nVal[i-1]; } break; case ParamType::PTR_STRING : { OString aStr(OUStringToOString(GetString().getString(), osl_getThreadTextEncoding())); if ( aStr.getLength() >= ADDIN_MAXSTRLEN ) SetError( FormulaError::StringOverflow ); else { pStr[i-1] = new char[ADDIN_MAXSTRLEN]; strncpy( pStr[i-1], aStr.getStr(), ADDIN_MAXSTRLEN ); pStr[i-1][ADDIN_MAXSTRLEN-1] = 0; ppParam[i] = pStr[i-1]; } } break; case ParamType::PTR_DOUBLE_ARR : { SCCOL nCol1; SCROW nRow1; SCTAB nTab1; SCCOL nCol2; SCROW nRow2; SCTAB nTab2; PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); pCellArr[i-1] = new sal_uInt8[MAXARRSIZE]; if (!CreateDoubleArr(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, pCellArr[i-1])) SetError(FormulaError::CodeOverflow); else ppParam[i] = pCellArr[i-1]; } break; case ParamType::PTR_STRING_ARR : { SCCOL nCol1; SCROW nRow1; SCTAB nTab1; SCCOL nCol2; SCROW nRow2; SCTAB nTab2; PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); pCellArr[i-1] = new sal_uInt8[MAXARRSIZE]; if (!CreateStringArr(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, pCellArr[i-1])) SetError(FormulaError::CodeOverflow); else ppParam[i] = pCellArr[i-1]; } break; case ParamType::PTR_CELL_ARR : { SCCOL nCol1; SCROW nRow1; SCTAB nTab1; SCCOL nCol2; SCROW nRow2; SCTAB nTab2; PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); pCellArr[i-1] = new sal_uInt8[MAXARRSIZE]; if (!CreateCellArr(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, pCellArr[i-1])) SetError(FormulaError::CodeOverflow); else ppParam[i] = pCellArr[i-1]; } break; default : SetError(FormulaError::IllegalParameter); break; } } while ( i-- ) Pop(); // In case of error (otherwise i==0) pop all parameters if (nGlobalError == FormulaError::NONE) { if ( pLegacyFuncData->GetAsyncType() == ParamType::NONE ) { switch ( eParamType[0] ) { case ParamType::PTR_DOUBLE : { double nErg = 0.0; ppParam[0] = &nErg; pLegacyFuncData->Call(ppParam); PushDouble(nErg); } break; case ParamType::PTR_STRING : { std::unique_ptr pcErg(new char[ADDIN_MAXSTRLEN]); ppParam[0] = pcErg.get(); pLegacyFuncData->Call(ppParam); OUString aUni( pcErg.get(), strlen(pcErg.get()), osl_getThreadTextEncoding() ); PushString( aUni ); } break; default: PushError( FormulaError::UnknownState ); } } else { // enable asyncs after loading pArr->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT ); // assure identical handler with identical call? double nErg = 0.0; ppParam[0] = &nErg; pLegacyFuncData->Call(ppParam); sal_uLong nHandle = sal_uLong( nErg ); if ( nHandle >= 65536 ) { ScAddInAsync* pAs = ScAddInAsync::Get( nHandle ); if ( !pAs ) { pAs = new ScAddInAsync(nHandle, pLegacyFuncData, &mrDoc); pMyFormulaCell->StartListening( *pAs ); } else { pMyFormulaCell->StartListening( *pAs ); if ( !pAs->HasDocument( &mrDoc ) ) pAs->AddDocument( &mrDoc ); } if ( pAs->IsValid() ) { switch ( pAs->GetType() ) { case ParamType::PTR_DOUBLE : PushDouble( pAs->GetValue() ); break; case ParamType::PTR_STRING : PushString( pAs->GetString() ); break; default: PushError( FormulaError::UnknownState ); } } else PushNA(); } else PushNoValue(); } } for (i = 0; i < MAXFUNCPARAM; i++) { delete[] pStr[i]; delete[] pCellArr[i]; } } else { while( nParamCount-- > 0) Pop(); PushIllegalParameter(); } } else if ( !( aUnoName = ScGlobal::GetAddInCollection()->FindFunction(aFuncName, false) ).isEmpty() ) { // bLocalFirst=false in FindFunction, cFunc should be the stored // internal name ScUnoAddInCall aCall( *ScGlobal::GetAddInCollection(), aUnoName, nParamCount ); if ( !aCall.ValidParamCount() ) SetError( FormulaError::IllegalParameter ); if ( aCall.NeedsCaller() && GetError() == FormulaError::NONE ) { SfxObjectShell* pShell = mrDoc.GetDocumentShell(); if (pShell) aCall.SetCallerFromObjectShell( pShell ); else { // use temporary model object (without document) to supply options aCall.SetCaller( static_cast( new ScDocOptionsObj( mrDoc.GetDocOptions() ) ) ); } } short nPar = nParamCount; while ( nPar > 0 && GetError() == FormulaError::NONE ) { --nPar; // 0 .. (nParamCount-1) uno::Any aParam; if (IsMissing()) { // Add-In has to explicitly handle an omitted empty missing // argument, do not default to anything like GetDouble() would // do (e.g. 0). Pop(); aCall.SetParam( nPar, aParam ); continue; // while } StackVar nStackType = GetStackType(); ScAddInArgumentType eType = aCall.GetArgType( nPar ); switch (eType) { case SC_ADDINARG_INTEGER: { sal_Int32 nVal = GetInt32(); if (nGlobalError == FormulaError::NONE) aParam <<= nVal; } break; case SC_ADDINARG_DOUBLE: aParam <<= GetDouble(); break; case SC_ADDINARG_STRING: aParam <<= GetString().getString(); break; case SC_ADDINARG_INTEGER_ARRAY: switch( nStackType ) { case svDouble: case svString: case svSingleRef: { sal_Int32 nVal = GetInt32(); if (nGlobalError == FormulaError::NONE) { uno::Sequence aInner( &nVal, 1 ); uno::Sequence< uno::Sequence > aOuter( &aInner, 1 ); aParam <<= aOuter; } } break; case svDoubleRef: { ScRange aRange; PopDoubleRef( aRange ); if (!ScRangeToSequence::FillLongArray( aParam, mrDoc, aRange )) SetError(FormulaError::IllegalParameter); } break; case svMatrix: if (!ScRangeToSequence::FillLongArray( aParam, PopMatrix().get() )) SetError(FormulaError::IllegalParameter); break; default: PopError(); SetError(FormulaError::IllegalParameter); } break; case SC_ADDINARG_DOUBLE_ARRAY: switch( nStackType ) { case svDouble: case svString: case svSingleRef: { double fVal = GetDouble(); uno::Sequence aInner( &fVal, 1 ); uno::Sequence< uno::Sequence > aOuter( &aInner, 1 ); aParam <<= aOuter; } break; case svDoubleRef: { ScRange aRange; PopDoubleRef( aRange ); if (!ScRangeToSequence::FillDoubleArray( aParam, mrDoc, aRange )) SetError(FormulaError::IllegalParameter); } break; case svMatrix: if (!ScRangeToSequence::FillDoubleArray( aParam, PopMatrix().get() )) SetError(FormulaError::IllegalParameter); break; default: PopError(); SetError(FormulaError::IllegalParameter); } break; case SC_ADDINARG_STRING_ARRAY: switch( nStackType ) { case svDouble: case svString: case svSingleRef: { OUString aString = GetString().getString(); uno::Sequence aInner( &aString, 1 ); uno::Sequence< uno::Sequence > aOuter( &aInner, 1 ); aParam <<= aOuter; } break; case svDoubleRef: { ScRange aRange; PopDoubleRef( aRange ); if (!ScRangeToSequence::FillStringArray( aParam, mrDoc, aRange )) SetError(FormulaError::IllegalParameter); } break; case svMatrix: if (!ScRangeToSequence::FillStringArray( aParam, PopMatrix().get(), pFormatter )) SetError(FormulaError::IllegalParameter); break; default: PopError(); SetError(FormulaError::IllegalParameter); } break; case SC_ADDINARG_MIXED_ARRAY: switch( nStackType ) { case svDouble: case svString: case svSingleRef: { uno::Any aElem; if ( nStackType == svDouble ) aElem <<= GetDouble(); else if ( nStackType == svString ) aElem <<= GetString().getString(); else { ScAddress aAdr; if ( PopDoubleRefOrSingleRef( aAdr ) ) { ScRefCellValue aCell(mrDoc, aAdr); if (aCell.hasString()) { svl::SharedString aStr; GetCellString(aStr, aCell); aElem <<= aStr.getString(); } else aElem <<= GetCellValue(aAdr, aCell); } } uno::Sequence aInner( &aElem, 1 ); uno::Sequence< uno::Sequence > aOuter( &aInner, 1 ); aParam <<= aOuter; } break; case svDoubleRef: { ScRange aRange; PopDoubleRef( aRange ); if (!ScRangeToSequence::FillMixedArray( aParam, mrDoc, aRange )) SetError(FormulaError::IllegalParameter); } break; case svMatrix: if (!ScRangeToSequence::FillMixedArray( aParam, PopMatrix().get() )) SetError(FormulaError::IllegalParameter); break; default: PopError(); SetError(FormulaError::IllegalParameter); } break; case SC_ADDINARG_VALUE_OR_ARRAY: switch( nStackType ) { case svDouble: aParam <<= GetDouble(); break; case svString: aParam <<= GetString().getString(); break; case svSingleRef: { ScAddress aAdr; if ( PopDoubleRefOrSingleRef( aAdr ) ) { ScRefCellValue aCell(mrDoc, aAdr); if (aCell.hasString()) { svl::SharedString aStr; GetCellString(aStr, aCell); aParam <<= aStr.getString(); } else aParam <<= GetCellValue(aAdr, aCell); } } break; case svDoubleRef: { ScRange aRange; PopDoubleRef( aRange ); if (!ScRangeToSequence::FillMixedArray( aParam, mrDoc, aRange )) SetError(FormulaError::IllegalParameter); } break; case svMatrix: if (!ScRangeToSequence::FillMixedArray( aParam, PopMatrix().get() )) SetError(FormulaError::IllegalParameter); break; default: PopError(); SetError(FormulaError::IllegalParameter); } break; case SC_ADDINARG_CELLRANGE: switch( nStackType ) { case svSingleRef: { ScAddress aAdr; PopSingleRef( aAdr ); ScRange aRange( aAdr ); uno::Reference xObj = ScCellRangeObj::CreateRangeFromDoc( mrDoc, aRange ); if (xObj.is()) aParam <<= xObj; else SetError(FormulaError::IllegalParameter); } break; case svDoubleRef: { ScRange aRange; PopDoubleRef( aRange ); uno::Reference xObj = ScCellRangeObj::CreateRangeFromDoc( mrDoc, aRange ); if (xObj.is()) { aParam <<= xObj; } else { SetError(FormulaError::IllegalParameter); } } break; default: PopError(); SetError(FormulaError::IllegalParameter); } break; default: PopError(); SetError(FormulaError::IllegalParameter); } aCall.SetParam( nPar, aParam ); } while (nPar-- > 0) { Pop(); // in case of error, remove remaining args } if ( GetError() == FormulaError::NONE ) { aCall.ExecuteCall(); if ( aCall.HasVarRes() ) // handle async functions { pArr->AddRecalcMode( ScRecalcMode::ONLOAD_LENIENT ); uno::Reference xRes = aCall.GetVarRes(); ScAddInListener* pLis = ScAddInListener::Get( xRes ); // In case there is no pMyFormulaCell, i.e. while interpreting // temporarily from within the Function Wizard, try to obtain a // valid result from an existing listener for that volatile, or // create a new and hope for an immediate result. If none // available that should lead to a void result and thus #N/A. bool bTemporaryListener = false; if ( !pLis ) { pLis = ScAddInListener::CreateListener( xRes, &mrDoc ); if (pMyFormulaCell) pMyFormulaCell->StartListening( *pLis ); else bTemporaryListener = true; } else if (pMyFormulaCell) { pMyFormulaCell->StartListening( *pLis ); if ( !pLis->HasDocument( &mrDoc ) ) { pLis->AddDocument( &mrDoc ); } } aCall.SetResult( pLis->GetResult() ); // use result from async if (bTemporaryListener) { try { // EventObject can be any, not evaluated by // ScAddInListener::disposing() css::lang::EventObject aEvent; pLis->disposing(aEvent); // pLis is dead hereafter } catch (const uno::Exception&) { } } } if ( aCall.GetErrCode() != FormulaError::NONE ) { PushError( aCall.GetErrCode() ); } else if ( aCall.HasMatrix() ) { PushMatrix( aCall.GetMatrix() ); } else if ( aCall.HasString() ) { PushString( aCall.GetString() ); } else { PushDouble( aCall.GetValue() ); } } else // error... PushError( GetError()); } else { while( nParamCount-- > 0) { Pop(); } PushError( FormulaError::NoAddin ); } } void ScInterpreter::ScMissing() { if ( aCode.IsEndOfPath() ) PushTempToken( new ScEmptyCellToken( false, false ) ); else PushTempToken( new FormulaMissingToken ); } #if HAVE_FEATURE_SCRIPTING static uno::Any lcl_getSheetModule( const uno::Reference& xCellRange, const ScDocument* pDok ) { uno::Reference< sheet::XSheetCellRange > xSheetRange( xCellRange, uno::UNO_QUERY_THROW ); uno::Reference< beans::XPropertySet > xProps( xSheetRange->getSpreadsheet(), uno::UNO_QUERY_THROW ); OUString sCodeName; xProps->getPropertyValue("CodeName") >>= sCodeName; // #TODO #FIXME ideally we should 'throw' here if we don't get a valid parent, but... it is possible // to create a module ( and use 'Option VBASupport 1' ) for a calc document, in this scenario there // are *NO* special document module objects ( of course being able to switch between vba/non vba mode at // the document in the future could fix this, especially IF the switching of the vba mode takes care to // create the special document module objects if they don't exist. BasicManager* pBasMgr = pDok->GetDocumentShell()->GetBasicManager(); uno::Reference< uno::XInterface > xIf; if ( pBasMgr && !pBasMgr->GetName().isEmpty() ) { OUString sProj( "Standard" ); if ( !pDok->GetDocumentShell()->GetBasicManager()->GetName().isEmpty() ) { sProj = pDok->GetDocumentShell()->GetBasicManager()->GetName(); } StarBASIC* pBasic = pDok->GetDocumentShell()->GetBasicManager()->GetLib( sProj ); if ( pBasic ) { SbModule* pMod = pBasic->FindModule( sCodeName ); if ( pMod ) { xIf = pMod->GetUnoModule(); } } } return uno::makeAny( xIf ); } static bool lcl_setVBARange( const ScRange& aRange, const ScDocument& rDok, SbxVariable* pPar ) { bool bOk = false; try { uno::Reference< uno::XInterface > xVBARange; uno::Reference xCellRange = ScCellRangeObj::CreateRangeFromDoc( rDok, aRange ); uno::Sequence< uno::Any > aArgs(2); aArgs[0] = lcl_getSheetModule( xCellRange, &rDok ); aArgs[1] <<= xCellRange; xVBARange = ooo::vba::createVBAUnoAPIServiceWithArgs( rDok.GetDocumentShell(), "ooo.vba.excel.Range", aArgs ); if ( xVBARange.is() ) { SbxObjectRef aObj = GetSbUnoObject( "A-Range", uno::Any( xVBARange ) ); SetSbUnoObjectDfltPropName( aObj.get() ); bOk = pPar->PutObject( aObj.get() ); } } catch( uno::Exception& ) { } return bOk; } static bool lcl_isNumericResult( double& fVal, const SbxVariable* pVar ) { switch (pVar->GetType()) { case SbxINTEGER: case SbxLONG: case SbxSINGLE: case SbxDOUBLE: case SbxCURRENCY: case SbxDATE: case SbxUSHORT: case SbxULONG: case SbxINT: case SbxUINT: case SbxSALINT64: case SbxSALUINT64: case SbxDECIMAL: fVal = pVar->GetDouble(); return true; case SbxBOOL: fVal = (pVar->GetBool() ? 1.0 : 0.0); return true; default: ; // nothing } return false; } #endif void ScInterpreter::ScMacro() { #if !HAVE_FEATURE_SCRIPTING PushNoValue(); // without DocShell no CallBasic return; #else SbxBase::ResetError(); sal_uInt8 nParamCount = GetByte(); OUString aMacro( pCur->GetExternal() ); SfxObjectShell* pDocSh = mrDoc.GetDocumentShell(); if ( !pDocSh ) { PushNoValue(); // without DocShell no CallBasic return; } // no security queue beforehand (just CheckMacroWarn), moved to CallBasic // If the Dok was loaded during a Basic-Calls, // is the Sbx-object created(?) // pDocSh->GetSbxObject(); // search function with the name, // then assemble SfxObjectShell::CallBasic from aBasicStr, aMacroStr StarBASIC* pRoot; try { pRoot = pDocSh->GetBasic(); } catch (...) { pRoot = nullptr; } SbxVariable* pVar = pRoot ? pRoot->Find(aMacro, SbxClassType::Method) : nullptr; if( !pVar || pVar->GetType() == SbxVOID ) { PushError( FormulaError::NoMacro ); return; } SbMethod* pMethod = dynamic_cast(pVar); if( !pMethod ) { PushError( FormulaError::NoMacro ); return; } bool bVolatileMacro = false; SbModule* pModule = pMethod->GetModule(); bool bUseVBAObjects = pModule->IsVBACompat(); SbxObject* pObject = pModule->GetParent(); OSL_ENSURE(dynamic_cast(pObject) != nullptr, "No Basic found!"); OUString aMacroStr = pObject->GetName() + "." + pModule->GetName() + "." + pMethod->GetName(); OUString aBasicStr; if (pRoot && bUseVBAObjects) { // just here to make sure the VBA objects when we run the macro during ODF import pRoot->getVBAGlobals(); } if (pObject->GetParent()) { aBasicStr = pObject->GetParent()->GetName(); // document BASIC } else { aBasicStr = SfxGetpApp()->GetName(); // application BASIC } // assemble a parameter array SbxArrayRef refPar = new SbxArray; bool bOk = true; for( sal_uInt32 i = nParamCount; i && bOk ; i-- ) { SbxVariable* pPar = refPar->Get(i); switch( GetStackType() ) { case svDouble: pPar->PutDouble( GetDouble() ); break; case svString: pPar->PutString( GetString().getString() ); break; case svExternalSingleRef: { ScExternalRefCache::TokenRef pToken; PopExternalSingleRef(pToken); if (nGlobalError != FormulaError::NONE) bOk = false; else { if ( pToken->GetType() == svString ) pPar->PutString( pToken->GetString().getString() ); else if ( pToken->GetType() == svDouble ) pPar->PutDouble( pToken->GetDouble() ); else { SetError( FormulaError::IllegalArgument ); bOk = false; } } } break; case svSingleRef: { ScAddress aAdr; PopSingleRef( aAdr ); if ( bUseVBAObjects ) { ScRange aRange( aAdr ); bOk = lcl_setVBARange( aRange, mrDoc, pPar ); } else { bOk = SetSbxVariable( pPar, aAdr ); } } break; case svDoubleRef: { SCCOL nCol1; SCROW nRow1; SCTAB nTab1; SCCOL nCol2; SCROW nRow2; SCTAB nTab2; PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); if( nTab1 != nTab2 ) { SetError( FormulaError::IllegalParameter ); bOk = false; } else { if ( bUseVBAObjects ) { ScRange aRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); bOk = lcl_setVBARange( aRange, mrDoc, pPar ); } else { SbxDimArrayRef refArray = new SbxDimArray; refArray->AddDim(1, nRow2 - nRow1 + 1); refArray->AddDim(1, nCol2 - nCol1 + 1); ScAddress aAdr( nCol1, nRow1, nTab1 ); for( SCROW nRow = nRow1; bOk && nRow <= nRow2; nRow++ ) { aAdr.SetRow( nRow ); sal_Int32 nIdx[ 2 ]; nIdx[ 0 ] = nRow-nRow1+1; for( SCCOL nCol = nCol1; bOk && nCol <= nCol2; nCol++ ) { aAdr.SetCol( nCol ); nIdx[ 1 ] = nCol-nCol1+1; SbxVariable* p = refArray->Get(nIdx); bOk = SetSbxVariable( p, aAdr ); } } pPar->PutObject( refArray.get() ); } } } break; case svExternalDoubleRef: case svMatrix: { ScMatrixRef pMat = GetMatrix(); SCSIZE nC, nR; if (pMat && nGlobalError == FormulaError::NONE) { pMat->GetDimensions(nC, nR); SbxDimArrayRef refArray = new SbxDimArray; refArray->AddDim(1, static_cast(nR)); refArray->AddDim(1, static_cast(nC)); for( SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++ ) { sal_Int32 nIdx[ 2 ]; nIdx[ 0 ] = static_cast(nMatRow+1); for( SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++ ) { nIdx[ 1 ] = static_cast(nMatCol+1); SbxVariable* p = refArray->Get(nIdx); if (pMat->IsStringOrEmpty(nMatCol, nMatRow)) { p->PutString( pMat->GetString(nMatCol, nMatRow).getString() ); } else { p->PutDouble( pMat->GetDouble(nMatCol, nMatRow)); } } } pPar->PutObject( refArray.get() ); } else { SetError( FormulaError::IllegalParameter ); } } break; default: SetError( FormulaError::IllegalParameter ); bOk = false; } } if( bOk ) { mrDoc.LockTable( aPos.Tab() ); SbxVariableRef refRes = new SbxVariable; mrDoc.IncMacroInterpretLevel(); ErrCode eRet = pDocSh->CallBasic( aMacroStr, aBasicStr, refPar.get(), refRes.get() ); mrDoc.DecMacroInterpretLevel(); mrDoc.UnlockTable( aPos.Tab() ); ScMacroManager* pMacroMgr = mrDoc.GetMacroManager(); if (pMacroMgr) { bVolatileMacro = pMacroMgr->GetUserFuncVolatile( pMethod->GetName() ); pMacroMgr->AddDependentCell(pModule->GetName(), pMyFormulaCell); } double fVal; SbxDataType eResType = refRes->GetType(); if( SbxBase::GetError() ) { SetError( FormulaError::NoValue); } if ( eRet != ERRCODE_NONE ) { PushNoValue(); } else if (lcl_isNumericResult( fVal, refRes.get())) { switch (eResType) { case SbxDATE: nFuncFmtType = SvNumFormatType::DATE; break; case SbxBOOL: nFuncFmtType = SvNumFormatType::LOGICAL; break; // Do not add SbxCURRENCY, we don't know which currency. default: ; // nothing } PushDouble( fVal ); } else if ( eResType & SbxARRAY ) { SbxBase* pElemObj = refRes->GetObject(); SbxDimArray* pDimArray = dynamic_cast(pElemObj); sal_Int32 nDim = pDimArray ? pDimArray->GetDims() : 0; if ( 1 <= nDim && nDim <= 2 ) { sal_Int32 nCs, nCe, nRs; SCSIZE nC, nR; SCCOL nColIdx; SCROW nRowIdx; if ( nDim == 1 ) { // array( cols ) one line, several columns pDimArray->GetDim(1, nCs, nCe); nC = static_cast(nCe - nCs + 1); nRs = 0; nR = 1; nColIdx = 0; nRowIdx = 1; } else { // array( rows, cols ) sal_Int32 nRe; pDimArray->GetDim(1, nRs, nRe); nR = static_cast(nRe - nRs + 1); pDimArray->GetDim(2, nCs, nCe); nC = static_cast(nCe - nCs + 1); nColIdx = 1; nRowIdx = 0; } ScMatrixRef pMat = GetNewMat( nC, nR, /*bEmpty*/true); if ( pMat ) { SbxVariable* pV; for ( SCSIZE j=0; j < nR; j++ ) { sal_Int32 nIdx[ 2 ]; // in one-dimensional array( cols ) nIdx[1] // from SbxDimArray::Get is ignored nIdx[ nRowIdx ] = nRs + static_cast(j); for ( SCSIZE i=0; i < nC; i++ ) { nIdx[ nColIdx ] = nCs + static_cast(i); pV = pDimArray->Get(nIdx); if ( lcl_isNumericResult( fVal, pV) ) { pMat->PutDouble( fVal, i, j ); } else { pMat->PutString(mrStrPool.intern(pV->GetOUString()), i, j); } } } PushMatrix( pMat ); } else { PushIllegalArgument(); } } else { PushNoValue(); } } else { PushString( refRes->GetOUString() ); } } if (bVolatileMacro && meVolatileType == NOT_VOLATILE) meVolatileType = VOLATILE_MACRO; #endif } #if HAVE_FEATURE_SCRIPTING bool ScInterpreter::SetSbxVariable( SbxVariable* pVar, const ScAddress& rPos ) { bool bOk = true; ScRefCellValue aCell(mrDoc, rPos); if (!aCell.isEmpty()) { FormulaError nErr; double nVal; switch (aCell.meType) { case CELLTYPE_VALUE : nVal = GetValueCellValue(rPos, aCell.mfValue); pVar->PutDouble( nVal ); break; case CELLTYPE_STRING : case CELLTYPE_EDIT : pVar->PutString(aCell.getString(&mrDoc)); break; case CELLTYPE_FORMULA : nErr = aCell.mpFormula->GetErrCode(); if( nErr == FormulaError::NONE ) { if (aCell.mpFormula->IsValue()) { nVal = aCell.mpFormula->GetValue(); pVar->PutDouble( nVal ); } else pVar->PutString(aCell.mpFormula->GetString().getString()); } else { SetError( nErr ); bOk = false; } break; default : pVar->PutEmpty(); } } else pVar->PutEmpty(); return bOk; } #endif void ScInterpreter::ScTableOp() { sal_uInt8 nParamCount = GetByte(); if (nParamCount != 3 && nParamCount != 5) { PushIllegalParameter(); return; } std::unique_ptr pTableOp(new ScInterpreterTableOpParams); if (nParamCount == 5) { PopSingleRef( pTableOp->aNew2 ); PopSingleRef( pTableOp->aOld2 ); } PopSingleRef( pTableOp->aNew1 ); PopSingleRef( pTableOp->aOld1 ); PopSingleRef( pTableOp->aFormulaPos ); pTableOp->bValid = true; mrDoc.m_TableOpList.push_back(pTableOp.get()); mrDoc.IncInterpreterTableOpLevel(); bool bReuseLastParams = (mrDoc.aLastTableOpParams == *pTableOp); if ( bReuseLastParams ) { pTableOp->aNotifiedFormulaPos = mrDoc.aLastTableOpParams.aNotifiedFormulaPos; pTableOp->bRefresh = true; for ( const auto& rPos : pTableOp->aNotifiedFormulaPos ) { // emulate broadcast and indirectly collect cell pointers ScRefCellValue aCell(mrDoc, rPos); if (aCell.meType == CELLTYPE_FORMULA) aCell.mpFormula->SetTableOpDirty(); } } else { // broadcast and indirectly collect cell pointers and positions mrDoc.SetTableOpDirty( pTableOp->aOld1 ); if ( nParamCount == 5 ) mrDoc.SetTableOpDirty( pTableOp->aOld2 ); } pTableOp->bCollectNotifications = false; ScRefCellValue aCell(mrDoc, pTableOp->aFormulaPos); if (aCell.meType == CELLTYPE_FORMULA) aCell.mpFormula->SetDirtyVar(); if (aCell.hasNumeric()) { PushDouble(GetCellValue(pTableOp->aFormulaPos, aCell)); } else { svl::SharedString aCellString; GetCellString(aCellString, aCell); PushString( aCellString ); } auto const itr = ::std::find(mrDoc.m_TableOpList.begin(), mrDoc.m_TableOpList.end(), pTableOp.get()); if (itr != mrDoc.m_TableOpList.end()) { mrDoc.m_TableOpList.erase(itr); } // set dirty again once more to be able to recalculate original for ( const auto& pCell : pTableOp->aNotifiedFormulaCells ) { pCell->SetTableOpDirty(); } // save these params for next incarnation if ( !bReuseLastParams ) mrDoc.aLastTableOpParams = *pTableOp; if (aCell.meType == CELLTYPE_FORMULA) { aCell.mpFormula->SetDirtyVar(); aCell.mpFormula->GetErrCode(); // recalculate original } // Reset all dirty flags so next incarnation does really collect all cell // pointers during notifications and not just non-dirty ones, which may // happen if a formula cell is used by more than one TableOp block. for ( const auto& pCell : pTableOp->aNotifiedFormulaCells ) { pCell->ResetTableOpDirtyVar(); } pTableOp.reset(); mrDoc.DecInterpreterTableOpLevel(); } void ScInterpreter::ScDBArea() { ScDBData* pDBData = mrDoc.GetDBCollection()->getNamedDBs().findByIndex(pCur->GetIndex()); if (pDBData) { ScComplexRefData aRefData; aRefData.InitFlags(); ScRange aRange; pDBData->GetArea(aRange); aRange.aEnd.SetTab(aRange.aStart.Tab()); aRefData.SetRange(mrDoc.GetSheetLimits(), aRange, aPos); PushTempToken( new ScDoubleRefToken( mrDoc.GetSheetLimits(), aRefData ) ); } else PushError( FormulaError::NoName); } void ScInterpreter::ScColRowNameAuto() { ScComplexRefData aRefData( *pCur->GetDoubleRef() ); ScRange aAbs = aRefData.toAbs(mrDoc, aPos); if (!mrDoc.ValidRange(aAbs)) { PushError( FormulaError::NoRef ); return; } SCCOL nStartCol; SCROW nStartRow; // maybe remember limit by using defined ColRowNameRange SCCOL nCol2 = aAbs.aEnd.Col(); SCROW nRow2 = aAbs.aEnd.Row(); // DataArea of the first cell nStartCol = aAbs.aStart.Col(); nStartRow = aAbs.aStart.Row(); aAbs.aEnd = aAbs.aStart; // Shrink to the top-left cell. { // Expand to the data area. Only modify the end position. SCCOL nDACol1 = aAbs.aStart.Col(), nDACol2 = aAbs.aEnd.Col(); SCROW nDARow1 = aAbs.aStart.Row(), nDARow2 = aAbs.aEnd.Row(); mrDoc.GetDataArea(aAbs.aStart.Tab(), nDACol1, nDARow1, nDACol2, nDARow2, true, false); aAbs.aEnd.SetCol(nDACol2); aAbs.aEnd.SetRow(nDARow2); } // corresponds with ScCompiler::GetToken if ( aRefData.Ref1.IsColRel() ) { // ColName aAbs.aEnd.SetCol(nStartCol); // maybe get previous limit by using defined ColRowNameRange if (aAbs.aEnd.Row() > nRow2) aAbs.aEnd.SetRow(nRow2); if ( aPos.Col() == nStartCol ) { SCROW nMyRow = aPos.Row(); if ( nStartRow <= nMyRow && nMyRow <= aAbs.aEnd.Row()) { //Formula in the same column and within the range if ( nMyRow == nStartRow ) { // take the rest under the name nStartRow++; if ( nStartRow > mrDoc.MaxRow() ) nStartRow = mrDoc.MaxRow(); aAbs.aStart.SetRow(nStartRow); } else { // below the name to the formula cell aAbs.aEnd.SetRow(nMyRow - 1); } } } } else { // RowName aAbs.aEnd.SetRow(nStartRow); // maybe get previous limit by using defined ColRowNameRange if (aAbs.aEnd.Col() > nCol2) aAbs.aEnd.SetCol(nCol2); if ( aPos.Row() == nStartRow ) { SCCOL nMyCol = aPos.Col(); if (nStartCol <= nMyCol && nMyCol <= aAbs.aEnd.Col()) { //Formula in the same column and within the range if ( nMyCol == nStartCol ) { // take the rest under the name nStartCol++; if ( nStartCol > mrDoc.MaxCol() ) nStartCol = mrDoc.MaxCol(); aAbs.aStart.SetCol(nStartCol); } else { // below the name to the formula cell aAbs.aEnd.SetCol(nMyCol - 1); } } } } aRefData.SetRange(mrDoc.GetSheetLimits(), aAbs, aPos); PushTempToken( new ScDoubleRefToken( mrDoc.GetSheetLimits(), aRefData ) ); } // --- internals ------------------------------------------------------------ void ScInterpreter::ScTTT() { // temporary test, testing functions etc. sal_uInt8 nParamCount = GetByte(); // do something, count down nParamCount with Pops! // clean up Stack while ( nParamCount-- > 0) Pop(); PushError(FormulaError::NoValue); } ScInterpreter::ScInterpreter( ScFormulaCell* pCell, ScDocument& rDoc, ScInterpreterContext& rContext, const ScAddress& rPos, ScTokenArray& r, bool bForGroupThreading ) : aCode(r) , aPos(rPos) , pArr(&r) , mrContext(rContext) , mrDoc(rDoc) , mpLinkManager(rDoc.GetLinkManager()) , mrStrPool(rDoc.GetSharedStringPool()) , pJumpMatrix(nullptr) , pMyFormulaCell(pCell) , pFormatter(rContext.GetFormatTable()) , pCur(nullptr) , nGlobalError(FormulaError::NONE) , sp(0) , maxsp(0) , nFuncFmtIndex(0) , nCurFmtIndex(0) , nRetFmtIndex(0) , nFuncFmtType(SvNumFormatType::ALL) , nCurFmtType(SvNumFormatType::ALL) , nRetFmtType(SvNumFormatType::ALL) , mnStringNoValueError(FormulaError::NoValue) , mnSubTotalFlags(SubtotalFlags::NONE) , cPar(0) , bCalcAsShown(rDoc.GetDocOptions().IsCalcAsShown()) , meVolatileType(r.IsRecalcModeAlways() ? VOLATILE : NOT_VOLATILE) { MergeCalcConfig(); if(pMyFormulaCell) { ScMatrixMode cMatFlag = pMyFormulaCell->GetMatrixFlag(); bMatrixFormula = ( cMatFlag == ScMatrixMode::Formula ); } else bMatrixFormula = false; // Lets not use the global stack while formula-group-threading. // as it complicates its life-cycle mgmt since for threading formula-groups, // ScInterpreter is preallocated (in main thread) for each worker thread. if (!bGlobalStackInUse && !bForGroupThreading) { bGlobalStackInUse = true; if (!pGlobalStack) pGlobalStack.reset(new ScTokenStack); pStackObj = pGlobalStack.get(); } else { pStackObj = new ScTokenStack; } pStack = pStackObj->pPointer; } ScInterpreter::~ScInterpreter() { if ( pStackObj == pGlobalStack.get() ) bGlobalStackInUse = false; else delete pStackObj; } void ScInterpreter::Init( ScFormulaCell* pCell, const ScAddress& rPos, ScTokenArray& rTokArray ) { aCode.ReInit(rTokArray); aPos = rPos; pArr = &rTokArray; xResult = nullptr; pJumpMatrix = nullptr; pTokenMatrixMap.reset(); pMyFormulaCell = pCell; pCur = nullptr; nGlobalError = FormulaError::NONE; sp = 0; maxsp = 0; nFuncFmtIndex = 0; nCurFmtIndex = 0; nRetFmtIndex = 0; nFuncFmtType = SvNumFormatType::ALL; nCurFmtType = SvNumFormatType::ALL; nRetFmtType = SvNumFormatType::ALL; mnStringNoValueError = FormulaError::NoValue; mnSubTotalFlags = SubtotalFlags::NONE; cPar = 0; } ScCalcConfig& ScInterpreter::GetOrCreateGlobalConfig() { if (!mpGlobalConfig) mpGlobalConfig = new ScCalcConfig(); return *mpGlobalConfig; } void ScInterpreter::SetGlobalConfig(const ScCalcConfig& rConfig) { GetOrCreateGlobalConfig() = rConfig; } const ScCalcConfig& ScInterpreter::GetGlobalConfig() { return GetOrCreateGlobalConfig(); } void ScInterpreter::MergeCalcConfig() { maCalcConfig = GetOrCreateGlobalConfig(); maCalcConfig.MergeDocumentSpecific( mrDoc.GetCalcConfig()); } void ScInterpreter::GlobalExit() { OSL_ENSURE(!bGlobalStackInUse, "who is still using the TokenStack?"); pGlobalStack.reset(); } namespace { double applyImplicitIntersection(const sc::RangeMatrix& rMat, const ScAddress& rPos) { if (rMat.mnRow1 <= rPos.Row() && rPos.Row() <= rMat.mnRow2 && rMat.mnCol1 == rMat.mnCol2) { SCROW nOffset = rPos.Row() - rMat.mnRow1; return rMat.mpMat->GetDouble(0, nOffset); } if (rMat.mnCol1 <= rPos.Col() && rPos.Col() <= rMat.mnCol2 && rMat.mnRow1 == rMat.mnRow2) { SCROW nOffset = rPos.Col() - rMat.mnCol1; return rMat.mpMat->GetDouble(nOffset, 0); } double fVal; rtl::math::setNan(&fVal); return fVal; } // Test for Functions that evaluate an error code and directly set nGlobalError to 0 bool IsErrFunc(OpCode oc) { switch (oc) { case ocCount : case ocCount2 : case ocErrorType : case ocIsEmpty : case ocIsErr : case ocIsError : case ocIsFormula : case ocIsLogical : case ocIsNA : case ocIsNonString : case ocIsRef : case ocIsString : case ocIsValue : case ocN : case ocType : case ocIfError : case ocIfNA : case ocErrorType_ODF : case ocAggregate: // may ignore errors depending on option case ocIfs_MS: case ocSwitch_MS: return true; default: return false; } } } //namespace StackVar ScInterpreter::Interpret() { SvNumFormatType nRetTypeExpr = SvNumFormatType::UNDEFINED; sal_uInt32 nRetIndexExpr = 0; sal_uInt16 nErrorFunction = 0; sal_uInt16 nErrorFunctionCount = 0; std::vector aErrorFunctionStack; sal_uInt16 nStackBase; nGlobalError = FormulaError::NONE; nStackBase = sp = maxsp = 0; nRetFmtType = SvNumFormatType::UNDEFINED; nFuncFmtType = SvNumFormatType::UNDEFINED; nFuncFmtIndex = nCurFmtIndex = nRetFmtIndex = 0; xResult = nullptr; pJumpMatrix = nullptr; mnSubTotalFlags = SubtotalFlags::NONE; ScTokenMatrixMap::const_iterator aTokenMatrixMapIter; // Once upon a time we used to have FP exceptions on, and there was a // Windows printer driver that kept switching off exceptions, so we had to // switch them back on again every time. Who knows if there isn't a driver // that keeps switching exceptions on, now that we run with exceptions off, // so reassure exceptions are really off. SAL_MATH_FPEXCEPTIONS_OFF(); OpCode eOp = ocNone; aCode.Reset(); for (;;) { pCur = aCode.Next(); if (!pCur || (nGlobalError != FormulaError::NONE && nErrorFunction > nErrorFunctionCount) ) break; eOp = pCur->GetOpCode(); cPar = pCur->GetByte(); if ( eOp == ocPush ) { // RPN code push without error PushWithoutError( *pCur ); nCurFmtType = SvNumFormatType::UNDEFINED; } else if (pTokenMatrixMap && !FormulaCompiler::IsOpCodeJumpCommand( eOp ) && ((aTokenMatrixMapIter = pTokenMatrixMap->find( pCur)) != pTokenMatrixMap->end()) && (*aTokenMatrixMapIter).second->GetType() != svJumpMatrix) { // Path already calculated, reuse result. nStackBase = sp - pCur->GetParamCount(); if ( nStackBase > sp ) nStackBase = sp; // underflow?!? sp = nStackBase; PushTokenRef( (*aTokenMatrixMapIter).second); } else { // previous expression determines the current number format nCurFmtType = nRetTypeExpr; nCurFmtIndex = nRetIndexExpr; // default function's format, others are set if needed nFuncFmtType = SvNumFormatType::NUMBER; nFuncFmtIndex = 0; if (FormulaCompiler::IsOpCodeJumpCommand( eOp )) nStackBase = sp; // don't mess around with the jumps else { // Convert parameters to matrix if in array/matrix formula and // parameters of function indicate doing so. Create JumpMatrix // if necessary. if ( MatrixParameterConversion() ) { eOp = ocNone; // JumpMatrix created nStackBase = sp; } else if (sp >= pCur->GetParamCount()) nStackBase = sp - pCur->GetParamCount(); else nStackBase = sp; // underflow?!? } switch( eOp ) { case ocSep: case ocClose: // pushed by the compiler case ocMissing : ScMissing(); break; case ocMacro : ScMacro(); break; case ocDBArea : ScDBArea(); break; case ocColRowNameAuto : ScColRowNameAuto(); break; case ocIf : ScIfJump(); break; case ocIfError : ScIfError( false ); break; case ocIfNA : ScIfError( true ); break; case ocChoose : ScChooseJump(); break; case ocAdd : ScAdd(); break; case ocSub : ScSub(); break; case ocMul : ScMul(); break; case ocDiv : ScDiv(); break; case ocAmpersand : ScAmpersand(); break; case ocPow : ScPow(); break; case ocEqual : ScEqual(); break; case ocNotEqual : ScNotEqual(); break; case ocLess : ScLess(); break; case ocGreater : ScGreater(); break; case ocLessEqual : ScLessEqual(); break; case ocGreaterEqual : ScGreaterEqual(); break; case ocAnd : ScAnd(); break; case ocOr : ScOr(); break; case ocXor : ScXor(); break; case ocIntersect : ScIntersect(); break; case ocRange : ScRangeFunc(); break; case ocUnion : ScUnionFunc(); break; case ocNot : ScNot(); break; case ocNegSub : case ocNeg : ScNeg(); break; case ocPercentSign : ScPercentSign(); break; case ocPi : ScPi(); break; case ocRandom : ScRandom(); break; case ocRandomNV : ScRandom(); break; case ocRandbetweenNV : ScRandbetween(); break; case ocTrue : ScTrue(); break; case ocFalse : ScFalse(); break; case ocGetActDate : ScGetActDate(); break; case ocGetActTime : ScGetActTime(); break; case ocNotAvail : PushError( FormulaError::NotAvailable); break; case ocDeg : ScDeg(); break; case ocRad : ScRad(); break; case ocSin : ScSin(); break; case ocCos : ScCos(); break; case ocTan : ScTan(); break; case ocCot : ScCot(); break; case ocArcSin : ScArcSin(); break; case ocArcCos : ScArcCos(); break; case ocArcTan : ScArcTan(); break; case ocArcCot : ScArcCot(); break; case ocSinHyp : ScSinHyp(); break; case ocCosHyp : ScCosHyp(); break; case ocTanHyp : ScTanHyp(); break; case ocCotHyp : ScCotHyp(); break; case ocArcSinHyp : ScArcSinHyp(); break; case ocArcCosHyp : ScArcCosHyp(); break; case ocArcTanHyp : ScArcTanHyp(); break; case ocArcCotHyp : ScArcCotHyp(); break; case ocCosecant : ScCosecant(); break; case ocSecant : ScSecant(); break; case ocCosecantHyp : ScCosecantHyp(); break; case ocSecantHyp : ScSecantHyp(); break; case ocExp : ScExp(); break; case ocLn : ScLn(); break; case ocLog10 : ScLog10(); break; case ocSqrt : ScSqrt(); break; case ocFact : ScFact(); break; case ocGetYear : ScGetYear(); break; case ocGetMonth : ScGetMonth(); break; case ocGetDay : ScGetDay(); break; case ocGetDayOfWeek : ScGetDayOfWeek(); break; case ocWeek : ScGetWeekOfYear(); break; case ocIsoWeeknum : ScGetIsoWeekOfYear(); break; case ocWeeknumOOo : ScWeeknumOOo(); break; case ocEasterSunday : ScEasterSunday(); break; case ocNetWorkdays : ScNetWorkdays( false); break; case ocNetWorkdays_MS : ScNetWorkdays( true ); break; case ocWorkday_MS : ScWorkday_MS(); break; case ocGetHour : ScGetHour(); break; case ocGetMin : ScGetMin(); break; case ocGetSec : ScGetSec(); break; case ocPlusMinus : ScPlusMinus(); break; case ocAbs : ScAbs(); break; case ocInt : ScInt(); break; case ocEven : ScEven(); break; case ocOdd : ScOdd(); break; case ocPhi : ScPhi(); break; case ocGauss : ScGauss(); break; case ocStdNormDist : ScStdNormDist(); break; case ocStdNormDist_MS : ScStdNormDist_MS(); break; case ocFisher : ScFisher(); break; case ocFisherInv : ScFisherInv(); break; case ocIsEmpty : ScIsEmpty(); break; case ocIsString : ScIsString(); break; case ocIsNonString : ScIsNonString(); break; case ocIsLogical : ScIsLogical(); break; case ocType : ScType(); break; case ocCell : ScCell(); break; case ocIsRef : ScIsRef(); break; case ocIsValue : ScIsValue(); break; case ocIsFormula : ScIsFormula(); break; case ocFormula : ScFormula(); break; case ocIsNA : ScIsNV(); break; case ocIsErr : ScIsErr(); break; case ocIsError : ScIsError(); break; case ocIsEven : ScIsEven(); break; case ocIsOdd : ScIsOdd(); break; case ocN : ScN(); break; case ocGetDateValue : ScGetDateValue(); break; case ocGetTimeValue : ScGetTimeValue(); break; case ocCode : ScCode(); break; case ocTrim : ScTrim(); break; case ocUpper : ScUpper(); break; case ocProper : ScProper(); break; case ocLower : ScLower(); break; case ocLen : ScLen(); break; case ocT : ScT(); break; case ocClean : ScClean(); break; case ocValue : ScValue(); break; case ocNumberValue : ScNumberValue(); break; case ocChar : ScChar(); break; case ocArcTan2 : ScArcTan2(); break; case ocMod : ScMod(); break; case ocPower : ScPower(); break; case ocRound : ScRound(); break; case ocRoundSig : ScRoundSignificant(); break; case ocRoundUp : ScRoundUp(); break; case ocTrunc : case ocRoundDown : ScRoundDown(); break; case ocCeil : ScCeil( true ); break; case ocCeil_MS : ScCeil_MS(); break; case ocCeil_Precise : case ocCeil_ISO : ScCeil_Precise(); break; case ocCeil_Math : ScCeil( false ); break; case ocFloor : ScFloor( true ); break; case ocFloor_MS : ScFloor_MS(); break; case ocFloor_Precise : ScFloor_Precise(); break; case ocFloor_Math : ScFloor( false ); break; case ocSumProduct : ScSumProduct(); break; case ocSumSQ : ScSumSQ(); break; case ocSumX2MY2 : ScSumX2MY2(); break; case ocSumX2DY2 : ScSumX2DY2(); break; case ocSumXMY2 : ScSumXMY2(); break; case ocRawSubtract : ScRawSubtract(); break; case ocLog : ScLog(); break; case ocGCD : ScGCD(); break; case ocLCM : ScLCM(); break; case ocGetDate : ScGetDate(); break; case ocGetTime : ScGetTime(); break; case ocGetDiffDate : ScGetDiffDate(); break; case ocGetDiffDate360 : ScGetDiffDate360(); break; case ocGetDateDif : ScGetDateDif(); break; case ocMin : ScMin() ; break; case ocMinA : ScMin( true ); break; case ocMax : ScMax(); break; case ocMaxA : ScMax( true ); break; case ocSum : ScSum(); break; case ocProduct : ScProduct(); break; case ocNPV : ScNPV(); break; case ocIRR : ScIRR(); break; case ocMIRR : ScMIRR(); break; case ocISPMT : ScISPMT(); break; case ocAverage : ScAverage() ; break; case ocAverageA : ScAverage( true ); break; case ocCount : ScCount(); break; case ocCount2 : ScCount2(); break; case ocVar : case ocVarS : ScVar(); break; case ocVarA : ScVar( true ); break; case ocVarP : case ocVarP_MS : ScVarP(); break; case ocVarPA : ScVarP( true ); break; case ocStDev : case ocStDevS : ScStDev(); break; case ocStDevA : ScStDev( true ); break; case ocStDevP : case ocStDevP_MS : ScStDevP(); break; case ocStDevPA : ScStDevP( true ); break; case ocPV : ScPV(); break; case ocSYD : ScSYD(); break; case ocDDB : ScDDB(); break; case ocDB : ScDB(); break; case ocVBD : ScVDB(); break; case ocPDuration : ScPDuration(); break; case ocSLN : ScSLN(); break; case ocPMT : ScPMT(); break; case ocColumns : ScColumns(); break; case ocRows : ScRows(); break; case ocSheets : ScSheets(); break; case ocColumn : ScColumn(); break; case ocRow : ScRow(); break; case ocSheet : ScSheet(); break; case ocRRI : ScRRI(); break; case ocFV : ScFV(); break; case ocNper : ScNper(); break; case ocRate : ScRate(); break; case ocFilterXML : ScFilterXML(); break; case ocWebservice : ScWebservice(); break; case ocEncodeURL : ScEncodeURL(); break; case ocColor : ScColor(); break; case ocErf_MS : ScErf(); break; case ocErfc_MS : ScErfc(); break; case ocIpmt : ScIpmt(); break; case ocPpmt : ScPpmt(); break; case ocCumIpmt : ScCumIpmt(); break; case ocCumPrinc : ScCumPrinc(); break; case ocEffect : ScEffect(); break; case ocNominal : ScNominal(); break; case ocSubTotal : ScSubTotal(); break; case ocAggregate : ScAggregate(); break; case ocDBSum : ScDBSum(); break; case ocDBCount : ScDBCount(); break; case ocDBCount2 : ScDBCount2(); break; case ocDBAverage : ScDBAverage(); break; case ocDBGet : ScDBGet(); break; case ocDBMax : ScDBMax(); break; case ocDBMin : ScDBMin(); break; case ocDBProduct : ScDBProduct(); break; case ocDBStdDev : ScDBStdDev(); break; case ocDBStdDevP : ScDBStdDevP(); break; case ocDBVar : ScDBVar(); break; case ocDBVarP : ScDBVarP(); break; case ocIndirect : ScIndirect(); break; case ocAddress : ScAddressFunc(); break; case ocMatch : ScMatch(); break; case ocCountEmptyCells : ScCountEmptyCells(); break; case ocCountIf : ScCountIf(); break; case ocSumIf : ScSumIf(); break; case ocAverageIf : ScAverageIf(); break; case ocSumIfs : ScSumIfs(); break; case ocAverageIfs : ScAverageIfs(); break; case ocCountIfs : ScCountIfs(); break; case ocLookup : ScLookup(); break; case ocVLookup : ScVLookup(); break; case ocHLookup : ScHLookup(); break; case ocIndex : ScIndex(); break; case ocMultiArea : ScMultiArea(); break; case ocOffset : ScOffset(); break; case ocAreas : ScAreas(); break; case ocCurrency : ScCurrency(); break; case ocReplace : ScReplace(); break; case ocFixed : ScFixed(); break; case ocFind : ScFind(); break; case ocExact : ScExact(); break; case ocLeft : ScLeft(); break; case ocRight : ScRight(); break; case ocSearch : ScSearch(); break; case ocMid : ScMid(); break; case ocText : ScText(); break; case ocSubstitute : ScSubstitute(); break; case ocRegex : ScRegex(); break; case ocRept : ScRept(); break; case ocConcat : ScConcat(); break; case ocConcat_MS : ScConcat_MS(); break; case ocTextJoin_MS : ScTextJoin_MS(); break; case ocIfs_MS : ScIfs_MS(); break; case ocSwitch_MS : ScSwitch_MS(); break; case ocMinIfs_MS : ScMinIfs_MS(); break; case ocMaxIfs_MS : ScMaxIfs_MS(); break; case ocMatValue : ScMatValue(); break; case ocMatrixUnit : ScEMat(); break; case ocMatDet : ScMatDet(); break; case ocMatInv : ScMatInv(); break; case ocMatMult : ScMatMult(); break; case ocMatTrans : ScMatTrans(); break; case ocMatRef : ScMatRef(); break; case ocB : ScB(); break; case ocNormDist : ScNormDist( 3 ); break; case ocNormDist_MS : ScNormDist( 4 ); break; case ocExpDist : case ocExpDist_MS : ScExpDist(); break; case ocBinomDist : case ocBinomDist_MS : ScBinomDist(); break; case ocPoissonDist : ScPoissonDist( true ); break; case ocPoissonDist_MS : ScPoissonDist( false ); break; case ocCombin : ScCombin(); break; case ocCombinA : ScCombinA(); break; case ocPermut : ScPermut(); break; case ocPermutationA : ScPermutationA(); break; case ocHypGeomDist : ScHypGeomDist( 4 ); break; case ocHypGeomDist_MS : ScHypGeomDist( 5 ); break; case ocLogNormDist : ScLogNormDist( 1 ); break; case ocLogNormDist_MS : ScLogNormDist( 4 ); break; case ocTDist : ScTDist(); break; case ocTDist_MS : ScTDist_MS(); break; case ocTDist_RT : ScTDist_T( 1 ); break; case ocTDist_2T : ScTDist_T( 2 ); break; case ocFDist : case ocFDist_RT : ScFDist(); break; case ocFDist_LT : ScFDist_LT(); break; case ocChiDist : ScChiDist( true ); break; case ocChiDist_MS : ScChiDist( false ); break; case ocChiSqDist : ScChiSqDist(); break; case ocChiSqDist_MS : ScChiSqDist_MS(); break; case ocStandard : ScStandard(); break; case ocAveDev : ScAveDev(); break; case ocDevSq : ScDevSq(); break; case ocKurt : ScKurt(); break; case ocSkew : ScSkew(); break; case ocSkewp : ScSkewp(); break; case ocModalValue : ScModalValue(); break; case ocModalValue_MS : ScModalValue_MS( true ); break; case ocModalValue_Multi : ScModalValue_MS( false ); break; case ocMedian : ScMedian(); break; case ocGeoMean : ScGeoMean(); break; case ocHarMean : ScHarMean(); break; case ocWeibull : case ocWeibull_MS : ScWeibull(); break; case ocBinomInv : case ocCritBinom : ScCritBinom(); break; case ocNegBinomVert : ScNegBinomDist(); break; case ocNegBinomDist_MS : ScNegBinomDist_MS(); break; case ocNoName : ScNoName(); break; case ocBad : ScBadName(); break; case ocZTest : case ocZTest_MS : ScZTest(); break; case ocTTest : case ocTTest_MS : ScTTest(); break; case ocFTest : case ocFTest_MS : ScFTest(); break; case ocRank : case ocRank_Eq : ScRank( false ); break; case ocRank_Avg : ScRank( true ); break; case ocPercentile : case ocPercentile_Inc : ScPercentile( true ); break; case ocPercentile_Exc : ScPercentile( false ); break; case ocPercentrank : case ocPercentrank_Inc : ScPercentrank( true ); break; case ocPercentrank_Exc : ScPercentrank( false ); break; case ocLarge : ScLarge(); break; case ocSmall : ScSmall(); break; case ocFrequency : ScFrequency(); break; case ocQuartile : case ocQuartile_Inc : ScQuartile( true ); break; case ocQuartile_Exc : ScQuartile( false ); break; case ocNormInv : case ocNormInv_MS : ScNormInv(); break; case ocSNormInv : case ocSNormInv_MS : ScSNormInv(); break; case ocConfidence : case ocConfidence_N : ScConfidence(); break; case ocConfidence_T : ScConfidenceT(); break; case ocTrimMean : ScTrimMean(); break; case ocProb : ScProbability(); break; case ocCorrel : ScCorrel(); break; case ocCovar : case ocCovarianceP : ScCovarianceP(); break; case ocCovarianceS : ScCovarianceS(); break; case ocPearson : ScPearson(); break; case ocRSQ : ScRSQ(); break; case ocSTEYX : ScSTEYX(); break; case ocSlope : ScSlope(); break; case ocIntercept : ScIntercept(); break; case ocTrend : ScTrend(); break; case ocGrowth : ScGrowth(); break; case ocLinest : ScLinest(); break; case ocLogest : ScLogest(); break; case ocForecast_LIN : case ocForecast : ScForecast(); break; case ocForecast_ETS_ADD : ScForecast_Ets( etsAdd ); break; case ocForecast_ETS_SEA : ScForecast_Ets( etsSeason ); break; case ocForecast_ETS_MUL : ScForecast_Ets( etsMult ); break; case ocForecast_ETS_PIA : ScForecast_Ets( etsPIAdd ); break; case ocForecast_ETS_PIM : ScForecast_Ets( etsPIMult ); break; case ocForecast_ETS_STA : ScForecast_Ets( etsStatAdd ); break; case ocForecast_ETS_STM : ScForecast_Ets( etsStatMult ); break; case ocGammaLn : case ocGammaLn_MS : ScLogGamma(); break; case ocGamma : ScGamma(); break; case ocGammaDist : ScGammaDist( true ); break; case ocGammaDist_MS : ScGammaDist( false ); break; case ocGammaInv : case ocGammaInv_MS : ScGammaInv(); break; case ocChiTest : case ocChiTest_MS : ScChiTest(); break; case ocChiInv : case ocChiInv_MS : ScChiInv(); break; case ocChiSqInv : case ocChiSqInv_MS : ScChiSqInv(); break; case ocTInv : case ocTInv_2T : ScTInv( 2 ); break; case ocTInv_MS : ScTInv( 4 ); break; case ocFInv : case ocFInv_RT : ScFInv(); break; case ocFInv_LT : ScFInv_LT(); break; case ocLogInv : case ocLogInv_MS : ScLogNormInv(); break; case ocBetaDist : ScBetaDist(); break; case ocBetaDist_MS : ScBetaDist_MS(); break; case ocBetaInv : case ocBetaInv_MS : ScBetaInv(); break; case ocFourier : ScFourier(); break; case ocExternal : ScExternal(); break; case ocTableOp : ScTableOp(); break; case ocStop : break; case ocErrorType : ScErrorType(); break; case ocErrorType_ODF : ScErrorType_ODF(); break; case ocCurrent : ScCurrent(); break; case ocStyle : ScStyle(); break; case ocDde : ScDde(); break; case ocBase : ScBase(); break; case ocDecimal : ScDecimal(); break; case ocConvertOOo : ScConvertOOo(); break; case ocEuroConvert : ScEuroConvert(); break; case ocRoman : ScRoman(); break; case ocArabic : ScArabic(); break; case ocInfo : ScInfo(); break; case ocHyperLink : ScHyperLink(); break; case ocBahtText : ScBahtText(); break; case ocGetPivotData : ScGetPivotData(); break; case ocJis : ScJis(); break; case ocAsc : ScAsc(); break; case ocLenB : ScLenB(); break; case ocRightB : ScRightB(); break; case ocLeftB : ScLeftB(); break; case ocMidB : ScMidB(); break; case ocReplaceB : ScReplaceB(); break; case ocFindB : ScFindB(); break; case ocSearchB : ScSearchB(); break; case ocUnicode : ScUnicode(); break; case ocUnichar : ScUnichar(); break; case ocBitAnd : ScBitAnd(); break; case ocBitOr : ScBitOr(); break; case ocBitXor : ScBitXor(); break; case ocBitRshift : ScBitRshift(); break; case ocBitLshift : ScBitLshift(); break; case ocTTT : ScTTT(); break; case ocDebugVar : ScDebugVar(); break; case ocNone : nFuncFmtType = SvNumFormatType::UNDEFINED; break; default : PushError( FormulaError::UnknownOpCode); break; } // If the function pushed a subroutine as result, continue with // execution of the subroutine. if (sp > nStackBase && pStack[sp-1]->GetOpCode() == ocCall) { Pop(); continue; } if (FormulaCompiler::IsOpCodeVolatile(eOp)) meVolatileType = VOLATILE; // Remember result matrix in case it could be reused. if (pTokenMatrixMap && sp && GetStackType() == svMatrix) pTokenMatrixMap->emplace(pCur, pStack[sp-1]); // outer function determines format of an expression if ( nFuncFmtType != SvNumFormatType::UNDEFINED ) { nRetTypeExpr = nFuncFmtType; // Inherit the format index for currency, date or time formats. switch (nFuncFmtType) { case SvNumFormatType::CURRENCY: case SvNumFormatType::DATE: case SvNumFormatType::TIME: case SvNumFormatType::DATETIME: case SvNumFormatType::DURATION: nRetIndexExpr = nFuncFmtIndex; break; default: nRetIndexExpr = 0; } } } // Need a clean stack environment for the JumpMatrix to work. if (nGlobalError != FormulaError::NONE && eOp != ocPush && sp > nStackBase + 1) { // Not all functions pop all parameters in case an error is // generated. Clean up stack. Assumes that every function pushes a // result, may be arbitrary in case of error. FormulaConstTokenRef xLocalResult = pStack[ sp - 1 ]; while (sp > nStackBase) Pop(); PushTokenRef( xLocalResult ); } bool bGotResult; do { bGotResult = false; sal_uInt8 nLevel = 0; if ( GetStackType( ++nLevel ) == svJumpMatrix ) ; // nothing else if ( GetStackType( ++nLevel ) == svJumpMatrix ) ; // nothing else nLevel = 0; if ( nLevel == 1 || (nLevel == 2 && aCode.IsEndOfPath()) ) { if (nLevel == 1) aErrorFunctionStack.push_back( nErrorFunction); bGotResult = JumpMatrix( nLevel ); if (aErrorFunctionStack.empty()) assert(!"ScInterpreter::Interpret - aErrorFunctionStack empty in JumpMatrix context"); else { nErrorFunction = aErrorFunctionStack.back(); if (bGotResult) aErrorFunctionStack.pop_back(); } } else pJumpMatrix = nullptr; } while ( bGotResult ); if( IsErrFunc(eOp) ) ++nErrorFunction; if ( nGlobalError != FormulaError::NONE ) { if ( !nErrorFunctionCount ) { // count of errorcode functions in formula FormulaTokenArrayPlainIterator aIter(*pArr); for ( FormulaToken* t = aIter.FirstRPN(); t; t = aIter.NextRPN() ) { if ( IsErrFunc(t->GetOpCode()) ) ++nErrorFunctionCount; } } if ( nErrorFunction >= nErrorFunctionCount ) ++nErrorFunction; // that's it, error => terminate } } // End: obtain result bool bForcedResultType; switch (eOp) { case ocGetDateValue: case ocGetTimeValue: // Force final result of DATEVALUE and TIMEVALUE to number type, // which so far was date or time for calculations. nRetTypeExpr = nFuncFmtType = SvNumFormatType::NUMBER; nRetIndexExpr = nFuncFmtIndex = 0; bForcedResultType = true; break; default: bForcedResultType = false; } if (sp == 1) { pCur = pStack[ sp-1 ]; if( pCur->GetOpCode() == ocPush ) { // An svRefList can be resolved if it a) contains just one // reference, or b) in array context contains an array of single // cell references. if (pCur->GetType() == svRefList) { PopRefListPushMatrixOrRef(); pCur = pStack[ sp-1 ]; } switch( pCur->GetType() ) { case svEmptyCell: ; // nothing break; case svError: nGlobalError = pCur->GetError(); break; case svDouble : { // If typed, pop token to obtain type information and // push a plain untyped double so the result token to // be transferred to the formula cell result does not // unnecessarily duplicate the information. if (pCur->GetDoubleType() != 0) { double fVal = PopDouble(); if (!bForcedResultType) { if (nCurFmtType != nFuncFmtType) nRetIndexExpr = 0; // carry format index only for matching type nRetTypeExpr = nFuncFmtType = nCurFmtType; } if (nRetTypeExpr == SvNumFormatType::DURATION) { // Round the duration in case a wall clock time // display format is used instead of a duration // format. To micro seconds which then catches // the converted hh:mm:ss.9999997 cases. if (fVal != 0.0) { fVal *= 86400.0; fVal = rtl::math::round( fVal, 6); fVal /= 86400.0; } } PushTempToken( CreateFormulaDoubleToken( fVal)); } if ( nFuncFmtType == SvNumFormatType::UNDEFINED ) { nRetTypeExpr = SvNumFormatType::NUMBER; nRetIndexExpr = 0; } } break; case svString : nRetTypeExpr = SvNumFormatType::TEXT; nRetIndexExpr = 0; break; case svSingleRef : { ScAddress aAdr; PopSingleRef( aAdr ); if( nGlobalError == FormulaError::NONE) PushCellResultToken( false, aAdr, &nRetTypeExpr, &nRetIndexExpr, true); } break; case svRefList : PopError(); // maybe #REF! takes precedence over #VALUE! PushError( FormulaError::NoValue); break; case svDoubleRef : { if ( bMatrixFormula ) { // create matrix for {=A1:A5} PopDoubleRefPushMatrix(); ScMatrixRef xMat = PopMatrix(); QueryMatrixType(xMat, nRetTypeExpr, nRetIndexExpr); } else { ScRange aRange; PopDoubleRef( aRange ); ScAddress aAdr; if ( nGlobalError == FormulaError::NONE && DoubleRefToPosSingleRef( aRange, aAdr)) PushCellResultToken( false, aAdr, &nRetTypeExpr, &nRetIndexExpr, true); } } break; case svExternalDoubleRef: { ScMatrixRef xMat; PopExternalDoubleRef(xMat); QueryMatrixType(xMat, nRetTypeExpr, nRetIndexExpr); } break; case svMatrix : { sc::RangeMatrix aMat = PopRangeMatrix(); if (aMat.isRangeValid()) { // This matrix represents a range reference. Apply implicit intersection. double fVal = applyImplicitIntersection(aMat, aPos); if (std::isnan(fVal)) PushNoValue(); else PushInt(fVal); } else // This is a normal matrix. QueryMatrixType(aMat.mpMat, nRetTypeExpr, nRetIndexExpr); } break; case svExternalSingleRef: { FormulaTokenRef xToken; ScExternalRefCache::CellFormat aFmt; PopExternalSingleRef(xToken, &aFmt); if (nGlobalError != FormulaError::NONE) break; PushTokenRef(xToken); if (aFmt.mbIsSet) { nFuncFmtType = aFmt.mnType; nFuncFmtIndex = aFmt.mnIndex; } } break; default : SetError( FormulaError::UnknownStackVariable); } } else SetError( FormulaError::UnknownStackVariable); } else if (sp > 1) SetError( FormulaError::OperatorExpected); else SetError( FormulaError::NoCode); if (bForcedResultType || nRetTypeExpr != SvNumFormatType::UNDEFINED) { nRetFmtType = nRetTypeExpr; nRetFmtIndex = nRetIndexExpr; } else if( nFuncFmtType != SvNumFormatType::UNDEFINED ) { nRetFmtType = nFuncFmtType; nRetFmtIndex = nFuncFmtIndex; } else nRetFmtType = SvNumFormatType::NUMBER; if (nGlobalError != FormulaError::NONE && GetStackType() != svError ) PushError( nGlobalError); // THE final result. xResult = PopToken(); if (!xResult) xResult = new FormulaErrorToken( FormulaError::UnknownStackVariable); // release tokens in expression stack const FormulaToken** p = pStack; while( maxsp-- ) (*p++)->DecRef(); StackVar eType = xResult->GetType(); if (eType == svMatrix) // Results are immutable in case they would be reused as input for new // interpreters. xResult->GetMatrix()->SetImmutable(); return eType; } void ScInterpreter::AssertFormulaMatrix() { bMatrixFormula = true; } const svl::SharedString & ScInterpreter::GetStringResult() const { return xResult->GetString(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */