Search code examples
qore

Passing value by reference to Qore script function from C++ code


I need pass returnValue to a method as argument passed by reference and adjust original var value when function id done. So using ReferenceArgumentHelper class.

What's wrong in code bellow when returnValue is unintentionally deleted (when it is a node, i.e. string) and valgrind detects it. callMethod("onFunctionExit" calls an Qore script method and I can see there correct returnValue value. I suspect it's deleted when exiting onFunctionExit when ReferenceArgumentHelper is destroyed. rah.getArg() references reference variable, so it should not be deleted in callMethod.

   DLLLOCAL ThreadDebugEnum callMethod(const char* name, const ThreadDebugEnum defaultResult, QoreProgram *pgm, int paramCount, AbstractQoreNode** params, ExceptionSink* xsink) {
      int rv;
      QoreListNode* l = new QoreListNode();
      qore_program_to_object_map_t::iterator i = qore_program_to_object_map.find(pgm);
      if (i == qore_program_to_object_map.end()) {
         return defaultResult;
      }
      i->second->ref();
      l->push(i->second);
      for (int i=0; i<paramCount; i++) {
         if (params[i])
               params[i]->ref();
         l->push(params[i]);
      }
      rv = qo->intEvalMethod(name, l, xsink);
      l->deref(xsink);
      return (ThreadDebugEnum) rv;
   }

   DLLLOCAL virtual ThreadDebugEnum onFunctionExit(QoreProgram *pgm, const StatementBlock *blockStatement, QoreValue& returnValue, ExceptionSink* xsink) {
      AbstractQoreNode* params[2];
      params[0] = getLocation(blockStatement);
      ReferenceArgumentHelper rah(returnValue.takeNode(), xsink); // grab node from returnValue and pass to helper
      params[1] = rah.getArg(); // caller owns ref
      ThreadDebugEnum rv = callMethod("onFunctionExit", DBG_SB_RUN, pgm, 2, params, xsink);
      AbstractQoreNode* rc = rah.getOutputValue();  // caller owns ref
      returnValue.assign(rc);  // takes reference
//      returnValue.ref();
      }
      return rv;
   }

When looking deeply I did not get why compiler is happy with code in /lib/ReferenceArgumentHelper.cpp:

struct lvih_intern {
   LocalVar lv;
   ExceptionSink* xsink;
   ReferenceNode* ref;

   DLLLOCAL lvih_intern(AbstractQoreNode* val, ExceptionSink* xs) : lv("ref_arg_helper", 0), xsink(xs) {
      printd(5, "ReferenceArgumentHelper::ReferenceArgumentHelper() instantiating %p (val: %p type: '%s') \n", &lv, val, val ? val->getTypeName() : "n/a");
      lv.instantiate(val);   <--------------
      VarRefNode* vr = new VarRefNode(strdup("ref_arg_helper"), VT_LOCAL);
      vr->ref.id = &lv;
      ref = new ReferenceNode(vr, 0, vr, 0);
   }

class LocalVar {
....
   DLLLOCAL void instantiate(QoreValue nval) const {

What is behind conversion AbstractQoreNode* to QoreValue in method call? I did not find an overloaded operator or so. I'm looking what exactly happens with references.


Solution

  • ** EDIT **

    To make a long story short, ReferenceArgumentHelper was buggy; it hadn't been used in years and was not up to date. The class has been fixed which should fix your issue I hope.

    Thank you for pointing this out, and let me know if you have any further problems with this or the fix to the affected code.