mirror of https://github.com/KLayout/klayout.git
1532 lines
42 KiB
C++
1532 lines
42 KiB
C++
|
|
/*
|
|
|
|
KLayout Layout Viewer
|
|
Copyright (C) 2006-2017 Matthias Koefferlein
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
|
#include "gsiDecl.h"
|
|
#include "gsiExpression.h"
|
|
#include "gsiObjectHolder.h"
|
|
|
|
#include "tlExpression.h"
|
|
#include "tlLog.h"
|
|
|
|
#include <set>
|
|
#include <map>
|
|
#include <list>
|
|
#include <cstdio>
|
|
#include <algorithm>
|
|
|
|
#include <QMutex>
|
|
#include <QMutexLocker>
|
|
|
|
namespace gsi
|
|
{
|
|
|
|
// -------------------------------------------------------------------
|
|
// Method table implementation
|
|
|
|
/**
|
|
* @brief A single entry in the method table
|
|
* This class provides an entry for one name. It provides flags
|
|
* (ctor) for the method and a list of implementations
|
|
* (gsi::MethodBase objects)
|
|
*/
|
|
class ExpressionMethodTableEntry
|
|
{
|
|
public:
|
|
typedef std::vector<const gsi::MethodBase *>::const_iterator method_iterator;
|
|
|
|
ExpressionMethodTableEntry (const std::string &name)
|
|
: m_name (name)
|
|
{ }
|
|
|
|
const std::string &name () const
|
|
{
|
|
return m_name;
|
|
}
|
|
|
|
void add (const gsi::MethodBase *m)
|
|
{
|
|
m_methods.push_back (m);
|
|
}
|
|
|
|
void finish ()
|
|
{
|
|
// remove duplicate entries in the method list
|
|
std::vector<const gsi::MethodBase *> m = m_methods;
|
|
std::sort(m.begin (), m.end ());
|
|
m_methods.assign (m.begin (), std::unique (m.begin (), m.end ()));
|
|
}
|
|
|
|
method_iterator begin () const
|
|
{
|
|
return m_methods.begin ();
|
|
}
|
|
|
|
method_iterator end () const
|
|
{
|
|
return m_methods.end ();
|
|
}
|
|
|
|
private:
|
|
std::string m_name;
|
|
std::vector<const gsi::MethodBase *> m_methods;
|
|
};
|
|
|
|
/**
|
|
* @brief The method table for a class
|
|
* The method table will provide the methods associated with a native method, i.e.
|
|
* a certain name. It only provides the methods, not a overload resolution strategy.
|
|
*/
|
|
class ExpressionMethodTable
|
|
: public gsi::PerClassClientSpecificData
|
|
{
|
|
public:
|
|
/**
|
|
* @brief Find a method by name and static flag
|
|
* This method will return a pair of true and the method ID if a method with
|
|
* the static attribute and the name is found. Otherwise the first value of
|
|
* the returned pair will be false.
|
|
*/
|
|
std::pair<bool, size_t> find (bool st, const std::string &name) const
|
|
{
|
|
std::map<std::pair<bool, std::string>, size_t>::const_iterator t = m_name_map.find (std::make_pair (st, name));
|
|
if (t != m_name_map.end ()) {
|
|
return std::make_pair (true, t->second);
|
|
} else {
|
|
return std::make_pair (false, 0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Returns the name of the method with ID mid
|
|
*/
|
|
const std::string &name (size_t mid) const
|
|
{
|
|
return m_table [mid].name ();
|
|
}
|
|
|
|
/**
|
|
* @brief Begin iterator for the overloaded methods for method ID mid
|
|
*/
|
|
ExpressionMethodTableEntry::method_iterator begin (size_t mid) const
|
|
{
|
|
return m_table[mid].begin ();
|
|
}
|
|
|
|
/**
|
|
* @brief End iterator for the overloaded methods for method ID mid
|
|
*/
|
|
ExpressionMethodTableEntry::method_iterator end (size_t mid) const
|
|
{
|
|
return m_table[mid].end ();
|
|
}
|
|
|
|
static const ExpressionMethodTable *method_table_by_class (const gsi::ClassBase *cls_decl)
|
|
{
|
|
const ExpressionMethodTable *mt = dynamic_cast<const ExpressionMethodTable *>(cls_decl->gsi_data ());
|
|
tl_assert (mt != 0);
|
|
return mt;
|
|
}
|
|
|
|
static void initialize_class (const gsi::ClassBase *cls_decl)
|
|
{
|
|
ExpressionMethodTable *mtnc = new ExpressionMethodTable (cls_decl);
|
|
cls_decl->set_gsi_data (mtnc);
|
|
}
|
|
|
|
private:
|
|
const gsi::ClassBase *mp_cls_decl;
|
|
std::map<std::pair<bool, std::string>, size_t> m_name_map;
|
|
std::vector<ExpressionMethodTableEntry> m_table;
|
|
|
|
/**
|
|
* @brief Adds the given method with the given name to the list of methods registered under that name
|
|
*/
|
|
void add_method (const std::string &name, const gsi::MethodBase *mb)
|
|
{
|
|
bool st = mb->is_static ();
|
|
|
|
std::map<std::pair<bool, std::string>, size_t>::iterator n = m_name_map.find (std::make_pair (st, name));
|
|
if (n == m_name_map.end ()) {
|
|
|
|
m_name_map.insert (std::make_pair (std::make_pair(st, name), m_table.size ()));
|
|
m_table.push_back (ExpressionMethodTableEntry (name));
|
|
m_table.back ().add (mb);
|
|
|
|
} else {
|
|
|
|
m_table [n->second].add (mb);
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Private ctor - no construction from the outside
|
|
*/
|
|
ExpressionMethodTable ();
|
|
|
|
/**
|
|
* @brief Private ctor - no construction from the outside
|
|
* This constructor will create the method table for the given class.
|
|
*/
|
|
ExpressionMethodTable (const gsi::ClassBase *cls_decl)
|
|
: mp_cls_decl (cls_decl)
|
|
{
|
|
for (gsi::ClassBase::method_iterator m = cls_decl->begin_methods (); m != cls_decl->end_methods (); ++m) {
|
|
|
|
if (! (*m)->is_callback ()) {
|
|
|
|
for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) {
|
|
if (syn->is_setter) {
|
|
add_method (syn->name + "=", *m);
|
|
} else {
|
|
add_method (syn->name, *m);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// do some cleanup
|
|
for (std::vector<ExpressionMethodTableEntry>::iterator m = m_table.begin (); m != m_table.end (); ++m) {
|
|
m->finish ();
|
|
}
|
|
}
|
|
};
|
|
|
|
// -------------------------------------------------------------------
|
|
|
|
/**
|
|
* @brief Fetches the final object pointer from a tl::Variant
|
|
*/
|
|
inline void *get_object (tl::Variant &var)
|
|
{
|
|
return var.to_user ();
|
|
}
|
|
|
|
/**
|
|
* @brief Fetches the object pointer
|
|
* In contrast to get_object, this function will fetch the pointer
|
|
* without trying to create the object and without checking whether
|
|
* it is destroyed already.
|
|
*/
|
|
void *get_object_raw (tl::Variant &var)
|
|
{
|
|
void *obj = 0;
|
|
if (var.type_code () == tl::Variant::t_user) {
|
|
|
|
obj = var.native_ptr ();
|
|
|
|
} else if (var.type_code () == tl::Variant::t_user_ref) {
|
|
|
|
Proxy *p = dynamic_cast<Proxy *> (var.to_object ());
|
|
if (p) {
|
|
obj = p->raw_obj ();
|
|
}
|
|
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
// Test if an argument can be converted to the given type
|
|
|
|
bool test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose);
|
|
|
|
template <class R>
|
|
struct test_arg_func
|
|
{
|
|
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType & /*atype*/, bool /*loose*/)
|
|
{
|
|
*ret = arg.can_convert_to<R> ();
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct test_arg_func<gsi::VoidType>
|
|
{
|
|
void operator () (bool *ret, const tl::Variant & /*arg*/, const gsi::ArgType & /*atype*/, bool /*loose*/)
|
|
{
|
|
*ret = true;
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct test_arg_func<gsi::ObjectType>
|
|
{
|
|
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose)
|
|
{
|
|
// allow nil of pointers
|
|
if ((atype.is_ptr () || atype.is_cptr ()) && arg.is_nil ()) {
|
|
*ret = true;
|
|
return;
|
|
}
|
|
|
|
if (! arg.is_user ()) {
|
|
*ret = false;
|
|
return;
|
|
}
|
|
|
|
const tl::VariantUserClassBase *cls = arg.user_cls ();
|
|
if (! cls) {
|
|
*ret = false;
|
|
} else if (! cls->gsi_cls ()->is_derived_from (atype.cls ()) && (! loose || ! cls->gsi_cls ()->can_convert_to(atype.cls ()))) {
|
|
*ret = false;
|
|
} else if ((atype.is_ref () || atype.is_ptr ()) && cls->is_const ()) {
|
|
*ret = false;
|
|
} else {
|
|
*ret = true;
|
|
}
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct test_arg_func<gsi::VectorType>
|
|
{
|
|
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose)
|
|
{
|
|
if (! arg.is_list ()) {
|
|
*ret = false;
|
|
return;
|
|
}
|
|
|
|
tl_assert (atype.inner () != 0);
|
|
const ArgType &ainner = *atype.inner ();
|
|
|
|
*ret = true;
|
|
for (tl::Variant::const_iterator v = arg.begin (); v != arg.end () && *ret; ++v) {
|
|
if (! test_arg (ainner, *v, loose)) {
|
|
*ret = false;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct test_arg_func<gsi::MapType>
|
|
{
|
|
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType &atype, bool loose)
|
|
{
|
|
// Note: delegating that to the function avoids "injected class name used as template template expression" warning
|
|
if (! arg.is_array ()) {
|
|
*ret = false;
|
|
return;
|
|
}
|
|
|
|
tl_assert (atype.inner () != 0);
|
|
tl_assert (atype.inner_k () != 0);
|
|
const ArgType &ainner = *atype.inner ();
|
|
const ArgType &ainner_k = *atype.inner_k ();
|
|
|
|
if (! arg.is_list ()) {
|
|
*ret = false;
|
|
return;
|
|
}
|
|
|
|
*ret = true;
|
|
for (tl::Variant::const_array_iterator a = arg.begin_array (); a != arg.end_array () && *ret; ++a) {
|
|
if (! test_arg (ainner_k, a->first, loose)) {
|
|
*ret = false;
|
|
} else if (! test_arg (ainner, a->second, loose)) {
|
|
*ret = false;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
bool
|
|
test_arg (const gsi::ArgType &atype, const tl::Variant &arg, bool loose)
|
|
{
|
|
// for const X * or X *, nil is an allowed value
|
|
if ((atype.is_cptr () || atype.is_ptr ()) && arg.is_nil ()) {
|
|
return true;
|
|
}
|
|
|
|
bool ret = false;
|
|
gsi::do_on_type<test_arg_func> () (atype.type (), &ret, arg, atype, loose);
|
|
return ret;
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
// Variant to C conversion
|
|
|
|
template <class R>
|
|
struct var2c
|
|
{
|
|
static R get (const tl::Variant &rval)
|
|
{
|
|
return rval.to<R> ();
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct var2c<tl::Variant>
|
|
{
|
|
static const tl::Variant &get (const tl::Variant &rval)
|
|
{
|
|
return rval;
|
|
}
|
|
};
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Serialization helpers
|
|
|
|
/**
|
|
* @brief An adaptor for a vector which uses the tl::Variant's list perspective
|
|
*/
|
|
class VariantBasedVectorAdaptorIterator
|
|
: public gsi::VectorAdaptorIterator
|
|
{
|
|
public:
|
|
VariantBasedVectorAdaptorIterator (tl::Variant::iterator b, tl::Variant::iterator e, const gsi::ArgType *ainner);
|
|
|
|
virtual void get (SerialArgs &w, tl::Heap &heap) const;
|
|
virtual bool at_end () const;
|
|
virtual void inc ();
|
|
|
|
private:
|
|
tl::Variant::iterator m_b, m_e;
|
|
const gsi::ArgType *mp_ainner;
|
|
};
|
|
|
|
/**
|
|
* @brief An adaptor for a vector which uses the tl::Variant's list perspective
|
|
*/
|
|
class VariantBasedVectorAdaptor
|
|
: public gsi::VectorAdaptor
|
|
{
|
|
public:
|
|
VariantBasedVectorAdaptor (tl::Variant *var, const gsi::ArgType *ainner);
|
|
|
|
virtual VectorAdaptorIterator *create_iterator () const;
|
|
virtual void push (SerialArgs &r, tl::Heap &heap);
|
|
virtual void clear ();
|
|
virtual size_t size () const;
|
|
virtual size_t serial_size () const;
|
|
|
|
private:
|
|
const gsi::ArgType *mp_ainner;
|
|
tl::Variant *mp_var;
|
|
};
|
|
|
|
/**
|
|
* @brief An adaptor for a map which uses the tl::Variant's array perspective
|
|
*/
|
|
class VariantBasedMapAdaptorIterator
|
|
: public gsi::MapAdaptorIterator
|
|
{
|
|
public:
|
|
VariantBasedMapAdaptorIterator (tl::Variant::array_iterator b, tl::Variant::array_iterator e, const gsi::ArgType *ainner, const gsi::ArgType *ainner_k);
|
|
|
|
virtual void get (SerialArgs &w, tl::Heap &heap) const;
|
|
virtual bool at_end () const;
|
|
virtual void inc ();
|
|
|
|
private:
|
|
tl::Variant::array_iterator m_b, m_e;
|
|
const gsi::ArgType *mp_ainner, *mp_ainner_k;
|
|
};
|
|
|
|
/**
|
|
* @brief An adaptor for a vector which uses the tl::Variant's list perspective
|
|
*/
|
|
class VariantBasedMapAdaptor
|
|
: public gsi::MapAdaptor
|
|
{
|
|
public:
|
|
VariantBasedMapAdaptor (tl::Variant *var, const gsi::ArgType *ainner, const gsi::ArgType *ainner_k);
|
|
|
|
virtual MapAdaptorIterator *create_iterator () const;
|
|
virtual void insert (SerialArgs &r, tl::Heap &heap);
|
|
virtual void clear ();
|
|
virtual size_t size () const;
|
|
virtual size_t serial_size () const;
|
|
|
|
private:
|
|
const gsi::ArgType *mp_ainner, *mp_ainner_k;
|
|
tl::Variant *mp_var;
|
|
};
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Writer function for serialization
|
|
|
|
/**
|
|
* @brief Serialization of POD types
|
|
*/
|
|
template <class R>
|
|
struct writer
|
|
{
|
|
void operator() (gsi::SerialArgs *aa, tl::Variant *arg, const gsi::ArgType &atype, tl::Heap *heap)
|
|
{
|
|
if (arg->is_nil () && atype.type () != gsi::T_var) {
|
|
|
|
if (! (atype.is_ptr () || atype.is_cptr ())) {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Arguments of reference or direct type cannot be passed nil")));
|
|
} else if (atype.is_ptr ()) {
|
|
aa->write<R *> ((R *)0);
|
|
} else {
|
|
aa->write<const R *> ((const R *)0);
|
|
}
|
|
|
|
} else {
|
|
|
|
if (atype.is_ref () || atype.is_ptr ()) {
|
|
|
|
// TODO: morph the variant to the requested type and pass it's pointer (requires a non-const reference for arg)
|
|
// -> we would have a reference that can modify the argument (out parameter).
|
|
R *v = new R (var2c<R>::get (*arg));
|
|
heap->push (v);
|
|
|
|
aa->write<void *> (v);
|
|
|
|
} else if (atype.is_cref ()) {
|
|
// Note: POD's are written as copies for const refs, so we can pass a temporary here:
|
|
// (avoids having to create a temp object)
|
|
aa->write<const R &> (var2c<R>::get (*arg));
|
|
} else if (atype.is_cptr ()) {
|
|
// Note: POD's are written as copies for const ptrs, so we can pass a temporary here:
|
|
// (avoids having to create a temp object)
|
|
R r = var2c<R>::get (*arg);
|
|
aa->write<const R *> (&r);
|
|
} else {
|
|
aa->write<R> (var2c<R>::get (*arg));
|
|
}
|
|
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief Serialization for strings
|
|
*/
|
|
template <>
|
|
struct writer<StringType>
|
|
{
|
|
void operator() (gsi::SerialArgs *aa, tl::Variant *arg, const gsi::ArgType &atype, tl::Heap *)
|
|
{
|
|
// Cannot pass ownership currently
|
|
tl_assert (!atype.pass_obj ());
|
|
|
|
if (arg->is_nil ()) {
|
|
|
|
if (! (atype.is_ptr () || atype.is_cptr ())) {
|
|
// nil is treated as an empty string for references
|
|
aa->write<void *> ((void *)new StringAdaptorImpl<std::string> (std::string ()));
|
|
} else {
|
|
aa->write<void *> ((void *)0);
|
|
}
|
|
|
|
} else {
|
|
|
|
// TODO: morph the variant to the requested type and pass it's pointer (requires a non-const reference for arg)
|
|
// -> we would have a reference that can modify the argument (out parameter).
|
|
// NOTE: by convention we pass the ownership to the receiver for adaptors.
|
|
aa->write<void *> ((void *)new StringAdaptorImpl<std::string> (arg->to_string ()));
|
|
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief Specialization for Variant
|
|
*/
|
|
template <>
|
|
struct writer<VariantType>
|
|
{
|
|
void operator() (gsi::SerialArgs *aa, tl::Variant *arg, const gsi::ArgType &, tl::Heap *)
|
|
{
|
|
// TODO: clarify: is nil a zero-pointer to a variant or a pointer to a "nil" variant?
|
|
// NOTE: by convention we pass the ownership to the receiver for adaptors.
|
|
aa->write<void *> ((void *)new VariantAdaptorImpl<tl::Variant> (arg));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief Specialization for Vectors
|
|
*/
|
|
template <>
|
|
struct writer<VectorType>
|
|
{
|
|
void operator() (gsi::SerialArgs *aa, tl::Variant *arg, const gsi::ArgType &atype, tl::Heap *)
|
|
{
|
|
if (arg->is_nil ()) {
|
|
if (! (atype.is_ptr () || atype.is_cptr ())) {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Arguments of reference or direct type cannot be passed nil")));
|
|
} else {
|
|
aa->write<void *> ((void *)0);
|
|
}
|
|
} else {
|
|
tl_assert (atype.inner () != 0);
|
|
aa->write<void *> ((void *)new VariantBasedVectorAdaptor (arg, atype.inner ()));
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief Specialization for Maps
|
|
*/
|
|
template <>
|
|
struct writer<MapType>
|
|
{
|
|
void operator() (gsi::SerialArgs *aa, tl::Variant *arg, const gsi::ArgType &atype, tl::Heap *)
|
|
{
|
|
if (arg->is_nil ()) {
|
|
if (! (atype.is_ptr () || atype.is_cptr ())) {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Arguments of reference or direct type cannot be passed nil")));
|
|
} else {
|
|
aa->write<void *> ((void *)0);
|
|
}
|
|
} else {
|
|
tl_assert (atype.inner () != 0);
|
|
tl_assert (atype.inner_k () != 0);
|
|
aa->write<void *> ((void *)new VariantBasedMapAdaptor (arg, atype.inner (), atype.inner_k ()));
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief Specialization for void
|
|
*/
|
|
template <>
|
|
struct writer<gsi::VoidType>
|
|
{
|
|
void operator() (gsi::SerialArgs *, tl::Variant *, const gsi::ArgType &, tl::Heap *)
|
|
{
|
|
// nothing - void type won't be serialized
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief Specialization for void
|
|
*/
|
|
template <>
|
|
struct writer<gsi::ObjectType>
|
|
{
|
|
void operator() (gsi::SerialArgs *aa, tl::Variant *arg, const gsi::ArgType &atype, tl::Heap *heap)
|
|
{
|
|
if (atype.is_ref () || atype.is_cref () || atype.is_ptr () || atype.is_cptr ()) {
|
|
|
|
if (arg->is_nil ()) {
|
|
|
|
if (atype.is_ref () || atype.is_cref ()) {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Cannot pass nil to reference parameters")));
|
|
}
|
|
|
|
aa->write<void *> ((void *) 0);
|
|
|
|
} else {
|
|
|
|
if (! arg->is_user ()) {
|
|
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ()));
|
|
}
|
|
|
|
const tl::VariantUserClassBase *cls = arg->user_cls ();
|
|
if (!cls) {
|
|
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ()));
|
|
}
|
|
if (cls->is_const () && (atype.is_ref () || atype.is_ptr ())) {
|
|
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Cannot pass a const reference of class %s to a non-const reference or pointer parameter")), atype.cls ()->name ()));
|
|
}
|
|
|
|
if (cls->gsi_cls ()->is_derived_from (atype.cls ())) {
|
|
|
|
if (cls->gsi_cls ()->adapted_type_info ()) {
|
|
// resolved adapted type
|
|
aa->write<void *> ((void *) cls->gsi_cls ()->adapted_from_obj (get_object (*arg)));
|
|
} else {
|
|
aa->write<void *> (get_object (*arg));
|
|
}
|
|
|
|
} else if ((atype.is_cref () || atype.is_cptr ()) && cls->gsi_cls ()->can_convert_to (atype.cls ())) {
|
|
|
|
// We can convert objects for cref and cptr, but ownership over these objects is not transferred.
|
|
// Hence we have to keep them on the heap.
|
|
void *new_obj = atype.cls ()->create_obj_from (cls->gsi_cls (), get_object (*arg));
|
|
heap->push (new gsi::ObjectHolder (atype.cls (), new_obj));
|
|
aa->write<void *> (new_obj);
|
|
|
|
} else {
|
|
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ()));
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (! arg->is_user ()) {
|
|
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ()));
|
|
}
|
|
|
|
const tl::VariantUserClassBase *cls = arg->user_cls ();
|
|
if (!cls) {
|
|
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ()));
|
|
}
|
|
if (cls->is_const () && (atype.is_ref () || atype.is_ptr ())) {
|
|
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Cannot pass a const reference of class %s to a non-const reference or pointer parameter")), atype.cls ()->name ()));
|
|
}
|
|
|
|
if (cls->gsi_cls ()->is_derived_from (atype.cls ())) {
|
|
|
|
if (cls->gsi_cls ()->adapted_type_info ()) {
|
|
aa->write<void *> (cls->gsi_cls ()->create_adapted_from_obj (get_object (*arg)));
|
|
} else {
|
|
aa->write<void *> ((void *) cls->gsi_cls ()->clone (get_object (*arg)));
|
|
}
|
|
|
|
} else if (cls->gsi_cls ()->can_convert_to (atype.cls ())) {
|
|
|
|
aa->write<void *> (atype.cls ()->create_obj_from (cls->gsi_cls (), get_object (*arg)));
|
|
|
|
} else {
|
|
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Unexpected object type (expected argument of class %s)")), atype.cls ()->name ()));
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
};
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Reader function for serialization
|
|
|
|
/**
|
|
* @brief A reader function
|
|
*/
|
|
template <class R>
|
|
struct reader
|
|
{
|
|
void
|
|
operator() (tl::Variant *out, gsi::SerialArgs *rr, const gsi::ArgType &atype, tl::Heap *heap)
|
|
{
|
|
if (atype.is_ref ()) {
|
|
*out = rr->template read<R &> (*heap);
|
|
} else if (atype.is_cref ()) {
|
|
*out = rr->template read<const R &> (*heap);
|
|
} else if (atype.is_ptr ()) {
|
|
R *p = rr->template read<R *> (*heap);
|
|
if (p == 0) {
|
|
*out = tl::Variant ();
|
|
} else {
|
|
*out = *p;
|
|
}
|
|
} else if (atype.is_cptr ()) {
|
|
const R *p = rr->template read<const R *> (*heap);
|
|
if (p == 0) {
|
|
*out = tl::Variant ();
|
|
} else {
|
|
*out = *p;
|
|
}
|
|
} else {
|
|
*out = rr->template read<R> (*heap);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief A reader specialization for void *
|
|
*/
|
|
template <>
|
|
struct reader<void *>
|
|
{
|
|
void
|
|
operator() (tl::Variant *out, gsi::SerialArgs *rr, const gsi::ArgType &atype, tl::Heap *heap)
|
|
{
|
|
tl_assert (!atype.is_ref ());
|
|
tl_assert (!atype.is_cref ());
|
|
tl_assert (!atype.is_ptr ());
|
|
tl_assert (!atype.is_cptr ());
|
|
*out = size_t (rr->read<void *> (*heap));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief A reader specialization for strings
|
|
*/
|
|
template <>
|
|
struct reader<gsi::StringType>
|
|
{
|
|
void
|
|
operator() (tl::Variant *out, gsi::SerialArgs *rr, const gsi::ArgType &, tl::Heap *heap)
|
|
{
|
|
std::auto_ptr<StringAdaptor> a ((StringAdaptor *) rr->read<void *>(*heap));
|
|
if (!a.get ()) {
|
|
*out = tl::Variant ();
|
|
} else {
|
|
*out = tl::Variant (std::string (a->c_str (), a->size ()));
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief A reader specialization for variants
|
|
*/
|
|
template <>
|
|
struct reader<gsi::VariantType>
|
|
{
|
|
void
|
|
operator() (tl::Variant *out, gsi::SerialArgs *rr, const gsi::ArgType &, tl::Heap *heap)
|
|
{
|
|
std::auto_ptr<VariantAdaptor> a ((VariantAdaptor *) rr->read<void *>(*heap));
|
|
if (!a.get ()) {
|
|
*out = tl::Variant ();
|
|
} else {
|
|
*out = a->var ();
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief A reader specialization for maps
|
|
*/
|
|
template <>
|
|
struct reader<MapType>
|
|
{
|
|
void
|
|
operator() (tl::Variant *out, gsi::SerialArgs *rr, const gsi::ArgType &atype, tl::Heap *heap)
|
|
{
|
|
std::auto_ptr<MapAdaptor> a ((MapAdaptor *) rr->read<void *>(*heap));
|
|
if (!a.get ()) {
|
|
*out = tl::Variant ();
|
|
} else {
|
|
tl_assert (atype.inner () != 0);
|
|
tl_assert (atype.inner_k () != 0);
|
|
VariantBasedMapAdaptor t (out, atype.inner (), atype.inner_k ());
|
|
a->copy_to (&t, *heap);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief A reader specialization for const char *
|
|
*/
|
|
template <>
|
|
struct reader<VectorType>
|
|
{
|
|
void
|
|
operator() (tl::Variant *out, gsi::SerialArgs *rr, const gsi::ArgType &atype, tl::Heap *heap)
|
|
{
|
|
std::auto_ptr<VectorAdaptor> a ((VectorAdaptor *) rr->read<void *>(*heap));
|
|
if (!a.get ()) {
|
|
*out = tl::Variant ();
|
|
} else {
|
|
tl_assert (atype.inner () != 0);
|
|
VariantBasedVectorAdaptor t (out, atype.inner ());
|
|
a->copy_to (&t, *heap);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief A reader specialization for objects
|
|
*/
|
|
template <>
|
|
struct reader<ObjectType>
|
|
{
|
|
void
|
|
operator() (tl::Variant *out, gsi::SerialArgs *rr, const gsi::ArgType &atype, tl::Heap *heap)
|
|
{
|
|
void *obj = rr->read<void *> (*heap);
|
|
|
|
bool is_const = atype.is_cptr () || atype.is_cref ();
|
|
bool owner = true;
|
|
if (atype.is_ptr () || atype.is_cptr () || atype.is_ref () || atype.is_cref ()) {
|
|
owner = atype.pass_obj ();
|
|
}
|
|
bool can_destroy = atype.is_ptr () || owner;
|
|
|
|
const gsi::ClassBase *clsact = atype.cls ()->subclass_decl (obj);
|
|
tl_assert (clsact != 0);
|
|
|
|
if (obj == 0) {
|
|
|
|
*out = tl::Variant ();
|
|
|
|
} else if (!clsact->adapted_type_info () && clsact->is_managed ()) {
|
|
|
|
// gsi::ObjectBase-based objects can be managed by reference since they
|
|
// provide a tl::Object through the proxy.
|
|
|
|
*out = tl::Variant ();
|
|
|
|
const tl::VariantUserClassBase *cls = clsact->var_cls (atype.is_cref () || atype.is_cptr ());
|
|
tl_assert (cls != 0);
|
|
|
|
Proxy *proxy = clsact->gsi_object (obj)->find_client<Proxy> ();
|
|
if (proxy) {
|
|
|
|
out->set_user_ref (proxy, cls, false);
|
|
|
|
} else {
|
|
|
|
// establish a new proxy
|
|
proxy = new Proxy (clsact);
|
|
proxy->set (obj, owner, is_const, can_destroy);
|
|
|
|
out->set_user_ref (proxy, cls, owner);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const tl::VariantUserClassBase *cls = 0;
|
|
|
|
if (clsact->adapted_type_info ()) {
|
|
// create an adaptor from an adapted type
|
|
if (owner) {
|
|
obj = clsact->create_from_adapted_consume (obj);
|
|
} else {
|
|
obj = clsact->create_from_adapted (obj);
|
|
}
|
|
cls = clsact->var_cls (false);
|
|
} else {
|
|
cls = clsact->var_cls (is_const);
|
|
}
|
|
|
|
tl_assert (cls != 0);
|
|
*out = tl::Variant ();
|
|
out->set_user (obj, cls, owner);
|
|
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief A reader specialization for new objects
|
|
*/
|
|
template <>
|
|
struct reader<VoidType>
|
|
{
|
|
void
|
|
operator() (tl::Variant *, gsi::SerialArgs *, const gsi::ArgType &, tl::Heap *)
|
|
{
|
|
// nothing - void type won't be serialized
|
|
}
|
|
};
|
|
|
|
// ---------------------------------------------------------------------
|
|
// VariantBasedVectorAdaptorIterator implementation
|
|
|
|
VariantBasedVectorAdaptorIterator::VariantBasedVectorAdaptorIterator (tl::Variant::iterator b, tl::Variant::iterator e, const gsi::ArgType *ainner)
|
|
: m_b (b), m_e (e), mp_ainner (ainner)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void VariantBasedVectorAdaptorIterator::get (SerialArgs &w, tl::Heap &heap) const
|
|
{
|
|
gsi::do_on_type<writer> () (mp_ainner->type (), &w, &*m_b, *mp_ainner, &heap);
|
|
}
|
|
|
|
bool VariantBasedVectorAdaptorIterator::at_end () const
|
|
{
|
|
return m_b == m_e;
|
|
}
|
|
|
|
void VariantBasedVectorAdaptorIterator::inc ()
|
|
{
|
|
++m_b;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// VariantBasedVectorAdaptor implementation
|
|
|
|
VariantBasedVectorAdaptor::VariantBasedVectorAdaptor (tl::Variant *var, const gsi::ArgType *ainner)
|
|
: mp_ainner (ainner), mp_var (var)
|
|
{
|
|
}
|
|
|
|
VectorAdaptorIterator *VariantBasedVectorAdaptor::create_iterator () const
|
|
{
|
|
return new VariantBasedVectorAdaptorIterator (mp_var->begin (), mp_var->end (), mp_ainner);
|
|
}
|
|
|
|
void VariantBasedVectorAdaptor::push (SerialArgs &r, tl::Heap &heap)
|
|
{
|
|
tl::Variant member;
|
|
gsi::do_on_type<reader> () (mp_ainner->type (), &member, &r, *mp_ainner, &heap);
|
|
mp_var->push (member);
|
|
}
|
|
|
|
void VariantBasedVectorAdaptor::clear ()
|
|
{
|
|
mp_var->set_list ();
|
|
}
|
|
|
|
size_t VariantBasedVectorAdaptor::size () const
|
|
{
|
|
return mp_var->size ();
|
|
}
|
|
|
|
size_t VariantBasedVectorAdaptor::serial_size () const
|
|
{
|
|
return mp_ainner->size ();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// VariantBasedMapAdaptorIterator implementation
|
|
|
|
VariantBasedMapAdaptorIterator::VariantBasedMapAdaptorIterator (tl::Variant::array_iterator b, tl::Variant::array_iterator e, const gsi::ArgType *ainner, const gsi::ArgType *ainner_k)
|
|
: m_b (b), m_e (e), mp_ainner (ainner), mp_ainner_k (ainner_k)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void VariantBasedMapAdaptorIterator::get (SerialArgs &w, tl::Heap &heap) const
|
|
{
|
|
// Note: the const_cast is ugly but in this context we won't modify the variant given as the key.
|
|
// And it lets us keep the interface tidy.
|
|
gsi::do_on_type<writer> () (mp_ainner_k->type (), &w, const_cast<tl::Variant *> (&m_b->first), *mp_ainner_k, &heap);
|
|
gsi::do_on_type<writer> () (mp_ainner->type (), &w, &m_b->second, *mp_ainner, &heap);
|
|
}
|
|
|
|
bool VariantBasedMapAdaptorIterator::at_end () const
|
|
{
|
|
return m_b == m_e;
|
|
}
|
|
|
|
void VariantBasedMapAdaptorIterator::inc ()
|
|
{
|
|
++m_b;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// VariantBasedMapAdaptor implementation
|
|
|
|
VariantBasedMapAdaptor::VariantBasedMapAdaptor (tl::Variant *var, const gsi::ArgType *ainner, const gsi::ArgType *ainner_k)
|
|
: mp_ainner (ainner), mp_ainner_k (ainner_k), mp_var (var)
|
|
{
|
|
}
|
|
|
|
MapAdaptorIterator *VariantBasedMapAdaptor::create_iterator () const
|
|
{
|
|
return new VariantBasedMapAdaptorIterator (mp_var->begin_array (), mp_var->end_array (), mp_ainner, mp_ainner_k);
|
|
}
|
|
|
|
void VariantBasedMapAdaptor::insert (SerialArgs &r, tl::Heap &heap)
|
|
{
|
|
tl::Variant k, v;
|
|
gsi::do_on_type<reader> () (mp_ainner_k->type (), &k, &r, *mp_ainner_k, &heap);
|
|
gsi::do_on_type<reader> () (mp_ainner->type (), &v, &r, *mp_ainner, &heap);
|
|
mp_var->insert (k, v);
|
|
}
|
|
|
|
void VariantBasedMapAdaptor::clear ()
|
|
{
|
|
mp_var->set_array ();
|
|
}
|
|
|
|
size_t VariantBasedMapAdaptor::size () const
|
|
{
|
|
return mp_var->array_size ();
|
|
}
|
|
|
|
size_t VariantBasedMapAdaptor::serial_size () const
|
|
{
|
|
return mp_ainner_k->size () + mp_ainner->size ();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Implementation of initialize_expressions
|
|
|
|
class EvalClassFunction
|
|
: public tl::EvalFunction
|
|
{
|
|
public:
|
|
EvalClassFunction (const tl::VariantUserClassBase *var_cls)
|
|
: mp_var_cls (var_cls)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void execute (const tl::ExpressionParserContext & /*context*/, tl::Variant &out, const std::vector<tl::Variant> &args) const
|
|
{
|
|
if (! args.empty ()) {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Class '%s' is not a function - use 'new' to create a new object")), mp_var_cls->name ());
|
|
}
|
|
out = tl::Variant ((void *) 0, mp_var_cls, false);
|
|
}
|
|
|
|
private:
|
|
const tl::VariantUserClassBase *mp_var_cls;
|
|
};
|
|
|
|
void GSI_PUBLIC
|
|
initialize_expressions ()
|
|
{
|
|
// Allow duplicate initialization without any effect
|
|
static bool s_is_initialized = false;
|
|
if (s_is_initialized) {
|
|
return;
|
|
}
|
|
s_is_initialized = true;
|
|
|
|
gsi::initialize ();
|
|
|
|
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes (); ++c) {
|
|
|
|
// install the method table:
|
|
ExpressionMethodTable::initialize_class (&*c);
|
|
|
|
// register a function that creates a class object (use a function to avoid issues with
|
|
// late destruction of global variables which the class object is already gone)
|
|
const tl::VariantUserClassBase *cc = c->var_cls_cls ();
|
|
if (cc) {
|
|
tl::Eval::define_global_function (c->name (), new EvalClassFunction (cc));
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
// VariantUserClassImpl implementation
|
|
|
|
VariantUserClassImpl::VariantUserClassImpl ()
|
|
: mp_cls (0), mp_self (0), mp_object_cls (0), m_is_const (false)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void
|
|
VariantUserClassImpl::initialize (const gsi::ClassBase *cls, const tl::VariantUserClassBase *self, const tl::VariantUserClassBase *object_cls, bool is_const)
|
|
{
|
|
mp_cls = cls;
|
|
mp_self = self;
|
|
mp_object_cls = object_cls;
|
|
m_is_const = is_const;
|
|
}
|
|
|
|
VariantUserClassImpl::~VariantUserClassImpl ()
|
|
{
|
|
mp_cls = 0;
|
|
}
|
|
|
|
bool
|
|
VariantUserClassImpl::has_method (const std::string &method) const
|
|
{
|
|
const gsi::ClassBase *cls = mp_cls;
|
|
|
|
while (cls) {
|
|
if (ExpressionMethodTable::method_table_by_class (cls)->find (false, method).first) {
|
|
return true;
|
|
}
|
|
cls = cls->base ();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
VariantUserClassImpl::equal_impl (void *obj, void *other) const
|
|
{
|
|
if (obj) {
|
|
|
|
if (! has_method ("==")) {
|
|
|
|
// No == method - use object identity
|
|
return (void *) this == other;
|
|
|
|
} else {
|
|
|
|
tl::ExpressionParserContext context;
|
|
|
|
tl::Variant out;
|
|
|
|
tl::Variant object (obj, mp_object_cls, false);
|
|
std::vector<tl::Variant> vv;
|
|
vv.resize (1, tl::Variant ());
|
|
vv[0].set_user (other, mp_object_cls, false);
|
|
|
|
execute_gsi (context, out, object, "==", vv);
|
|
|
|
return out.to_bool ();
|
|
|
|
}
|
|
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool
|
|
VariantUserClassImpl::less_impl (void *obj, void *other) const
|
|
{
|
|
if (obj) {
|
|
|
|
if (! has_method ("<")) {
|
|
|
|
// No < method - use object pointers
|
|
return (void *) this < other;
|
|
|
|
} else {
|
|
|
|
tl::ExpressionParserContext context;
|
|
|
|
tl::Variant out;
|
|
|
|
tl::Variant object (obj, mp_object_cls, false);
|
|
std::vector<tl::Variant> vv;
|
|
vv.resize (1, tl::Variant ());
|
|
vv[0].set_user (other, mp_object_cls, false);
|
|
|
|
execute_gsi (context, out, object, "<", vv);
|
|
|
|
return out.to_bool ();
|
|
|
|
}
|
|
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::string
|
|
VariantUserClassImpl::to_string_impl (void *obj) const
|
|
{
|
|
if (obj) {
|
|
|
|
if (! has_method ("to_s")) {
|
|
|
|
// no method to convert the object to a string
|
|
return std::string ();
|
|
|
|
} else {
|
|
|
|
tl::ExpressionParserContext context;
|
|
|
|
tl::Variant out;
|
|
|
|
tl::Variant object (obj, mp_object_cls, false);
|
|
std::vector<tl::Variant> vv;
|
|
|
|
execute_gsi (context, out, object, "to_s", vv);
|
|
|
|
return out.to_string ();
|
|
|
|
}
|
|
|
|
} else {
|
|
return std::string ();
|
|
}
|
|
}
|
|
|
|
void
|
|
VariantUserClassImpl::execute (const tl::ExpressionParserContext &context, tl::Variant &out, tl::Variant &object, const std::string &method, const std::vector<tl::Variant> &args) const
|
|
{
|
|
if (mp_object_cls == 0 && method == "is_a") {
|
|
|
|
if (args.size () != 1) {
|
|
throw tl::EvalError (tl::to_string (QObject::tr ("'is_a' method requires exactly one argument")), context);
|
|
}
|
|
|
|
bool ret = false;
|
|
if (args [0].is_user ()) {
|
|
const tl::VariantUserClassBase *ub = args [0].user_cls ();
|
|
if (ub && ub->gsi_cls () == mp_cls) {
|
|
ret = true;
|
|
}
|
|
}
|
|
|
|
out = ret;
|
|
|
|
} else if (mp_object_cls != 0 && method == "new" && args.size () == 0) {
|
|
|
|
void *obj = mp_cls->create ();
|
|
if (obj) {
|
|
|
|
if (mp_cls->is_managed ()) {
|
|
|
|
Proxy *proxy = new Proxy (mp_cls);
|
|
proxy->set (obj, true, false, true);
|
|
|
|
// gsi::Object based objects are managed through a Proxy and
|
|
// shared pointers within tl::Variant. That mean: copy by reference.
|
|
out.set_user_ref (proxy, mp_object_cls, true);
|
|
|
|
} else {
|
|
out.set_user (obj, mp_object_cls, true);
|
|
}
|
|
|
|
} else {
|
|
out.reset ();
|
|
}
|
|
|
|
} else if (mp_object_cls == 0 && method == "dup") {
|
|
|
|
if (args.size () != 0) {
|
|
throw tl::EvalError (tl::to_string (QObject::tr ("'dup' method does not allow arguments")), context);
|
|
}
|
|
|
|
void *obj = mp_cls->create ();
|
|
if (obj) {
|
|
|
|
mp_cls->assign (obj, get_object (object));
|
|
|
|
if (mp_cls->is_managed ()) {
|
|
|
|
Proxy *proxy = new Proxy (mp_cls);
|
|
proxy->set (obj, true, false, true);
|
|
|
|
// gsi::Object based objects are managed through a Proxy and
|
|
// shared pointers within tl::Variant. That mean: copy by reference.
|
|
out.set_user_ref (proxy, mp_cls->var_cls (false), true);
|
|
|
|
} else {
|
|
out.set_user (obj, mp_cls->var_cls (false), true);
|
|
}
|
|
|
|
} else {
|
|
out.reset ();
|
|
}
|
|
|
|
} else {
|
|
try {
|
|
execute_gsi (context, out, object, method, args);
|
|
} catch (tl::EvalError &) {
|
|
throw;
|
|
} catch (tl::Exception &ex) {
|
|
throw tl::EvalError (ex.msg (), context);
|
|
}
|
|
}
|
|
}
|
|
|
|
static tl::Variant
|
|
special_method_impl (gsi::MethodBase::special_method_type smt, tl::Variant &self, const std::vector<tl::Variant> &args)
|
|
{
|
|
if (smt == gsi::MethodBase::Destroy) {
|
|
self.user_destroy ();
|
|
} else if (smt == gsi::MethodBase::Keep) {
|
|
// nothing to do here for GSI objects
|
|
} else if (smt == gsi::MethodBase::Release) {
|
|
// nothing to do here for GSI objects
|
|
} else if (smt == gsi::MethodBase::Create) {
|
|
// nothing to do here for GSI objects
|
|
} else if (smt == gsi::MethodBase::IsConst) {
|
|
return tl::Variant (self.user_is_const ());
|
|
} else if (smt == gsi::MethodBase::Destroyed) {
|
|
|
|
if (self.type_code () == tl::Variant::t_user) {
|
|
return self.to_user () == 0;
|
|
} else if (self.type_code () == tl::Variant::t_user_ref) {
|
|
Proxy *proxy = dynamic_cast<Proxy *> (self.to_object ());
|
|
if (proxy) {
|
|
return proxy->destroyed ();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
|
|
} else if (smt == gsi::MethodBase::Assign) {
|
|
tl_assert (args.size () == 1);
|
|
if (!args.front ().is_user () || self.user_cls () != args.front ().user_cls ()) {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Source and target object must be of the same type for assignment")));
|
|
}
|
|
self.user_assign (args.front ());
|
|
} else if (smt == gsi::MethodBase::Dup) {
|
|
return self.user_dup ();
|
|
}
|
|
|
|
return tl::Variant ();
|
|
}
|
|
|
|
void
|
|
VariantUserClassImpl::execute_gsi (const tl::ExpressionParserContext & /*context*/, tl::Variant &out, tl::Variant &object, const std::string &method, const std::vector<tl::Variant> &args) const
|
|
{
|
|
tl_assert (object.is_user ());
|
|
|
|
const gsi::ClassBase *clsact = mp_cls;
|
|
if (clsact) {
|
|
// determine the real class of the object (it may be a subclass)
|
|
void *obj = get_object_raw (object);
|
|
if (obj) {
|
|
clsact = clsact->subclass_decl (obj);
|
|
}
|
|
}
|
|
|
|
const ExpressionMethodTable *mt = 0;
|
|
size_t mid = 0;
|
|
|
|
const gsi::ClassBase *cls = clsact;
|
|
while (cls) {
|
|
|
|
mt = ExpressionMethodTable::method_table_by_class (cls);
|
|
std::pair<bool, size_t> t = mt->find (mp_object_cls != 0 /*static*/, method);
|
|
if (t.first) {
|
|
mid = t.second;
|
|
break;
|
|
}
|
|
|
|
cls = cls->base ();
|
|
|
|
}
|
|
|
|
if (cls == 0) {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("Unknown method")) + " '" + method + "' of class '" + clsact->name () + "'");
|
|
}
|
|
|
|
const gsi::MethodBase *meth = 0;
|
|
int candidates = 0;
|
|
|
|
for (ExpressionMethodTableEntry::method_iterator m = mt->begin (mid); m != mt->end (mid); ++m) {
|
|
|
|
if ((*m)->is_signal()) {
|
|
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Signals are not supported inside expressions (event %s)")), method.c_str ()));
|
|
} else if ((*m)->is_callback()) {
|
|
// ignore callbacks
|
|
} else if ((*m)->compatible_with_num_args (args.size ())) {
|
|
++candidates;
|
|
meth = *m;
|
|
}
|
|
|
|
}
|
|
|
|
// no candidate -> error
|
|
if (! meth) {
|
|
|
|
std::set<unsigned int> nargs;
|
|
for (ExpressionMethodTableEntry::method_iterator m = mt->begin (mid); m != mt->end (mid); ++m) {
|
|
nargs.insert (std::distance ((*m)->begin_arguments (), (*m)->end_arguments ()));
|
|
}
|
|
std::string nargs_s;
|
|
for (std::set<unsigned int>::const_iterator na = nargs.begin (); na != nargs.end (); ++na) {
|
|
if (na != nargs.begin ()) {
|
|
nargs_s += "/";
|
|
}
|
|
nargs_s += tl::to_string (*na);
|
|
}
|
|
|
|
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Invalid number of arguments for method %s, class %s (got %d, expected %s)")), method.c_str (), mp_cls->name (), int (args.size ()), nargs_s));
|
|
}
|
|
|
|
// more than one candidate -> refine by checking the arguments
|
|
if (candidates > 1) {
|
|
|
|
meth = 0;
|
|
candidates = 0;
|
|
int score = 0;
|
|
bool const_matching = true;
|
|
|
|
for (ExpressionMethodTableEntry::method_iterator m = mt->begin (mid); m != mt->end (mid); ++m) {
|
|
|
|
if (! (*m)->is_callback () && ! (*m)->is_signal ()) {
|
|
|
|
// check arguments (count and type)
|
|
bool is_valid = (*m)->compatible_with_num_args (args.size ());
|
|
int sc = 0;
|
|
int i = 0;
|
|
for (gsi::MethodBase::argument_iterator a = (*m)->begin_arguments (); is_valid && i < int (args.size ()) && a != (*m)->end_arguments (); ++a, ++i) {
|
|
if (test_arg (*a, args [i], false /*strict*/)) {
|
|
++sc;
|
|
} else if (test_arg (*a, args [i], true /*loose*/)) {
|
|
// non-scoring match
|
|
} else {
|
|
is_valid = false;
|
|
}
|
|
}
|
|
|
|
if (is_valid) {
|
|
|
|
// constness matching candidates have precedence
|
|
if ((*m)->is_const () != m_is_const) {
|
|
if (const_matching && candidates > 0) {
|
|
is_valid = false;
|
|
} else {
|
|
const_matching = false;
|
|
}
|
|
} else if (! const_matching) {
|
|
const_matching = true;
|
|
candidates = 0;
|
|
}
|
|
|
|
}
|
|
|
|
if (is_valid) {
|
|
|
|
// otherwise take the candidate with the better score
|
|
if (candidates > 0 && sc > score) {
|
|
candidates = 1;
|
|
meth = *m;
|
|
score = sc;
|
|
} else if (candidates == 0 || sc == score) {
|
|
++candidates;
|
|
meth = *m;
|
|
score = sc;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (! meth) {
|
|
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("No method with matching arguments for method %s, class %s")), method.c_str (), mp_cls->name ()));
|
|
}
|
|
|
|
if (candidates > 1) {
|
|
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Ambiguous overload variants for method %s, class %s - multiple method declarations match arguments")), method.c_str (), mp_cls->name ()));
|
|
}
|
|
|
|
if (m_is_const && ! meth->is_const ()) {
|
|
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Cannot call non-const method %s, class %s on a const reference")), method.c_str (), mp_cls->name ()));
|
|
}
|
|
|
|
if (meth->is_signal ()) {
|
|
|
|
// TODO: events not supported yet
|
|
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Signals not supported yet (method %s, class %s)")), method.c_str (), mp_cls->name ()));
|
|
|
|
} else if (meth->smt () != gsi::MethodBase::None) {
|
|
|
|
out = special_method_impl (meth->smt (), object, args);
|
|
|
|
} else {
|
|
|
|
gsi::SerialArgs arglist (meth->argsize ());
|
|
tl::Heap heap;
|
|
size_t narg = 0;
|
|
for (gsi::MethodBase::argument_iterator a = meth->begin_arguments (); a != meth->end_arguments () && narg < args.size (); ++a, ++narg) {
|
|
// Note: this const_cast is ugly, but it will basically enable "out" parameters
|
|
// TODO: clean this up.
|
|
gsi::do_on_type<writer> () (a->type (), &arglist, const_cast<tl::Variant *> (&args [narg]), *a, &heap);
|
|
}
|
|
|
|
SerialArgs retlist (meth->retsize ());
|
|
|
|
meth->call (get_object (object), arglist, retlist);
|
|
|
|
if (meth->ret_type ().is_iter ()) {
|
|
// TODO: iterators not supported yet
|
|
throw tl::Exception (tl::sprintf (tl::to_string (QObject::tr ("Iterators not supported yet (method %s, class %s)")), method.c_str (), mp_cls->name ()));
|
|
} else {
|
|
out = tl::Variant ();
|
|
gsi::do_on_type<reader> () (meth->ret_type ().type (), &out, &retlist, meth->ret_type (), &heap);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|