tdf#149151: disambiguate method parameters and array indexes

SbiRuntime::FindElement ends with a call to CheckArray(). The latter
takes a variable, checks if that is an array and if it has parameters;
and if both are true, tries to obtain an element of the array defined
by the parameters as array indexes (using SbxDimArray::Get).

The problem was, that the variable may actually be array; it may have
parameters; but this array can be the result of a function call; the
variable may be SbxMethod; and the parameters may be for the function,
not array indexes.

Specifically, the call like

  Iif(True, Array("A","B"), Array("B","A"))

searched for an entity named Iif; found that it was a RTL method;
prepared the arguments for it (consisting of four elements: 0 - the
method itself; 1 - boolean True; 2 - array ["A", "B"]; 3 - array
["B", "A"]); the function executed, putting the correct value to the
params[0], which was the method itself (so its value became an array);
and finally, the SbxMethod variable with the array value got passed
to CheckArray. The latter found out that it's an array, and that it
has parameters (those four) - and tried to get the element, which
index was defined by the parameters.

Here comes the specifics of LibreOffice Basic's argument handling.
SbxDimArray::Get only cares about first arguments expected by the
array dimensions; it ignores the rest. Since the array was single-
dimensional, it only considered parameter 1 (parameter 0 is, by
convention, reserved for return value). That was a boolean. When it
was False, its GetInteger returned 0; and finally, CheckArray took
element 0 from the array, returning a scalar value, as if it was

  Iif(False, Array("A","B"), Array("B","A"))(0)

But when the boolean was True, another Basic specific took place:
its numerical value is -1; and trying to get element -1 from the
array resulted in "Index out of defined range".

This change disambiguates between the cases of array index, and
function parameters, checking if the variable itself, and its 0th
parameter are both SbxMethods. Then it doesn't try to treat the
parameters as indexes into the array.

Change-Id: I597f94a7e688c8bba12d87a1bd5871491c25cac0
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/187145
Tested-by: Jenkins
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
This commit is contained in:
Mike Kaganski
2025-06-29 03:27:50 +05:00
parent 6ca495842c
commit 99247bfc4e
2 changed files with 37 additions and 8 deletions

View File

@ -8,11 +8,24 @@
Option Explicit
Function doUnitTest as String
' IIF
If ( IIF(True, 10, 12) <> 10 ) Then
doUnitTest = "FAIL"
Else
doUnitTest = "OK"
End If
Function doUnitTest() As String
TestUtil.TestInit
verify_testIif
doUnitTest = TestUtil.GetResult()
End Function
Sub verify_testIif
On Error GoTo errorHandler
TestUtil.AssertEqual(IIF(True, 10, 12), 10, "IIF(True, 10, 12)")
' tdf#149151
' Without the fix, this would fail with "hit error handler - 9: Index out of defined range"
TestUtil.Assert(IsArray(Iif(True, Array("A","B"), Array("B","A"))), "IsArray(Iif(True, Array(""A"",""B""), Array(""B"",""A"")))")
TestUtil.AssertEqualStrict(Iif(True, Array("A","B"), Array("B","A"))(0), "A", "Iif(True, Array(""A"",""B""), Array(""B"",""A""))(0)")
TestUtil.AssertEqualStrict(Iif(True, Array("A","B"), Array("B","A"))(1), "B", "Iif(True, Array(""A"",""B""), Array(""B"",""A""))(1)")
Exit Sub
errorHandler:
TestUtil.ReportErrorHandler("verify_testIif", Err, Error$, Erl)
End Sub

View File

@ -3953,7 +3953,23 @@ SbxVariable* SbiRuntime::CheckArray( SbxVariable* pElem )
// parameters may be missing, if an array is
// passed as an argument
if( pPar )
pElem = pDimArray->Get( pPar );
{
bool parIsArrayIndex = true;
if (dynamic_cast<const SbxMethod*>(pElem))
{
// If this was a method, then there are two possibilities:
// 1. pPar is this method's parameters.
// 2. pPar is the indexes into the array returned from the method.
// To disambiguate, check the 0th element of pPar.
if (dynamic_cast<const SbxMethod*>(pPar->Get(0)))
{
// pPar was the parameters to the method, not indexes into the array
parIsArrayIndex = false;
}
}
if (parIsArrayIndex)
pElem = pDimArray->Get(pPar);
}
}
else
{