tdf#131296: get numeric value with its data type in StepLOADNC
In order to load numeric values, generate SbiOpcode::NUMBER_ opcodes
including the numeric value and its data type instead of SbiOpcode::CONST_.
The numeric value and its data type will be restored in
SbiRuntime::StepLOADNC. When the new compiled code is stored in documents,
e.g. password-protected libraries, leagcy LO versions will just read up to
non-numeric characters, thus correctly obtaining number value and ignoring
the type, so the change is backward-compatible.
To interpret legacy compiled code, old treatment of SbiRuntime::StepLOADI
is restored, reverting commit 0b4f8bf571baf2ccd5a8aafdc4deb41867420be3.
This change reimplements the fix for tdf#129596.
Change-Id: I46ebfc77f9bea69479968950c0fb7264e4a7ab27
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/90858
Tested-by: Jenkins
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
diff --git a/basic/source/comp/exprgen.cxx b/basic/source/comp/exprgen.cxx
index 3981bef..6c60c02 100644
--- a/basic/source/comp/exprgen.cxx
+++ b/basic/source/comp/exprgen.cxx
@@ -72,16 +72,14 @@ void SbiExprNode::Gen( SbiCodeGen& rGen, RecursiveMode eRecMode )
case SbxEMPTY:
rGen.Gen( SbiOpcode::EMPTY_ );
break;
case SbxLONG:
case SbxINTEGER:
nStringId = rGen.GetParser()->aGblStrings.Add(nVal, eType);
rGen.Gen( SbiOpcode::CONST_, nStringId );
break;
case SbxSTRING:
nStringId = rGen.GetParser()->aGblStrings.Add( aStrVal );
rGen.Gen( SbiOpcode::SCONST_, nStringId );
break;
default:
// tdf#131296 - generate SbiOpcode::NUMBER_ instead of SbiOpcode::CONST_
// for SbxINTEGER and SbxLONG including their numeric value and its data type,
// which will be restored in SbiRuntime::StepLOADNC.
nStringId = rGen.GetParser()->aGblStrings.Add( nVal, eType );
rGen.Gen( SbiOpcode::NUMBER_, nStringId );
break;
diff --git a/basic/source/comp/symtbl.cxx b/basic/source/comp/symtbl.cxx
index b05575a..ac6f1ec 100644
--- a/basic/source/comp/symtbl.cxx
+++ b/basic/source/comp/symtbl.cxx
@@ -66,10 +66,13 @@ short SbiStringPool::Add( double n, SbxDataType t )
char buf[ 40 ];
switch( t )
{
case SbxINTEGER: snprintf( buf, sizeof(buf), "%d", static_cast<short>(n) ); break;
case SbxLONG: snprintf( buf, sizeof(buf), "%ld", static_cast<long>(n) ); break;
case SbxSINGLE: snprintf( buf, sizeof(buf), "%.6g", static_cast<float>(n) ); break;
case SbxDOUBLE: snprintf( buf, sizeof(buf), "%.16g", n ); break;
// tdf#131296 - store numeric value including its type character
// See GetSuffixType in basic/source/comp/scanner.cxx for type characters
case SbxINTEGER: snprintf( buf, sizeof(buf), "%d%%", static_cast<short>(n) ); break;
case SbxLONG: snprintf( buf, sizeof(buf), "%ld&", static_cast<long>(n) ); break;
case SbxSINGLE: snprintf( buf, sizeof(buf), "%.6g!", static_cast<float>(n) ); break;
case SbxDOUBLE: snprintf( buf, sizeof(buf), "%.16g", n ); break; // default processing in SbiRuntime::StepLOADNC - no type character
case SbxCURRENCY: snprintf(buf, sizeof(buf), "%.16g@", n); break;
default: break;
}
return Add( OUString::createFromAscii( buf ) );
diff --git a/basic/source/runtime/runtime.cxx b/basic/source/runtime/runtime.cxx
index ffd03f2..15b45ec 100644
--- a/basic/source/runtime/runtime.cxx
+++ b/basic/source/runtime/runtime.cxx
@@ -2773,8 +2773,6 @@ void SbiRuntime::StepERROR()
void SbiRuntime::StepLOADNC( sal_uInt32 nOp1 )
{
SbxVariable* p = new SbxVariable( SbxDOUBLE );
// #57844 use localized function
OUString aStr = pImg->GetString( static_cast<short>( nOp1 ) );
// also allow , !!!
@@ -2783,8 +2781,24 @@ void SbiRuntime::StepLOADNC( sal_uInt32 nOp1 )
{
aStr = aStr.replaceAt(iComma, 1, ".");
}
double n = ::rtl::math::stringToDouble( aStr, '.', ',' );
sal_Int32 nParseEnd = 0;
rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
double n = ::rtl::math::stringToDouble( aStr, '.', ',', &eStatus, &nParseEnd );
// tdf#131296 - retrieve data type put in SbiExprNode::Gen
SbxDataType eType = SbxDOUBLE;
if ( nParseEnd < aStr.getLength() )
{
switch ( aStr[nParseEnd] )
{
// See GetSuffixType in basic/source/comp/scanner.cxx for type characters
case '%': eType = SbxINTEGER; break;
case '&': eType = SbxLONG; break;
case '!': eType = SbxSINGLE; break;
case '@': eType = SbxCURRENCY; break;
}
}
SbxVariable* p = new SbxVariable( eType );
p->PutDouble( n );
PushVar( p );
}
@@ -2799,22 +2813,13 @@ void SbiRuntime::StepLOADSC( sal_uInt32 nOp1 )
}
// Immediate Load (+value)
// The opcode is not generated in SbiExprNode::Gen anymore; used for legacy images
void SbiRuntime::StepLOADI( sal_uInt32 nOp1 )
{
SbxVariable* p = new SbxVariable;
OUString aStr = pImg->GetString(static_cast<short>(nOp1));
double n = ::rtl::math::stringToDouble(aStr, '.', ',');
if (n >= SbxMININT && n <= SbxMAXINT)
{
p->PutInteger(static_cast<sal_Int16>(n));
}
else
{
p->PutLong(static_cast<sal_Int32>(n));
}
PushVar(p);
p->PutInteger( static_cast<sal_Int16>( nOp1 ) );
PushVar( p );
}
// store a named argument in Argv (+Arg-no. from 1!)
diff --git a/sc/qa/extras/macros-test.cxx b/sc/qa/extras/macros-test.cxx
index f887a95..3801a99 100644
--- a/sc/qa/extras/macros-test.cxx
+++ b/sc/qa/extras/macros-test.cxx
@@ -37,6 +37,8 @@ public:
void testPasswordProtectedStarBasic();
void testRowColumn();
void testPasswordProtectedUnicodeString();
void testTdf131296_legacy();
void testTdf131296_new();
CPPUNIT_TEST_SUITE(ScMacrosTest);
CPPUNIT_TEST(testStarBasic);
@@ -45,6 +47,8 @@ public:
CPPUNIT_TEST(testPasswordProtectedStarBasic);
CPPUNIT_TEST(testRowColumn);
CPPUNIT_TEST(testPasswordProtectedUnicodeString);
CPPUNIT_TEST(testTdf131296_legacy);
CPPUNIT_TEST(testTdf131296_new);
CPPUNIT_TEST_SUITE_END();
};
@@ -453,6 +457,85 @@ void ScMacrosTest::testPasswordProtectedUnicodeString()
xCloseable->close(true);
}
void ScMacrosTest::testTdf131296_legacy()
{
// For legacy password-protected library images, we must correctly get the constants' values,
// and also - for Integer - the type.
const std::vector<std::pair<OUString, OUString>> aTests({
{ "TestIntConst", "Integer: 123" },
{ "TestLongConst", "Double: 123" },
{ "TestSingleConst", "Double: 123" },
{ "TestDoubleConst", "Double: 123" },
});
OUString aFileName;
createFileURL("tdf131296_legacy.ods", aFileName);
auto xComponent = loadFromDesktop(aFileName, "com.sun.star.sheet.SpreadsheetDocument");
CPPUNIT_ASSERT(xComponent);
{
Any aRet;
Sequence<sal_Int16> aOutParamIndex;
Sequence<Any> aOutParam;
Sequence<uno::Any> aParams;
for (auto& [sTestName, sExpected] : aTests)
{
SfxObjectShell::CallXScript(xComponent,
"vnd.sun.Star.script:Protected.Module1." + sTestName
+ "?language=Basic&location=document",
aParams, aRet, aOutParamIndex, aOutParam);
OUString aReturnValue;
aRet >>= aReturnValue;
CPPUNIT_ASSERT_EQUAL_MESSAGE(sTestName.toUtf8().getStr(), sExpected, aReturnValue);
}
}
css::uno::Reference<css::util::XCloseable> xCloseable(xComponent, css::uno::UNO_QUERY_THROW);
xCloseable->close(true);
}
void ScMacrosTest::testTdf131296_new()
{
// For new password-protected library images, we must correctly get both the constants' values
// and their types.
const std::vector<std::pair<OUString, OUString>> aTests({
{ "TestIntConst", "Integer: 123" },
{ "TestLongConst", "Long: 123" },
{ "TestSingleConst", "Single: 123" },
{ "TestDoubleConst", "Double: 123" },
{ "TestCurrencyConst", "Currency: 123.0000" },
});
OUString aFileName;
createFileURL("tdf131296_new.ods", aFileName);
auto xComponent = loadFromDesktop(aFileName, "com.sun.star.sheet.SpreadsheetDocument");
CPPUNIT_ASSERT(xComponent);
{
Any aRet;
Sequence<sal_Int16> aOutParamIndex;
Sequence<Any> aOutParam;
Sequence<uno::Any> aParams;
for (auto& [sTestName, sExpected] : aTests)
{
SfxObjectShell::CallXScript(xComponent,
"vnd.sun.Star.script:Protected.Module1." + sTestName
+ "?language=Basic&location=document",
aParams, aRet, aOutParamIndex, aOutParam);
OUString aReturnValue;
aRet >>= aReturnValue;
CPPUNIT_ASSERT_EQUAL_MESSAGE(sTestName.toUtf8().getStr(), sExpected, aReturnValue);
}
}
css::uno::Reference<css::util::XCloseable> xCloseable(xComponent, css::uno::UNO_QUERY_THROW);
xCloseable->close(true);
}
ScMacrosTest::ScMacrosTest()
: UnoApiTest("/sc/qa/extras/testdocuments")
{
diff --git a/sc/qa/extras/testdocuments/tdf131296_legacy.ods b/sc/qa/extras/testdocuments/tdf131296_legacy.ods
new file mode 100644
index 0000000..43ebdeb
--- /dev/null
+++ b/sc/qa/extras/testdocuments/tdf131296_legacy.ods
Binary files differ
diff --git a/sc/qa/extras/testdocuments/tdf131296_new.ods b/sc/qa/extras/testdocuments/tdf131296_new.ods
new file mode 100644
index 0000000..9c0161c
--- /dev/null
+++ b/sc/qa/extras/testdocuments/tdf131296_new.ods
Binary files differ