bool QoreSmokeBinding::callMethod(Smoke::Index method, void *obj, Smoke::Stack args, bool isAbstract)
{
     //printd(0, "QoreSmokeBinding::callMethod() %s::%s() method=%d obj=%p isAbstract=%s (virt=%s)\n", smoke->classes[smoke->methods[method].classId].className, smoke->methodNames[smoke->methods[method].name], method, obj, isAbstract ? "true" : "false", qore_smoke_is_virtual() ? "true" : "false");

     if (qore_smoke_is_virtual()) {
          qore_smoke_clear_virtual();
          //printd(0, "QoreSmokeBinding::callMethod() qore_smoke_is_virtual, returning to Smoke\n");
          return false;
     }

     ExceptionSink xsink;
     Smoke::Method meth = smoke->methods[method];
     const char * cname = smoke->classes[meth.classId].className;
     const char * mname = smoke->methodNames[meth.name];

     QoreClass *qc;
     QoreObject *o = getQoreObject(meth.classId, obj, qc);

//     if (!strcmp(mname, "paintEngine"))
//        printd(0, "QoreSmokeBinding::callMethod() %s::%s() qore object=%p\n", cname, mname, o);

     if (!o) {
          // we must have an implementation for abstract methods
#ifdef DEBUG
          if (isAbstract) {
               printd(0, "trying to execute pure virtual method %s::%s() obj=%p\n", cname, mname, obj);
               xsink.raiseException("QT-ABSTRACT-METHOD-ERROR", "The Qt library tried to execute pure virtual %s::%s(), but the object has been deleted", cname, mname);
               xsink.handleExceptions();
          }
#endif
          assert(!isAbstract);
          return false;
     }

     if (!strcmp(mname, "qt_metacall")) {
          printd(5, "QoreSmokeBinding::callMethod() className: %s::%s obj: %p\n", cname, mname, obj);

          // get the QoreSmokePrivate info
          ReferenceHolder<QoreSmokePrivateQObjectData> qsp(reinterpret_cast<QoreSmokePrivateQObjectData *>(o->getReferencedPrivateData(QC_QOBJECT->getID(), &xsink)), &xsink);
          if (!qsp) {
               assert(xsink);
               return false;
          }

          // if the object has been deleted, or is not a signal/slot invokation, then return false
          if (!qsp->qobject() || args[1].s_enum != QMetaObject::InvokeMetaMethod)
               return false;

          int mc = qsp->getParentMethodCount();
          // see if call is for a method in a parent class
          if (args[2].s_int < mc)
               return false;

          // get method offset in this class
          args[2].s_int -= mc;

          printd(5, "QoreSmokeBinding::callMethod() %s::%s() method=%d obj=%p qsp=%p isAbstract=%s\n", smoke->classes[smoke->methods[method].classId].className, smoke->methodNames[smoke->methods[method].name], method, obj, *qsp, isAbstract ? "true" : "false");

          // handle the signal, which is connected to a dynamic method (signal or slot)
          qsp->handleSignal(0, args[2].s_int, (void**)args[3].s_voidp);

          args[0].s_int = -1;
          return true;
     }

     // first setup arguments so a user method variant can be found
     Smoke::Index *idx = smoke->argumentList + meth.args;
     type_vec_t typeInfoList;
     while (*idx) {
          Smoke::Type &t = smoke->types[*idx];
          idx++;

          // get argument type to find variant
          bool valid;
          const QoreTypeInfo *typeInfo = getQtTypeInfo(t, valid);
          // if we have an unrecognized type, then we cannot have overloaded this method either
          if (!typeInfo || !valid) {
               assert(!isAbstract);
               return false;
          }
          typeInfoList.push_back(typeInfo);
     }

     // see if there is a user method at all
     const QoreMethod *qmethod;
     const QoreExternalMethodVariant *variant = o->getClass()->findUserMethodVariant(mname, qmethod, typeInfoList);
     printd(5, "QoreSmokeBinding::callMethod() virtual method %s::%s() method=%p variant=%p isAbstract=%d\n", o->getClassName(), mname, qmethod, variant, isAbstract);
     if (!variant) {
          //printd(0, "QoreSmokeBinding::callMethod() virtual method %s::%s() not found\n", o->getClassName(), mname);
          if (!isAbstract)
               return false;
          xsink.raiseException("QT-ABSTRACT-METHOD-ERROR", "The Qt library tried to execute pure virtual %s::%s(), but this method is not implemented in the %s class", o->getClassName(), mname, o->getClassName());
          // FIXME: instead of assert'ing, since the function is abstract, we have to
          // create the default value for the return type and return true
#ifdef DEBUG
          // call this so we can see the exception before the process aborts
          xsink.handleExceptions();
          assert(false);
#endif
          return true;
     }

     //printd(0, "QoreSmokeBinding::callMethod() creating args list for %s::%s() (arg count: %d)\n", o->getClassName(), mname, typeList.size());

     ReferenceHolder<QoreListNode> qoreArgs(new QoreListNode(), &xsink);

     QList<Smoke::Type> typeList;
     idx = smoke->argumentList + meth.args;
     int i = 1;
     while (*idx) {
          Smoke::Type &t = smoke->types[*(idx++)];
          qoreArgs->push(Marshalling::stackToQore(smoke, t, args[i++], &xsink));

          if (xsink) {
               if (!isAbstract)
                    return false;

               // FIXME: instead of assert'ing, since the function is abstract, we have to
               // create the default value for the return type and return true
#ifdef DEBUG
               // call this so we can see the exception before the process aborts
               xsink.handleExceptions();
               assert(false);
#endif
               return true;
          }
     }

     //printd(5, "QoreSmokeBinding::callMethod() calling method smoke=%s::%s(), qore=%s::%s() args=%d valid=%d xsink=%d\n", cname, mname, qoreMethod->getClassName(), mname, typeList.size(), o->isValid(), (bool)xsink);
     ReferenceHolder<AbstractQoreNode> aNode(o->evalMethodVariant(*qmethod, variant, *qoreArgs, &xsink), &xsink);

     Smoke::Type &rt = smoke->types[meth.ret];
     if (CommonQoreMethod::qoreToStackStatic(smoke, &xsink, args[0], cname, mname, rt, *aNode, -1, 0, true) == -1) {
          if (!isAbstract)
               return false;
          // FIXME: instead of assert'ing, since the function is abstract, we have to
          // create the default value for the return type and return true
#ifdef DEBUG
          // call this so we can see the exception before the process aborts
          xsink.handleExceptions();
          assert(false);
#endif
          return true;
     }

     return true;
}
Beispiel #2
0
// returns a RunTimeObjectMethodReference or NULL if there's an exception
QoreValue ParseObjectMethodReferenceNode::evalValueImpl(bool& needs_deref, ExceptionSink* xsink) const {
   // evaluate lvalue expression
   ReferenceHolder<AbstractQoreNode> lv(exp->eval(xsink), xsink);
   if (*xsink)
      return QoreValue();

   QoreObject* o = (*lv) && (*lv)->getType() == NT_OBJECT ? reinterpret_cast<QoreObject*>(*lv) : 0;
   if (!o) {
      xsink->raiseException("OBJECT-METHOD-REFERENCE-ERROR", "expression does not evaluate to an object");
      return QoreValue();
   }

   //printd(5, "ParseObjectMethodReferenceNode::evalImpl() this: %p o: %p %s::%s() m: %p\n", this, o, o->getClassName(), method.c_str(), m);

   const QoreClass* oc = o->getClass();

   // find the method at runtime if necessary
   if (!m) {
      // serialize method resolution at runtime
      AutoLocker al(lck);
      // check m again inside the lock (this way we avoid the lock in the common case where the method has already been resolved)
      if (!m) {
	 bool m_priv = false;
	 m = oc->findMethod(method.c_str(), m_priv);
	 if (!m) {
	    m = oc->findStaticMethod(method.c_str(), m_priv);
	    if (!m) {
	       xsink->raiseException("OBJECT-METHOD-REFERENCE-ERROR", "cannot resolve reference to %s::%s(): unknown method", o->getClassName(), method.c_str());
	       return QoreValue();
	    }
	 }
	 
	 if (m_priv && !qore_class_private::runtimeCheckPrivateClassAccess(*oc)) {
	    if (m->isPrivate())
	       xsink->raiseException("ILLEGAL-CALL-REFERENCE", "cannot create a call reference to private %s::%s() from outside the class", o->getClassName(), method.c_str());
	    else
	       xsink->raiseException("ILLEGAL-CALL-REFERENCE", "cannot create a call reference to %s::%s() because the parent class that implements the method (%s::%s()) is privately inherited", o->getClassName(), method.c_str(), m->getClass()->getName(), method.c_str());
	    
	    return QoreValue();
	 }
      }
   }

   if (oc == m->getClass() || oc == qc)
      return new RunTimeResolvedMethodReferenceNode(o, m);
   return new RunTimeObjectMethodReferenceNode(o, method.c_str());
}