klayout/src/gsi/gsiExpression.cc

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);
}
}
}
}