1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
23 #include <string_view>
25 #include <osl/diagnose.h>
26 #include <o3tl/float_int_conversion.hxx>
27 #include <o3tl/safeint.hxx>
28 #include <tools/debug.hxx>
29 #include <tools/stream.hxx>
30 #include <sal/log.hxx>
32 #include <basic/sbx.hxx>
33 #include <sbunoobj.hxx>
34 #include "sbxconv.hxx"
35 #include <runtime.hxx>
36 #include <filefmt.hxx>
39 ///////////////////////////// constructors
43 aData
.eType
= SbxEMPTY
;
46 SbxValue::SbxValue( SbxDataType t
)
53 SetFlag( SbxFlagBits::Fixed
);
54 aData
.clear(SbxDataType( n
));
57 SbxValue::SbxValue( const SbxValue
& r
)
58 : SvRefBase( r
), SbxBase( r
)
62 SetError( ERRCODE_BASIC_PROP_WRITEONLY
);
64 aData
.eType
= SbxNULL
;
68 const_cast<SbxValue
*>(&r
)->Broadcast( SfxHintId::BasicDataWanted
);
70 // Copy pointer, increment references
75 aData
.pOUString
= new OUString( *aData
.pOUString
);
79 aData
.pObj
->AddFirstRef();
83 aData
.pDecimal
->addRef();
90 SbxValue
& SbxValue::operator=( const SbxValue
& r
)
95 SetError( ERRCODE_BASIC_PROP_READONLY
);
98 // string -> byte array
99 if( IsFixed() && (aData
.eType
== SbxOBJECT
)
100 && aData
.pObj
&& ( aData
.pObj
->GetType() == (SbxARRAY
| SbxBYTE
) )
101 && (r
.aData
.eType
== SbxSTRING
) )
103 OUString aStr
= r
.GetOUString();
104 SbxArray
* pArr
= StringToByteArray(aStr
);
108 // byte array -> string
109 if( r
.IsFixed() && (r
.aData
.eType
== SbxOBJECT
)
110 && r
.aData
.pObj
&& ( r
.aData
.pObj
->GetType() == (SbxARRAY
| SbxBYTE
) )
111 && (aData
.eType
== SbxSTRING
) )
113 SbxBase
* pObj
= r
.GetObject();
114 SbxArray
* pArr
= dynamic_cast<SbxArray
*>( pObj
);
117 OUString aStr
= ByteArrayToString( pArr
);
122 // Readout the content of the variables
125 // then the type has to match
126 aNew
.eType
= aData
.eType
;
127 else if( r
.IsFixed() )
128 // Source fixed: copy the type
129 aNew
.eType
= SbxDataType( r
.aData
.eType
& 0x0FFF );
131 // both variant: then don't care
132 aNew
.eType
= SbxVARIANT
;
140 SbxValue::~SbxValue()
142 SetFlag( SbxFlagBits::Write
);
143 // cid#1486004 silence Uncaught exception
144 suppress_fun_call_w_exception(SbxValue::Clear());
147 void SbxValue::Clear()
149 switch( aData
.eType
)
156 delete aData
.pOUString
; aData
.pOUString
= nullptr;
161 if( aData
.pObj
!= this )
163 SAL_INFO("basic.sbx", "Not at Parent-Prop - otherwise CyclicRef");
164 SbxVariable
*pThisVar
= dynamic_cast<SbxVariable
*>( this );
165 bool bParentProp
= pThisVar
&& (pThisVar
->GetUserData() & 0xFFFF) == 5345;
167 aData
.pObj
->ReleaseRef();
169 aData
.pObj
= nullptr;
173 releaseDecimalPtr( aData
.pDecimal
);
176 aData
.pData
= nullptr; break;
180 aEmpty
.clear(GetType());
188 void SbxValue::Broadcast( SfxHintId
)
191 //////////////////////////// Readout data
193 // Detect the "right" variables. If it is an object, will be addressed either
194 // the object itself or its default property.
195 // If the variable contain a variable or an object, this will be
198 SbxValue
* SbxValue::TheRealValue( bool bObjInObjError
) const
200 SbxValue
* p
= const_cast<SbxValue
*>(this);
203 SbxDataType t
= SbxDataType( p
->aData
.eType
& 0x0FFF );
206 // The block contains an object or a variable
207 SbxObject
* pObj
= dynamic_cast<SbxObject
*>( p
->aData
.pObj
);
210 // Has the object a default property?
211 SbxVariable
* pDflt
= pObj
->GetDfltProperty();
213 // If this is an object and contains itself,
214 // we cannot access on it
215 // The old condition to set an error is not correct,
216 // because e.g. a regular variant variable with an object
217 // could be affected if another value should be assigned.
218 // Therefore with flag.
219 if( bObjInObjError
&& !pDflt
&&
220 static_cast<SbxValue
*>(pObj
)->aData
.eType
== SbxOBJECT
&&
221 static_cast<SbxValue
*>(pObj
)->aData
.pObj
== pObj
)
223 #if !HAVE_FEATURE_SCRIPTING
224 const bool bSuccess
= false;
226 bool bSuccess
= handleToStringForCOMObjects( pObj
, p
);
230 SetError( ERRCODE_BASIC_BAD_PROP_VALUE
);
238 // Did we have an array?
239 SbxArray
* pArray
= dynamic_cast<SbxArray
*>( p
->aData
.pObj
);
242 // When indicated get the parameter
243 SbxArray
* pPar
= nullptr;
244 SbxVariable
* pVar
= dynamic_cast<SbxVariable
*>( p
);
246 pPar
= pVar
->GetParameters();
249 // Did we have a dimensioned array?
250 SbxDimArray
* pDimArray
= dynamic_cast<SbxDimArray
*>( p
->aData
.pObj
);
252 p
= pDimArray
->Get( pPar
);
254 p
= pArray
->Get(pPar
->Get(1)->GetInteger());
258 // Otherwise guess a SbxValue
259 SbxValue
* pVal
= dynamic_cast<SbxValue
*>( p
->aData
.pObj
);
271 bool SbxValue::Get( SbxValues
& rRes
) const
274 ErrCode eOld
= GetError();
275 if( eOld
!= ERRCODE_NONE
)
279 SetError( ERRCODE_BASIC_PROP_WRITEONLY
);
284 // If an object or a VARIANT is requested, don't search the real values
285 SbxValue
* p
= const_cast<SbxValue
*>(this);
286 if( rRes
.eType
!= SbxOBJECT
&& rRes
.eType
!= SbxVARIANT
)
287 p
= TheRealValue( true );
290 p
->Broadcast( SfxHintId::BasicDataWanted
);
296 case SbxVARIANT
: rRes
= p
->aData
; break;
297 case SbxINTEGER
: rRes
.nInteger
= ImpGetInteger( &p
->aData
); break;
298 case SbxLONG
: rRes
.nLong
= ImpGetLong( &p
->aData
); break;
299 case SbxSALINT64
: rRes
.nInt64
= ImpGetInt64( &p
->aData
); break;
300 case SbxSALUINT64
: rRes
.uInt64
= ImpGetUInt64( &p
->aData
); break;
301 case SbxSINGLE
: rRes
.nSingle
= ImpGetSingle( &p
->aData
); break;
302 case SbxDOUBLE
: rRes
.nDouble
= ImpGetDouble( &p
->aData
); break;
303 case SbxCURRENCY
:rRes
.nInt64
= ImpGetCurrency( &p
->aData
); break;
304 case SbxDECIMAL
: rRes
.pDecimal
= ImpGetDecimal( &p
->aData
); break;
305 case SbxDATE
: rRes
.nDouble
= ImpGetDate( &p
->aData
); break;
307 rRes
.nUShort
= sal::static_int_cast
< sal_uInt16
>(
308 ImpGetBool( &p
->aData
) );
310 case SbxCHAR
: rRes
.nChar
= ImpGetChar( &p
->aData
); break;
311 case SbxBYTE
: rRes
.nByte
= ImpGetByte( &p
->aData
); break;
312 case SbxUSHORT
: rRes
.nUShort
= ImpGetUShort( &p
->aData
); break;
313 case SbxULONG
: rRes
.nULong
= ImpGetULong( &p
->aData
); break;
315 case SbxSTRING
: p
->aPic
= ImpGetString( &p
->aData
);
316 rRes
.pOUString
= &p
->aPic
; break;
317 case SbxCoreSTRING
: p
->aPic
= ImpGetCoreString( &p
->aData
);
318 rRes
.pOUString
= &p
->aPic
; break;
320 rRes
.nInt
= static_cast<int>(ImpGetLong( &p
->aData
));
323 rRes
.nUInt
= static_cast<int>(ImpGetULong( &p
->aData
));
326 if( p
->aData
.eType
== SbxOBJECT
)
327 rRes
.pObj
= p
->aData
.pObj
;
330 SetError( ERRCODE_BASIC_NO_OBJECT
);
335 if( p
->aData
.eType
== rRes
.eType
)
339 SetError( ERRCODE_BASIC_CONVERSION
);
346 // Object contained itself
347 SbxDataType eTemp
= rRes
.eType
;
354 if( eOld
!= ERRCODE_NONE
)
360 SbxValues
SbxValue::Get(SbxDataType t
) const
367 const OUString
& SbxValue::GetCoreString() const
369 SbxValues
aRes(SbxCoreSTRING
);
372 const_cast<SbxValue
*>(this)->aToolString
= *aRes
.pOUString
;
376 const_cast<SbxValue
*>(this)->aToolString
.clear();
381 OUString
SbxValue::GetOUString() const
384 SbxValues
aRes(SbxSTRING
);
387 aResult
= *aRes
.pOUString
;
392 //////////////////////////// Write data
394 bool SbxValue::Put( const SbxValues
& rVal
)
397 ErrCode eOld
= GetError();
398 if( eOld
!= ERRCODE_NONE
)
401 SetError( ERRCODE_BASIC_PROP_READONLY
);
402 else if( rVal
.eType
& 0xF000 )
403 SetError( ERRCODE_BASIC_BAD_ARGUMENT
);
406 // If an object is requested, don't search the real values
408 if( rVal
.eType
!= SbxOBJECT
)
409 p
= TheRealValue( false ); // Don't allow an error here
413 SetError( ERRCODE_BASIC_PROP_READONLY
);
414 else if( p
->IsFixed() || p
->SetType( static_cast<SbxDataType
>( rVal
.eType
& 0x0FFF ) ) )
415 switch( rVal
.eType
& 0x0FFF )
420 case SbxINTEGER
: ImpPutInteger( &p
->aData
, rVal
.nInteger
); break;
421 case SbxLONG
: ImpPutLong( &p
->aData
, rVal
.nLong
); break;
422 case SbxSALINT64
: ImpPutInt64( &p
->aData
, rVal
.nInt64
); break;
423 case SbxSALUINT64
: ImpPutUInt64( &p
->aData
, rVal
.uInt64
); break;
424 case SbxSINGLE
: ImpPutSingle( &p
->aData
, rVal
.nSingle
); break;
425 case SbxDOUBLE
: ImpPutDouble( &p
->aData
, rVal
.nDouble
); break;
426 case SbxCURRENCY
: ImpPutCurrency( &p
->aData
, rVal
.nInt64
); break;
427 case SbxDECIMAL
: ImpPutDecimal( &p
->aData
, rVal
.pDecimal
); break;
428 case SbxDATE
: ImpPutDate( &p
->aData
, rVal
.nDouble
); break;
429 case SbxBOOL
: ImpPutBool( &p
->aData
, rVal
.nInteger
); break;
430 case SbxCHAR
: ImpPutChar( &p
->aData
, rVal
.nChar
); break;
431 case SbxBYTE
: ImpPutByte( &p
->aData
, rVal
.nByte
); break;
432 case SbxUSHORT
: ImpPutUShort( &p
->aData
, rVal
.nUShort
); break;
433 case SbxULONG
: ImpPutULong( &p
->aData
, rVal
.nULong
); break;
435 case SbxSTRING
: ImpPutString( &p
->aData
, rVal
.pOUString
); break;
437 ImpPutLong( &p
->aData
, static_cast<sal_Int32
>(rVal
.nInt
) );
440 ImpPutULong( &p
->aData
, static_cast<sal_uInt32
>(rVal
.nUInt
) );
443 if( !p
->IsFixed() || p
->aData
.eType
== SbxOBJECT
)
446 if( p
->aData
.eType
== SbxOBJECT
&& p
->aData
.pObj
== rVal
.pObj
)
449 // Delete only the value part!
450 p
->SbxValue::Clear();
453 p
->aData
.pObj
= rVal
.pObj
;
455 // if necessary increment Ref-Count
456 if( p
->aData
.pObj
&& p
->aData
.pObj
!= p
)
460 OSL_FAIL( "TheRealValue" );
462 SAL_INFO("basic.sbx", "Not at Parent-Prop - otherwise CyclicRef");
463 SbxVariable
*pThisVar
= dynamic_cast<SbxVariable
*>( this );
464 bool bParentProp
= pThisVar
&& (pThisVar
->GetUserData() & 0xFFFF) == 5345;
466 p
->aData
.pObj
->AddFirstRef();
470 SetError( ERRCODE_BASIC_CONVERSION
);
473 if( p
->aData
.eType
== rVal
.eType
)
477 SetError( ERRCODE_BASIC_CONVERSION
);
479 p
->aData
.eType
= SbxNULL
;
484 p
->SetModified( true );
485 p
->Broadcast( SfxHintId::BasicDataChanged
);
486 if( eOld
!= ERRCODE_NONE
)
495 // with advanced evaluation (International, "TRUE"/"FALSE")
496 static OUString
ImpConvStringExt(const OUString
& rSrc
, SbxDataType eTargetType
)
498 // only special cases are handled, nothing on default
501 // Consider international for floating point. Following default conversion (SbxValue::Put)
502 // assumes internationalized strings, but the input may use standard decimal dot.
507 sal_Unicode cDecimalSep
, cThousandSep
, cDecimalSepAlt
;
508 ImpGetIntntlSep(cDecimalSep
, cThousandSep
, cDecimalSepAlt
);
510 // 1. If any of the returned decimal separators is dot, do nothing
511 if (cDecimalSep
== '.' || cDecimalSepAlt
== '.')
514 // 2. If there are internationalized separators already, do nothing
515 if (rSrc
.indexOf(cDecimalSep
) >= 0 || rSrc
.indexOf(cDecimalSepAlt
) >= 0)
518 // 3. Replace all dots with the primary separator. This resolves possible ambiguity with
519 // dot as thousand separator, in favor of decimal dot; unlike "only change one dot"
520 // approach, this prevents inconsistency like converting "234.567" to a number with
521 // floating point 234.567, while "1.234.567" to a whole number 1234567. The latter will
523 return rSrc
.replaceAll(".", OUStringChar(cDecimalSep
));
526 // check as string in case of sal_Bool sal_True and sal_False
528 if (rSrc
.equalsIgnoreAsciiCase("true"))
529 return OUString::number(SbxTRUE
);
530 if (rSrc
.equalsIgnoreAsciiCase("false"))
531 return OUString::number(SbxFALSE
);
542 // Method to execute a pretreatment of the strings at special types.
543 // In particular necessary for BASIC-IDE, so that
544 // the output in the Watch-Window can be written back with PutStringExt,
545 // if Float were declared with either '.' or locale-specific decimal
546 // separator, or BOOl explicit with "TRUE" or "FALSE".
547 // Implementation in ImpConvStringExt
548 void SbxValue::PutStringExt( const OUString
& r
)
550 // Identify the own type (not as in Put() with TheRealValue(),
551 // Objects are not handled anyway)
552 SbxDataType eTargetType
= SbxDataType( aData
.eType
& 0x0FFF );
553 OUString
aStr(ImpConvStringExt(r
, eTargetType
));
555 // tinker a Source-Value
556 SbxValues
aRes(SbxSTRING
);
557 aRes
.pOUString
= &aStr
;
559 // #34939: For Strings which contain a number, and if this has a Num-Type,
560 // set a Fixed flag so that the type will not be changed
561 SbxFlagBits nFlags_
= GetFlags();
562 if( ( eTargetType
>= SbxINTEGER
&& eTargetType
<= SbxCURRENCY
) ||
563 ( eTargetType
>= SbxCHAR
&& eTargetType
<= SbxUINT
) ||
564 eTargetType
== SbxBOOL
)
568 if( aVal
.IsNumeric() )
569 SetFlag( SbxFlagBits::Fixed
);
572 const bool bRet
= Put(aRes
);
574 // If FIXED resulted in an error, set it back
575 // (UI-Action should not result in an error, but simply fail)
582 bool SbxValue::PutBool( bool b
)
584 SbxValues
aRes(SbxBOOL
);
585 aRes
.nUShort
= sal::static_int_cast
< sal_uInt16
>(b
? SbxTRUE
: SbxFALSE
);
589 bool SbxValue::PutEmpty()
591 bool bRet
= SetType( SbxEMPTY
);
596 void SbxValue::PutNull()
598 bool bRet
= SetType( SbxNULL
);
604 // Special decimal methods
605 void SbxValue::PutDecimal( css::bridge::oleautomation::Decimal
const & rAutomationDec
)
608 aData
.pDecimal
= new SbxDecimal( rAutomationDec
);
609 aData
.pDecimal
->addRef();
610 aData
.eType
= SbxDECIMAL
;
613 void SbxValue::fillAutomationDecimal
614 ( css::bridge::oleautomation::Decimal
& rAutomationDec
) const
616 SbxDecimal
* pDecimal
= GetDecimal();
617 if( pDecimal
!= nullptr )
619 pDecimal
->fillAutomationDecimal( rAutomationDec
);
624 bool SbxValue::PutString( const OUString
& r
)
626 SbxValues
aRes(SbxSTRING
);
627 aRes
.pOUString
= const_cast<OUString
*>(&r
);
632 #define PUT( p, e, t, m ) \
633 bool SbxValue::p( t n ) \
634 { SbxValues aRes(e); aRes.m = n; return Put(aRes); }
636 void SbxValue::PutDate( double n
)
637 { SbxValues
aRes(SbxDATE
); aRes
.nDouble
= n
; Put( aRes
); }
638 void SbxValue::PutErr( sal_uInt16 n
)
639 { SbxValues
aRes(SbxERROR
); aRes
.nUShort
= n
; Put( aRes
); }
641 PUT( PutByte
, SbxBYTE
, sal_uInt8
, nByte
)
642 PUT( PutChar
, SbxCHAR
, sal_Unicode
, nChar
)
643 PUT( PutCurrency
, SbxCURRENCY
, sal_Int64
, nInt64
)
644 PUT( PutDouble
, SbxDOUBLE
, double, nDouble
)
645 PUT( PutInteger
, SbxINTEGER
, sal_Int16
, nInteger
)
646 PUT( PutLong
, SbxLONG
, sal_Int32
, nLong
)
647 PUT( PutObject
, SbxOBJECT
, SbxBase
*, pObj
)
648 PUT( PutSingle
, SbxSINGLE
, float, nSingle
)
649 PUT( PutULong
, SbxULONG
, sal_uInt32
, nULong
)
650 PUT( PutUShort
, SbxUSHORT
, sal_uInt16
, nUShort
)
651 PUT( PutInt64
, SbxSALINT64
, sal_Int64
, nInt64
)
652 PUT( PutUInt64
, SbxSALUINT64
, sal_uInt64
, uInt64
)
653 PUT( PutDecimal
, SbxDECIMAL
, SbxDecimal
*, pDecimal
)
655 ////////////////////////// Setting of the data type
657 bool SbxValue::IsFixed() const
659 return (GetFlags() & SbxFlagBits::Fixed
) || ((aData
.eType
& SbxBYREF
) != 0);
662 // A variable is numeric, if it is EMPTY or really numeric
663 // or if it contains a complete convertible String
665 // #41692, implement it for RTL and Basic-Core separately
666 bool SbxValue::IsNumeric() const
668 return ImpIsNumeric( /*bOnlyIntntl*/false );
671 bool SbxValue::IsNumericRTL() const
673 return ImpIsNumeric( /*bOnlyIntntl*/true );
676 bool SbxValue::ImpIsNumeric( bool bOnlyIntntl
) const
681 SetError( ERRCODE_BASIC_PROP_WRITEONLY
);
685 if( auto pSbxVar
= dynamic_cast<const SbxVariable
*>( this) )
686 const_cast<SbxVariable
*>(pSbxVar
)->Broadcast( SfxHintId::BasicDataWanted
);
687 SbxDataType t
= GetType();
690 if( aData
.pOUString
)
692 OUString
s( *aData
.pOUString
);
696 bool bHasNumber
= false;
697 if( ImpScan( s
, n
, t2
, &nLen
, &bHasNumber
, bOnlyIntntl
) == ERRCODE_NONE
)
698 return nLen
== s
.getLength() && bHasNumber
;
702 #if HAVE_FEATURE_SCRIPTING
703 else if (t
== SbxBOOL
&& bOnlyIntntl
&& SbiRuntime::isVBAEnabled())
708 || ( t
>= SbxINTEGER
&& t
<= SbxCURRENCY
)
709 || ( t
>= SbxCHAR
&& t
<= SbxUINT
);
712 SbxDataType
SbxValue::GetType() const
714 return SbxDataType( aData
.eType
& 0x0FFF );
718 bool SbxValue::SetType( SbxDataType t
)
720 DBG_ASSERT( !( t
& 0xF000 ), "SetType of BYREF|ARRAY is forbidden!" );
721 if( ( t
== SbxEMPTY
&& aData
.eType
== SbxVOID
)
722 || ( aData
.eType
== SbxEMPTY
&& t
== SbxVOID
) )
724 if( ( t
& 0x0FFF ) == SbxVARIANT
)
726 // Try to set the data type to Variant
727 ResetFlag( SbxFlagBits::Fixed
);
730 SetError( ERRCODE_BASIC_CONVERSION
);
735 if( ( t
& 0x0FFF ) == ( aData
.eType
& 0x0FFF ) )
738 if( !CanWrite() || IsFixed() )
740 SetError( ERRCODE_BASIC_CONVERSION
);
745 // De-allocate potential objects
746 switch( aData
.eType
)
749 delete aData
.pOUString
;
752 if( aData
.pObj
&& aData
.pObj
!= this )
754 SAL_WARN("basic.sbx", "Not at Parent-Prop - otherwise CyclicRef");
755 SbxVariable
*pThisVar
= dynamic_cast<SbxVariable
*>( this );
756 sal_uInt32 nSlotId
= pThisVar
757 ? pThisVar
->GetUserData() & 0xFFFF
759 DBG_ASSERT( nSlotId
!= 5345 || pThisVar
->GetName() == "Parent",
760 "SID_PARENTOBJECT is not named 'Parent'" );
761 bool bParentProp
= nSlotId
== 5345;
763 aData
.pObj
->ReleaseRef();
773 bool SbxValue::Convert( SbxDataType eTo
)
775 eTo
= SbxDataType( eTo
& 0x0FFF );
776 if( ( aData
.eType
& 0x0FFF ) == eTo
)
780 if( eTo
== SbxVARIANT
)
782 // Trial to set the data type to Variant
783 ResetFlag( SbxFlagBits::Fixed
);
786 SetError( ERRCODE_BASIC_CONVERSION
);
792 // Converting from null doesn't work. Once null, always null!
793 if( aData
.eType
== SbxNULL
)
795 SetError( ERRCODE_BASIC_CONVERSION
);
799 // Conversion of the data:
803 // The data type could be converted. It ends here with fixed elements,
804 // because the data had not to be taken over
816 ////////////////////////////////// Calculating
818 static sal_Int64
MulAndDiv(sal_Int64 n
, sal_Int64 mul
, sal_Int64 div
)
822 SbxBase::SetError(ERRCODE_BASIC_ZERODIV
);
825 auto errorValue
= [](sal_Int64 x
, sal_Int64 y
, sal_Int64 z
)
827 const int i
= (x
< 0 ? -1 : 1) * (y
< 0 ? -1 : 1) * (z
< 0 ? -1 : 1);
828 return i
== 1 ? SAL_MAX_INT64
: SAL_MIN_INT64
;
831 // If x * integral part of (mul/div) overflows -> product does not fit
832 if (o3tl::checked_multiply(n
, mul
/ div
, result
))
834 SbxBase::SetError(ERRCODE_BASIC_MATH_OVERFLOW
);
835 return errorValue(n
, mul
, div
);
837 if (sal_Int64 mul_frac
= mul
% div
)
839 // can't overflow: mul_frac < div
840 sal_Int64 result_frac
= n
/ div
* mul_frac
;
841 if (sal_Int64 x_frac
= n
% div
)
842 result_frac
+= x_frac
* mul_frac
/ div
;
843 if (o3tl::checked_add(result
, result_frac
, result
))
845 SbxBase::SetError(ERRCODE_BASIC_MATH_OVERFLOW
);
846 return errorValue(n
, mul
, div
);
852 bool SbxValue::Compute( SbxOperator eOp
, const SbxValue
& rOp
)
854 #if !HAVE_FEATURE_SCRIPTING
855 const bool bVBAInterop
= false;
857 bool bVBAInterop
= SbiRuntime::isVBAEnabled();
859 SbxDataType eThisType
= GetType();
860 SbxDataType eOpType
= rOp
.GetType();
861 ErrCode eOld
= GetError();
862 if( eOld
!= ERRCODE_NONE
)
865 SetError( ERRCODE_BASIC_PROP_READONLY
);
866 else if( !rOp
.CanRead() )
867 SetError( ERRCODE_BASIC_PROP_WRITEONLY
);
868 // Special rule 1: If one operand is null, the result is null
869 else if( eThisType
== SbxNULL
|| eOpType
== SbxNULL
)
874 bool bDecimal
= false;
875 if( bVBAInterop
&& ( ( eThisType
== SbxSTRING
&& eOpType
!= SbxSTRING
&& eOpType
!= SbxEMPTY
) ||
876 ( eThisType
!= SbxSTRING
&& eThisType
!= SbxEMPTY
&& eOpType
== SbxSTRING
) ) &&
877 ( eOp
== SbxMUL
|| eOp
== SbxDIV
|| eOp
== SbxPLUS
|| eOp
== SbxMINUS
) )
881 else if( eThisType
== SbxSTRING
|| eOp
== SbxCAT
|| ( bVBAInterop
&& ( eOpType
== SbxSTRING
) && ( eOp
== SbxPLUS
) ) )
883 if( eOp
== SbxCAT
|| eOp
== SbxPLUS
)
885 // From 1999-11-5, keep OUString in mind
886 aL
.eType
= aR
.eType
= SbxSTRING
;
888 // From 1999-12-8, #70399: Here call GetType() again, Get() can change the type!
889 if( rOp
.GetType() == SbxEMPTY
)
890 goto Lbl_OpIsEmpty
; // concatenate empty, *this stays lhs as result
893 // #30576: To begin with test, if the conversion worked
894 if( aL
.pOUString
!= nullptr && aR
.pOUString
!= nullptr )
896 // tdf#108039: catch possible bad_alloc
898 *aL
.pOUString
+= *aR
.pOUString
;
900 catch (const std::bad_alloc
&) {
901 SetError(ERRCODE_BASIC_MATH_OVERFLOW
);
905 else if( aL
.pOUString
== nullptr )
907 aL
.pOUString
= new OUString();
911 SetError( ERRCODE_BASIC_CONVERSION
);
913 else if( eOpType
== SbxSTRING
&& rOp
.IsFixed() )
914 { // Numeric: there is no String allowed on the right side
915 SetError( ERRCODE_BASIC_CONVERSION
);
916 // falls all the way out
918 else if( ( eOp
>= SbxIDIV
&& eOp
<= SbxNOT
) || eOp
== SbxMOD
)
920 if( GetType() == eOpType
)
922 if (GetType() == SbxSALUINT64
|| GetType() == SbxSALINT64
|| GetType() == SbxULONG
)
923 aL
.eType
= aR
.eType
= GetType();
924 else if (GetType() == SbxCURRENCY
)
925 aL
.eType
= aR
.eType
= SbxSALINT64
; // Convert to integer value before operation
926 // tdf#145960 - return type of boolean operators should be of type boolean
927 else if ( eOpType
== SbxBOOL
&& eOp
!= SbxMOD
&& eOp
!= SbxIDIV
)
928 aL
.eType
= aR
.eType
= SbxBOOL
;
930 aL
.eType
= aR
.eType
= SbxLONG
;
933 aL
.eType
= aR
.eType
= SbxLONG
;
935 if (rOp
.Get(aR
) && Get(aL
)) // re-do Get after type assigns above
939 /* TODO: For SbxEMPTY operands with boolean operators use
940 * the VBA Nothing definition of Comparing Nullable Types?
941 * https://docs.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/data-types/nullable-value-types
943 /* TODO: it is unclear yet whether this also should be done
944 * for the non-bVBAInterop case or not, or at all, consider
945 * user defined spreadsheet functions where an empty cell
946 * is SbxEMPTY and usually is treated as 0 zero or "" empty
950 if( aL
.eType
== SbxSALUINT64
)
951 if( !aR
.uInt64
) SetError( ERRCODE_BASIC_ZERODIV
);
952 else aL
.uInt64
/= aR
.uInt64
;
953 else if( aL
.eType
== SbxCURRENCY
|| aL
.eType
== SbxSALINT64
)
954 if( !aR
.nInt64
) SetError( ERRCODE_BASIC_ZERODIV
);
955 else aL
.nInt64
/= aR
.nInt64
;
956 else if( aL
.eType
== SbxLONG
)
957 if( !aR
.nLong
) SetError( ERRCODE_BASIC_ZERODIV
);
958 else aL
.nLong
/= aR
.nLong
;
960 if( !aR
.nULong
) SetError( ERRCODE_BASIC_ZERODIV
);
961 else aL
.nULong
/= aR
.nULong
;
964 if( aL
.eType
== SbxCURRENCY
|| aL
.eType
== SbxSALINT64
)
965 if( !aR
.nInt64
) SetError( ERRCODE_BASIC_ZERODIV
);
966 else aL
.nInt64
%= aR
.nInt64
;
967 else if( aL
.eType
== SbxSALUINT64
)
968 if( !aR
.uInt64
) SetError( ERRCODE_BASIC_ZERODIV
);
969 else aL
.uInt64
%= aR
.uInt64
;
970 else if( aL
.eType
== SbxLONG
)
971 if( !aR
.nLong
) SetError( ERRCODE_BASIC_ZERODIV
);
972 else aL
.nLong
%= aR
.nLong
;
974 if( !aR
.nULong
) SetError( ERRCODE_BASIC_ZERODIV
);
975 else aL
.nULong
%= aR
.nULong
;
978 if( aL
.eType
!= SbxLONG
&& aL
.eType
!= SbxULONG
)
979 aL
.nInt64
&= aR
.nInt64
;
981 aL
.nLong
&= aR
.nLong
;
984 if( aL
.eType
!= SbxLONG
&& aL
.eType
!= SbxULONG
)
985 aL
.nInt64
|= aR
.nInt64
;
987 aL
.nLong
|= aR
.nLong
;
990 if( aL
.eType
!= SbxLONG
&& aL
.eType
!= SbxULONG
)
991 aL
.nInt64
^= aR
.nInt64
;
993 aL
.nLong
^= aR
.nLong
;
996 if( aL
.eType
!= SbxLONG
&& aL
.eType
!= SbxULONG
)
997 aL
.nInt64
= (aL
.nInt64
& aR
.nInt64
) | (~aL
.nInt64
& ~aR
.nInt64
);
999 aL
.nLong
= (aL
.nLong
& aR
.nLong
) | (~aL
.nLong
& ~aR
.nLong
);
1002 if( aL
.eType
!= SbxLONG
&& aL
.eType
!= SbxULONG
)
1003 aL
.nInt64
= ~aL
.nInt64
| aR
.nInt64
;
1005 aL
.nLong
= ~aL
.nLong
| aR
.nLong
;
1008 if( aL
.eType
!= SbxLONG
&& aL
.eType
!= SbxULONG
)
1010 if ( aL
.eType
!= SbxBOOL
)
1011 aL
.nInt64
= ~aL
.nInt64
;
1013 aL
.nLong
= ~aL
.nLong
;
1016 aL
.nLong
= ~aL
.nLong
;
1022 else if( ( GetType() == SbxDECIMAL
|| rOp
.GetType() == SbxDECIMAL
)
1023 && ( eOp
== SbxMUL
|| eOp
== SbxDIV
|| eOp
== SbxPLUS
|| eOp
== SbxMINUS
|| eOp
== SbxNEG
) )
1025 aL
.eType
= aR
.eType
= SbxDECIMAL
;
1027 if( rOp
.Get( aR
) && Get( aL
) )
1029 if( aL
.pDecimal
&& aR
.pDecimal
)
1035 bOk
= ( *(aL
.pDecimal
) *= *(aR
.pDecimal
) );
1038 if( aR
.pDecimal
->isZero() )
1039 SetError( ERRCODE_BASIC_ZERODIV
);
1041 bOk
= ( *(aL
.pDecimal
) /= *(aR
.pDecimal
) );
1044 bOk
= ( *(aL
.pDecimal
) += *(aR
.pDecimal
) );
1047 bOk
= ( *(aL
.pDecimal
) -= *(aR
.pDecimal
) );
1050 bOk
= ( aL
.pDecimal
->neg() );
1053 SetError( ERRCODE_BASIC_BAD_ARGUMENT
);
1056 SetError( ERRCODE_BASIC_MATH_OVERFLOW
);
1060 SetError( ERRCODE_BASIC_CONVERSION
);
1064 else if( GetType() == SbxCURRENCY
|| rOp
.GetType() == SbxCURRENCY
)
1066 aL
.eType
= aR
.eType
= SbxCURRENCY
;
1068 if (rOp
.Get(aR
) && Get(aL
))
1073 aL
.nInt64
= MulAndDiv(aL
.nInt64
, aR
.nInt64
, CURRENCY_FACTOR
);
1077 aL
.nInt64
= MulAndDiv(aL
.nInt64
, CURRENCY_FACTOR
, aR
.nInt64
);
1081 if (o3tl::checked_add(aL
.nInt64
, aR
.nInt64
, aL
.nInt64
))
1082 SetError(ERRCODE_BASIC_MATH_OVERFLOW
);
1086 // Use subtraction; allows to detect negation of SAL_MIN_INT64
1087 aR
.nInt64
= std::exchange(aL
.nInt64
, 0);
1090 if (o3tl::checked_sub(aL
.nInt64
, aR
.nInt64
, aL
.nInt64
))
1091 SetError(ERRCODE_BASIC_MATH_OVERFLOW
);
1095 SetError( ERRCODE_BASIC_BAD_ARGUMENT
);
1101 { // other types and operators including Date, Double and Single
1102 aL
.eType
= aR
.eType
= SbxDOUBLE
;
1110 aL
.nDouble
= pow( aL
.nDouble
, aR
.nDouble
);
1113 aL
.nDouble
*= aR
.nDouble
; break;
1115 if( !aR
.nDouble
) SetError( ERRCODE_BASIC_ZERODIV
);
1116 else aL
.nDouble
/= aR
.nDouble
;
1119 aL
.nDouble
+= aR
.nDouble
; break;
1121 aL
.nDouble
-= aR
.nDouble
; break;
1123 aL
.nDouble
= -aL
.nDouble
; break;
1125 SetError( ERRCODE_BASIC_BAD_ARGUMENT
);
1127 // Date with "+" or "-" needs special handling that
1128 // forces the Date type. If the operation is '+' the
1129 // result is always a Date, if '-' the result is only
1130 // a Date if one of lhs or rhs ( but not both ) is already
1132 if( GetType() == SbxDATE
|| rOp
.GetType() == SbxDATE
)
1134 if( eOp
== SbxPLUS
|| ( ( eOp
== SbxMINUS
) && ( GetType() != rOp
.GetType() ) ) )
1146 releaseDecimalPtr( aL
.pDecimal
);
1147 releaseDecimalPtr( aR
.pDecimal
);
1152 bool bRes
= !IsError();
1153 if( bRes
&& eOld
!= ERRCODE_NONE
)
1158 // The comparison routine deliver TRUE or FALSE.
1160 template <typename T
> static bool CompareNormal(const T
& l
, const T
& r
, SbxOperator eOp
)
1179 SbxBase::SetError(ERRCODE_BASIC_BAD_ARGUMENT
);
1183 bool SbxValue::Compare( SbxOperator eOp
, const SbxValue
& rOp
) const
1185 #if !HAVE_FEATURE_SCRIPTING
1186 const bool bVBAInterop
= false;
1188 bool bVBAInterop
= SbiRuntime::isVBAEnabled();
1192 ErrCode eOld
= GetError();
1193 if( eOld
!= ERRCODE_NONE
)
1195 if( !CanRead() || !rOp
.CanRead() )
1196 SetError( ERRCODE_BASIC_PROP_WRITEONLY
);
1197 else if( GetType() == SbxNULL
&& rOp
.GetType() == SbxNULL
&& !bVBAInterop
)
1201 else if( GetType() == SbxEMPTY
&& rOp
.GetType() == SbxEMPTY
)
1202 bRes
= !bVBAInterop
|| ( eOp
== SbxEQ
);
1203 // Special rule 1: If an operand is null, the result is FALSE
1204 else if( GetType() == SbxNULL
|| rOp
.GetType() == SbxNULL
)
1206 // Special rule 2: If both are variant and one is numeric
1207 // and the other is a String, num is < str
1208 else if( !IsFixed() && !rOp
.IsFixed()
1209 && ( rOp
.GetType() == SbxSTRING
&& GetType() != SbxSTRING
&& IsNumeric() ) && !bVBAInterop
1211 bRes
= eOp
== SbxLT
|| eOp
== SbxLE
|| eOp
== SbxNE
;
1212 else if( !IsFixed() && !rOp
.IsFixed()
1213 && ( GetType() == SbxSTRING
&& rOp
.GetType() != SbxSTRING
&& rOp
.IsNumeric() )
1216 bRes
= eOp
== SbxGT
|| eOp
== SbxGE
|| eOp
== SbxNE
;
1220 // If one of the operands is a String,
1221 // a String comparing take place
1222 if( GetType() == SbxSTRING
|| rOp
.GetType() == SbxSTRING
)
1224 aL
.eType
= aR
.eType
= SbxSTRING
;
1225 if (Get(aL
) && rOp
.Get(aR
))
1226 bRes
= CompareNormal(*aL
.pOUString
, *aR
.pOUString
, eOp
);
1228 // From 1995-12-19: If SbxSINGLE participate, then convert to SINGLE,
1229 // otherwise it shows a numeric error
1230 else if( GetType() == SbxSINGLE
|| rOp
.GetType() == SbxSINGLE
)
1232 aL
.eType
= aR
.eType
= SbxSINGLE
;
1233 if( Get( aL
) && rOp
.Get( aR
) )
1234 bRes
= CompareNormal(aL
.nSingle
, aR
.nSingle
, eOp
);
1236 else if( GetType() == SbxDECIMAL
&& rOp
.GetType() == SbxDECIMAL
)
1238 aL
.eType
= aR
.eType
= SbxDECIMAL
;
1241 if( aL
.pDecimal
&& aR
.pDecimal
)
1243 SbxDecimal::CmpResult eRes
= compare( *aL
.pDecimal
, *aR
.pDecimal
);
1247 bRes
= ( eRes
== SbxDecimal::CmpResult::EQ
); break;
1249 bRes
= ( eRes
!= SbxDecimal::CmpResult::EQ
); break;
1251 bRes
= ( eRes
== SbxDecimal::CmpResult::LT
); break;
1253 bRes
= ( eRes
== SbxDecimal::CmpResult::GT
); break;
1255 bRes
= ( eRes
!= SbxDecimal::CmpResult::GT
); break;
1257 bRes
= ( eRes
!= SbxDecimal::CmpResult::LT
); break;
1259 SetError( ERRCODE_BASIC_BAD_ARGUMENT
);
1264 SetError( ERRCODE_BASIC_CONVERSION
);
1266 releaseDecimalPtr( aL
.pDecimal
);
1267 releaseDecimalPtr( aR
.pDecimal
);
1269 else if (GetType() == SbxCURRENCY
&& rOp
.GetType() == SbxCURRENCY
)
1271 aL
.eType
= aR
.eType
= GetType();
1272 if (Get(aL
) && rOp
.Get(aR
))
1273 bRes
= CompareNormal(aL
.nInt64
, aR
.nInt64
, eOp
);
1275 // Everything else comparing on a SbxDOUBLE-Basis
1278 aL
.eType
= aR
.eType
= SbxDOUBLE
;
1279 bool bGetL
= Get( aL
);
1280 bool bGetR
= rOp
.Get( aR
);
1281 if( bGetL
&& bGetR
)
1282 bRes
= CompareNormal(aL
.nDouble
, aR
.nDouble
, eOp
);
1283 // at least one value was got
1284 // if this is VBA then a conversion error for one
1285 // side will yield a false result of an equality test
1286 else if ( bGetR
|| bGetL
)
1288 if ( bVBAInterop
&& eOp
== SbxEQ
&& GetError() == ERRCODE_BASIC_CONVERSION
)
1298 if( eOld
!= ERRCODE_NONE
)
1303 ///////////////////////////// Reading/Writing
1305 bool SbxValue::LoadData( SvStream
& r
, sal_uInt16
)
1307 // #TODO see if these types are really dumped to any stream
1308 // more than likely this is functionality used in the binfilter alone
1311 r
.ReadUInt16( nType
);
1312 aData
.eType
= SbxDataType( nType
);
1317 r
.ReadInt16( aData
.nInteger
); break;
1320 r
.ReadInt32( aData
.nLong
);
1325 OUString aVal
= read_uInt16_lenPrefixed_uInt8s_ToOUString(r
,
1326 RTL_TEXTENCODING_ASCII_US
);
1329 if( ImpScan( aVal
, d
, t
, nullptr ) != ERRCODE_NONE
|| t
== SbxDOUBLE
)
1331 aData
.nSingle
= 0.0F
;
1334 aData
.nSingle
= static_cast<float>(d
);
1341 OUString aVal
= read_uInt16_lenPrefixed_uInt8s_ToOUString(r
,
1342 RTL_TEXTENCODING_ASCII_US
);
1344 if( ImpScan( aVal
, aData
.nDouble
, t
, nullptr ) != ERRCODE_NONE
)
1346 aData
.nDouble
= 0.0;
1352 r
.ReadInt64(aData
.nInt64
);
1355 r
.ReadUInt64( aData
.uInt64
);
1359 sal_uInt32 tmpHi
= 0;
1360 sal_uInt32 tmpLo
= 0;
1361 r
.ReadUInt32( tmpHi
).ReadUInt32( tmpLo
);
1362 aData
.nInt64
= (static_cast<sal_Int64
>(tmpHi
) << 32);
1363 aData
.nInt64
|= static_cast<sal_Int64
>(tmpLo
);
1368 OUString aVal
= read_uInt16_lenPrefixed_uInt8s_ToOUString(r
,
1369 RTL_TEXTENCODING_ASCII_US
);
1370 if( !aVal
.isEmpty() )
1371 aData
.pOUString
= new OUString( aVal
);
1373 aData
.pOUString
= nullptr; // JSM 1995-09-22
1378 r
.ReadUInt16( aData
.nUShort
); break;
1382 r
.ReadUChar( nMode
);
1386 aData
.pObj
= nullptr;
1390 auto ref
= SbxBase::Load( r
);
1391 aData
.pObj
= ref
.get();
1392 // if necessary increment Ref-Count
1394 aData
.pObj
->AddFirstRef();
1395 return ( aData
.pObj
!= nullptr );
1411 r
.ReadUChar( aData
.nByte
); break;
1413 r
.ReadUInt32( aData
.nULong
); break;
1418 // Match the Int on this system?
1419 if( n
> SAL_TYPES_SIZEOFINT
)
1421 r
.ReadInt32( aData
.nLong
);
1422 aData
.eType
= SbxLONG
;
1426 r
.ReadInt32( nInt
);
1435 // Match the UInt on this system?
1436 if( n
> SAL_TYPES_SIZEOFINT
)
1438 r
.ReadUInt32( aData
.nULong
);
1439 aData
.eType
= SbxULONG
;
1443 r
.ReadUInt32( nUInt
);
1444 aData
.nUInt
= nUInt
;
1452 // #78919 For backwards compatibility
1457 aData
.clear(SbxNULL
);
1458 ResetFlag(SbxFlagBits::Fixed
);
1459 SAL_WARN( "basic.sbx", "Loaded a non-supported data type" );
1466 std::pair
<bool, sal_uInt32
> SbxValue::StoreData( SvStream
& r
) const
1468 sal_uInt16 nType
= sal::static_int_cast
< sal_uInt16
>(aData
.eType
);
1469 r
.WriteUInt16( nType
);
1470 switch( nType
& 0x0FFF )
1474 r
.WriteInt16( aData
.nInteger
); break;
1477 r
.WriteInt32( aData
.nLong
);
1480 // #49935: Save as double, otherwise an error during the read in
1481 const_cast<SbxValue
*>(this)->aData
.eType
= static_cast<SbxDataType
>( ( nType
& 0xF000 ) | SbxDOUBLE
);
1482 write_uInt16_lenPrefixed_uInt8s_FromOUString(r
, GetCoreString(), RTL_TEXTENCODING_ASCII_US
);
1483 const_cast<SbxValue
*>(this)->aData
.eType
= static_cast<SbxDataType
>(nType
);
1487 write_uInt16_lenPrefixed_uInt8s_FromOUString(r
, GetCoreString(), RTL_TEXTENCODING_ASCII_US
);
1491 // see comment in SbxValue::StoreData
1492 r
.WriteUInt64( aData
.uInt64
);
1496 sal_Int32 tmpHi
= ( (aData
.nInt64
>> 32) & 0xFFFFFFFF );
1497 sal_Int32 tmpLo
= static_cast<sal_Int32
>(aData
.nInt64
);
1498 r
.WriteInt32( tmpHi
).WriteInt32( tmpLo
);
1502 if( aData
.pOUString
)
1504 write_uInt16_lenPrefixed_uInt8s_FromOUString(r
, *aData
.pOUString
, RTL_TEXTENCODING_ASCII_US
);
1508 write_uInt16_lenPrefixed_uInt8s_FromOUString(r
, std::u16string_view(), RTL_TEXTENCODING_ASCII_US
);
1513 r
.WriteUInt16( aData
.nUShort
); break;
1515 // to save itself as Objectptr does not work!
1518 if( dynamic_cast<SbxValue
*>( aData
.pObj
) != this )
1521 return aData
.pObj
->Store( r
);
1531 char c
= sal::static_int_cast
< char >(aData
.nChar
);
1536 r
.WriteUChar( aData
.nByte
); break;
1538 r
.WriteUInt32( aData
.nULong
); break;
1541 r
.WriteUChar( SAL_TYPES_SIZEOFINT
).WriteInt32( aData
.nInt
);
1546 r
.WriteUChar( SAL_TYPES_SIZEOFINT
).WriteUInt32( aData
.nUInt
);
1553 // #78919 For backwards compatibility
1558 SAL_WARN( "basic.sbx", "Saving a non-supported data type" );
1559 return { false, 0 };
1561 return { true, B_IMG_VERSION_12
};
1564 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */