basic/qa/basic_coverage/test_With.bas |   11 +++
 basic/source/comp/loops.cxx           |   97 +++++++++++++++++++++-------------
 2 files changed, 72 insertions(+), 36 deletions(-)

New commits:
commit acd0556dc8d0ee48c4a0b338a093c9ece029a57c
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Sun Oct 6 22:53:18 2024 +0500
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Tue Oct 8 10:23:03 2024 +0200

    tdf#163219: only create local With variable for function results
    
    The problem is, that having a local variable referring the same value
    as the original expression 'foo' is not the same as referencing 'foo'
    itself. After 'foo' is re-assigned, the local variable still refers
    to the original value, not the new one.
    
    It seems impossible to implement the reference using existing codegen
    primitives, to imitate the true reference to 'foo', not to its value.
    If we implement it by changing the runtime, the bytecode won't work
    identically in older versions; if we introduce a new bytecode, that
    would be an incompatible change.
    
    As a workaround, only create the local With variable, when the block
    variable is created using some function (as much as known by parser).
    I think that there would be cases when this would still not work as
    intended: an example is a property implemented using getter function;
    the parser would likely treat the property as a variable, and avoid
    creation of the local variable; and the getter would be called every
    time a dot access will happen (which was the essence of tdf#132064).
    However, this seems a better alternative to the bug fixed here.
    
    Change-Id: I50bf679762fd2e73f215a000fa0ab60fd6ae7453
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/174564
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>
    (cherry picked from commit ff3791f67a6421d64f5f9d6a09feaead1a63ff92)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/174453
    Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org>

diff --git a/basic/qa/basic_coverage/test_With.bas 
b/basic/qa/basic_coverage/test_With.bas
index c09fc037bb06..b22e0d94ba1f 100644
--- a/basic/qa/basic_coverage/test_With.bas
+++ b/basic/qa/basic_coverage/test_With.bas
@@ -69,6 +69,17 @@ Sub test_with
     fields = "n = " & foo_var.n & " s = " & foo_var.s
     TestUtil.AssertEqual(fields, "n = 6 s = baz", "Field values of foo_var")
 
+    ' tdf#163219: make sure that assigning the variable in the block works
+    Set foo_var = Nothing
+    TestUtil.Assert(foo_var Is Nothing, "foo_var Is Nothing")
+    With foo_var
+        foo_var = New foo
+        .n = 7
+        .s = "something"
+    End With
+    TestUtil.AssertEqual(foo_var.n, 7, "foo_var.n")
+    TestUtil.AssertEqual(foo_var.s, "something", "foo_var.s")
+
     ' tdf#162935: Test an UNO struct - it used to copy into the With variable, 
not used by ref
     Dim uno_struct As New com.sun.star.table.CellRangeAddress
     With uno_struct
diff --git a/basic/source/comp/loops.cxx b/basic/source/comp/loops.cxx
index 997e2a9db23c..b638c82fd151 100644
--- a/basic/source/comp/loops.cxx
+++ b/basic/source/comp/loops.cxx
@@ -273,6 +273,63 @@ void SbiParser::For()
 
 // WITH .. END WITH
 
+namespace
+{
+// Generate a '{_with_library.module_offset} = rVar'
+// Use the {_with_library.module_offset} in OpenBlock
+// The name of the variable can't be used by user: a name like 
[{_with_library.module_offset}]
+// is valid, but not without the square brackets
+struct WithLocalVar
+{
+    WithLocalVar(SbiParser& rParser, SbiExpression& rVar)
+        : m_rParser(rParser)
+        , m_aWithParent(createLocalVar(rParser))
+    {
+        // Assignment
+        m_aWithParent.Gen();
+        rVar.Gen();
+        m_rParser.aGen.Gen(SbiOpcode::PUTC_);
+    }
+
+    ~WithLocalVar()
+    {
+        // {_with_library.module_offset} = Nothing
+        m_aWithParent.Gen();
+        m_rParser.aGen.Gen(SbiOpcode::RTL_, 
m_rParser.aGblStrings.Add(u"Nothing"_ustr), SbxOBJECT);
+        m_rParser.aGen.Gen(SbiOpcode::PUTC_);
+    }
+
+    static SbiExpression createLocalVar(SbiParser& rParser)
+    {
+        // Create the unique name
+        OUStringBuffer moduleName(rParser.aGen.GetModule().GetName());
+        for (auto parent = rParser.aGen.GetModule().GetParent(); parent;
+             parent = parent->GetParent())
+            moduleName.insert(0, parent->GetName() + ".");
+
+        OUString uniqueName
+            = "{_with_" + moduleName + "_" + 
OUString::number(rParser.aGen.GetOffset()) + "}";
+        while (rParser.pPool->Find(uniqueName) != nullptr)
+        {
+            static sal_Int64 unique_suffix;
+            uniqueName = "{_with_" + moduleName + "_" + 
OUString::number(rParser.aGen.GetOffset())
+                         + "_" + OUString::number(unique_suffix++) + "}";
+        }
+        SbiSymDef* pWithParentDef = new SbiSymDef(uniqueName);
+        pWithParentDef->SetType(SbxOBJECT);
+        rParser.pPool->Add(pWithParentDef);
+
+        // DIM local variable: work with Option Explicit
+        rParser.aGen.Gen(SbiOpcode::LOCAL_, pWithParentDef->GetId(), 
pWithParentDef->GetType());
+
+        return SbiExpression(&rParser, *pWithParentDef);
+    }
+
+    SbiParser& m_rParser;
+    SbiExpression m_aWithParent;
+};
+}
+
 void SbiParser::With()
 {
     SbiExpression aVar( this, SbOPERAND );
@@ -287,47 +344,15 @@ void SbiParser::With()
     else if( pDef->GetType() != SbxOBJECT )
         Error( ERRCODE_BASIC_NEEDS_OBJECT );
 
-
     pNode->SetType( SbxOBJECT );
 
-    // Generate a '{_with_library.module_offset} = aVar.GetExprNode()'
-    // Use the {_with_library.module_offset} in OpenBlock
-    // The name of the variable can't be used by user: a name like 
[{_with_library.module_offset}]
-    // is valid, but not without the square brackets
-
-    // Create the unique name
-    OUStringBuffer moduleName(aGen.GetModule().GetName());
-    for (auto parent = aGen.GetModule().GetParent(); parent; parent = 
parent->GetParent())
-        moduleName.insert(0, parent->GetName() + ".");
+    std::optional<WithLocalVar> oLocalVar;
+    if (pDef->GetProcDef())
+        oLocalVar.emplace(*this, aVar);
 
-    OUString uniqueName = "{_with_" + moduleName + "_" + 
OUString::number(aGen.GetOffset()) + "}";
-    while (pPool->Find(uniqueName) != nullptr)
-    {
-        static sal_Int64 unique_suffix;
-        uniqueName = "{_with_" + moduleName + "_" + 
OUString::number(aGen.GetOffset()) + "_"
-                     + OUString::number(unique_suffix++) + "}";
-    }
-    SbiSymDef* pWithParentDef = new SbiSymDef(uniqueName);
-    pWithParentDef->SetType(SbxOBJECT);
-    pPool->Add(pWithParentDef);
-
-    // DIM local variable: work with Option Explicit
-    aGen.Gen(SbiOpcode::LOCAL_, pWithParentDef->GetId(), 
pWithParentDef->GetType());
-
-    // Assignment
-    SbiExpression aWithParent(this, *pWithParentDef);
-    aWithParent.Gen();
-    aVar.Gen();
-    aGen.Gen(SbiOpcode::PUTC_);
-
-    OpenBlock(NIL, aWithParent.GetExprNode());
+    OpenBlock(NIL, oLocalVar ? oLocalVar->m_aWithParent.GetExprNode() : 
aVar.GetExprNode());
     StmntBlock( ENDWITH );
     CloseBlock();
-
-    // {_with_library.module_offset} = Nothing
-    aWithParent.Gen();
-    aGen.Gen(SbiOpcode::RTL_, aGblStrings.Add(u"Nothing"_ustr), SbxOBJECT);
-    aGen.Gen(SbiOpcode::PUTC_);
 }
 
 // LOOP/NEXT/WEND without construct

Reply via email to