WIP: more python modules, made gsi initialization safe against duplicate init.

This commit is contained in:
Matthias Koefferlein 2018-05-31 20:51:09 +02:00
parent 73297a3810
commit 07d9363ea7
13 changed files with 351 additions and 146 deletions

View File

@ -35,16 +35,15 @@ namespace gsi
void GSI_PUBLIC
initialize ()
{
// Allow duplicate initialization without any effect
static bool s_is_initialized = false;
if (s_is_initialized) {
// do something only if there are new classes
if (gsi::ClassBase::begin_new_classes () == gsi::ClassBase::end_new_classes ()) {
return;
}
s_is_initialized = true;
tl::SelfTimer timer (tl::verbosity () >= 21, "Initializing script environment");
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes (); ++c) {
// Do a first initialization of the new classes because they might add more classes
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_new_classes (); c != gsi::ClassBase::end_new_classes (); ++c) {
// TODO: get rid of that const cast
(const_cast<gsi::ClassBase *> (&*c))->initialize ();
}
@ -52,6 +51,7 @@ initialize ()
// merge the extensions to the main declaration
gsi::ClassBase::merge_declarations ();
// do a full re-initialization - maybe merge_declarations modified existing classes too
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes (); ++c) {
// Initialize the method table once again after we have merged the declarations
// TODO: get rid of that const cast
@ -61,6 +61,9 @@ initialize ()
tl_assert (c->declaration () == &*c);
}
// build or rebuild the variant user class table
// NOTE: as the variant classes are tied to the gsi::Class objects, we can rebuild the table
// and will get the same pointers for the classes that have been there before
tl::VariantUserClassBase::clear_class_table ();
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes (); ++c) {

View File

@ -38,15 +38,36 @@ namespace gsi
// ClassBase implementation
ClassBase::class_collection *ClassBase::mp_class_collection = 0;
ClassBase::class_collection *ClassBase::mp_new_class_collection = 0;
namespace {
struct type_info_compare
{
bool operator() (const std::type_info *a, const std::type_info *b) const
{
return a->before (*b);
}
};
}
// TODO: thread-safe? Unlikely that multiple threads access this member -
// we do a initial scan and after this no more write access here.
static std::map<const std::type_info *, const ClassBase *, type_info_compare> s_ti_to_class;
ClassBase::ClassBase (const std::string &doc, const Methods &mm, bool do_register)
: mp_base (0), mp_parent (0), m_doc (doc), m_methods (mm)
{
if (do_register) {
if (! mp_class_collection) {
mp_class_collection = new class_collection ();
// enter the class into the "new" collection
if (! mp_new_class_collection) {
mp_new_class_collection = new class_collection ();
}
mp_class_collection->push_back (this);
mp_new_class_collection->push_back (this);
// invalidate the "typeinfo to class" map
s_ti_to_class.clear ();
}
}
@ -186,6 +207,17 @@ ClassBase::collection ()
}
}
const ClassBase::class_collection &
ClassBase::new_collection ()
{
if (!mp_new_class_collection) {
static const class_collection empty;
return empty;
} else {
return *mp_new_class_collection;
}
}
static SpecialMethod *
sm_default_ctor (const char *name, const gsi::ClassBase *cls)
{
@ -340,127 +372,132 @@ sm_assign (const char *name, const gsi::ClassBase *cls)
void
ClassBase::merge_declarations ()
{
tl_assert (mp_class_collection != 0);
if (gsi::ClassBase::begin_new_classes () == gsi::ClassBase::end_new_classes ()) {
// Nothing to do.
return;
}
// merge the extensions to the main declaration
static bool merged = false;
if (! merged) {
// HINT: this code block must be called exactly ONCE!
merged = true;
// Check for duplicate declarations
std::set<const std::type_info *> types;
std::set<std::string> names;
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes (); ++c) {
if (c->declaration () == &*c && !types.insert (&c->type ()).second) {
tl::warn << "Duplicate GSI declaration of type " << c->type ().name ();
}
if (c->declaration () == &*c && !names.insert (c->declaration ()->name ()).second) {
tl::warn << "Duplicate GSI declaration of name " << c->declaration ()->name ();
}
// Check for duplicate declarations
std::set<const std::type_info *> types;
std::set<std::string> names;
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes (); ++c) {
if (c->declaration () == &*c && !types.insert (&c->type ()).second) {
tl::warn << "Duplicate GSI declaration of type " << c->type ().name ();
}
std::vector <const gsi::ClassBase *> to_remove;
// Consolidate the classes (merge, remove etc.)
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes (); ++c) {
if (! c->consolidate()) {
to_remove.push_back (&*c);
}
if (c->declaration () == &*c && !names.insert (c->declaration ()->name ()).second) {
tl::warn << "Duplicate GSI declaration of name " << c->declaration ()->name ();
}
}
// removed the classes which are no longer required
for (std::vector <const gsi::ClassBase *>::const_iterator ed = to_remove.begin (); ed != to_remove.end (); ++ed) {
std::vector <const gsi::ClassBase *> to_remove;
// Consolidate the classes (merge, remove etc.)
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_new_classes (); c != gsi::ClassBase::end_new_classes (); ++c) {
if (! c->consolidate()) {
to_remove.push_back (&*c);
}
}
// removed the classes which are no longer required
for (std::vector <const gsi::ClassBase *>::const_iterator ed = to_remove.begin (); ed != to_remove.end (); ++ed) {
// TODO: ugly const_cast hack
mp_new_class_collection->erase (const_cast<gsi::ClassBase *> (*ed));
}
// collect the subclasses of a class
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_new_classes (); c != gsi::ClassBase::end_new_classes (); ++c) {
if (c->base ()) {
// TODO: ugly const_cast hack
mp_class_collection->erase (const_cast<gsi::ClassBase *> (*ed));
const_cast<gsi::ClassBase *> (c->base ())->m_subclasses.push_back (const_cast<gsi::ClassBase *> (c.operator-> ()));
}
}
// collect the subclasses of a class
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes (); ++c) {
if (c->base ()) {
// TODO: ugly const_cast hack
const_cast<gsi::ClassBase *> (c->base ())->m_subclasses.push_back (const_cast<gsi::ClassBase *> (c.operator-> ()));
// Add to the classes the special methods and clean up the method table
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_new_classes (); c != gsi::ClassBase::end_new_classes (); ++c) {
std::set<std::pair<std::string, bool> > name_map;
for (gsi::ClassBase::method_iterator m = c->begin_methods (); m != c->end_methods (); ++m) {
for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) {
name_map.insert (std::make_pair (syn->name, (*m)->is_static ()));
}
}
// Add to the classes the special methods and clean up the method table
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes (); ++c) {
// We don't want the declaration object to be non-const except for this case. So
// we const_cast here.
gsi::ClassBase *non_const_decl = const_cast<gsi::ClassBase *> (c.operator-> ());
std::set<std::pair<std::string, bool> > name_map;
for (gsi::ClassBase::method_iterator m = c->begin_methods (); m != c->end_methods (); ++m) {
for (gsi::MethodBase::synonym_iterator syn = (*m)->begin_synonyms (); syn != (*m)->end_synonyms (); ++syn) {
name_map.insert (std::make_pair (syn->name, (*m)->is_static ()));
}
}
if (name_map.find (std::make_pair ("new", true)) == name_map.end ()) {
non_const_decl->add_method (sm_default_ctor ("new", &*c), false);
}
// We don't want the declaration object to be non-const except for this case. So
// we const_cast here.
gsi::ClassBase *non_const_decl = const_cast<gsi::ClassBase *> (&*c);
// Note: "unmanage" and "manage" is a better name ...
non_const_decl->add_method (sm_keep ("_unmanage"));
non_const_decl->add_method (sm_release ("_manage"));
if (name_map.find (std::make_pair ("new", true)) == name_map.end ()) {
non_const_decl->add_method (sm_default_ctor ("new", &*c), false);
}
if (name_map.find (std::make_pair ("create", false)) == name_map.end ()) {
// deprecate "create"
non_const_decl->add_method (sm_create ("_create|#create"));
} else {
// fallback name is "_create" to avoid conflicts
non_const_decl->add_method (sm_create ("_create"));
}
// Note: "unmanage" and "manage" is a better name ...
non_const_decl->add_method (sm_keep ("_unmanage"));
non_const_decl->add_method (sm_release ("_manage"));
if (name_map.find (std::make_pair ("create", false)) == name_map.end ()) {
// deprecate "create"
non_const_decl->add_method (sm_create ("_create|#create"));
if (c->can_destroy ()) {
if (name_map.find (std::make_pair ("destroy", false)) == name_map.end ()) {
// deprecate "destroy"
non_const_decl->add_method (sm_destroy ("_destroy|#destroy"));
} else {
// fallback name is "_create" to avoid conflicts
non_const_decl->add_method (sm_create ("_create"));
// fallback name is "_destroy" to avoid conflicts
non_const_decl->add_method (sm_destroy ("_destroy"));
}
}
if (c->can_destroy ()) {
if (name_map.find (std::make_pair ("destroy", false)) == name_map.end ()) {
// deprecate "destroy"
non_const_decl->add_method (sm_destroy ("_destroy|#destroy"));
} else {
// fallback name is "_destroy" to avoid conflicts
non_const_decl->add_method (sm_destroy ("_destroy"));
}
}
if (c->can_copy ()) {
if (c->can_copy ()) {
if (name_map.find (std::make_pair ("dup", false)) == name_map.end ()) {
non_const_decl->add_method (sm_dup ("dup", &*c));
} else {
// fallback name is "_dup" to avoid conflicts
non_const_decl->add_method (sm_dup ("_dup", &*c));
}
if (name_map.find (std::make_pair ("assign", false)) == name_map.end ()) {
non_const_decl->add_method (sm_assign ("assign", &*c));
} else {
// fallback name is "_assign" to avoid conflicts
non_const_decl->add_method (sm_assign ("_assign", &*c));
}
}
if (name_map.find (std::make_pair ("destroyed", false)) == name_map.end ()) {
// deprecate "destroyed"
non_const_decl->add_method (sm_destroyed ("_destroyed?|#destroyed?"));
if (name_map.find (std::make_pair ("dup", false)) == name_map.end ()) {
non_const_decl->add_method (sm_dup ("dup", &*c));
} else {
// fallback name is "_destroyed" to avoid conflicts
non_const_decl->add_method (sm_destroyed ("_destroyed?"));
// fallback name is "_dup" to avoid conflicts
non_const_decl->add_method (sm_dup ("_dup", &*c));
}
if (name_map.find (std::make_pair ("is_const_object", false)) == name_map.end ()) {
// deprecate "is_const"
non_const_decl->add_method (sm_is_const ("_is_const_object?|#is_const_object?"));
if (name_map.find (std::make_pair ("assign", false)) == name_map.end ()) {
non_const_decl->add_method (sm_assign ("assign", &*c));
} else {
// fallback name is "_is_const" to avoid conflicts
non_const_decl->add_method (sm_is_const ("_is_const_object?"));
// fallback name is "_assign" to avoid conflicts
non_const_decl->add_method (sm_assign ("_assign", &*c));
}
}
if (name_map.find (std::make_pair ("destroyed", false)) == name_map.end ()) {
// deprecate "destroyed"
non_const_decl->add_method (sm_destroyed ("_destroyed?|#destroyed?"));
} else {
// fallback name is "_destroyed" to avoid conflicts
non_const_decl->add_method (sm_destroyed ("_destroyed?"));
}
if (name_map.find (std::make_pair ("is_const_object", false)) == name_map.end ()) {
// deprecate "is_const"
non_const_decl->add_method (sm_is_const ("_is_const_object?|#is_const_object?"));
} else {
// fallback name is "_is_const" to avoid conflicts
non_const_decl->add_method (sm_is_const ("_is_const_object?"));
}
}
// finally merge the new classes into the existing ones
if (! mp_class_collection) {
mp_class_collection = new class_collection ();
}
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_new_classes (); c != gsi::ClassBase::end_new_classes (); ++c) {
gsi::ClassBase *non_const_decl = const_cast<gsi::ClassBase *> (c.operator-> ());
mp_class_collection->push_back (non_const_decl);
}
mp_new_class_collection->clear ();
}
void
@ -536,45 +573,33 @@ bool has_class (const std::string &name)
return class_by_name_no_assert (name) != 0;
}
namespace
static void add_class_to_map (const gsi::ClassBase *c)
{
struct type_info_compare
{
bool operator() (const std::type_info *a, const std::type_info *b) const
{
return a->before (*b);
if (c->declaration () != c) {
// only consider non-extensions
return;
}
};
const std::type_info *ti = c->adapted_type_info ();
if (! ti) {
ti = &c->type ();
}
if (ti && c->is_of_type (*ti) && !s_ti_to_class.insert (std::make_pair (ti, c)).second) {
// Duplicate registration of this class
tl::error << "Duplicate registration of class " << c->name () << " (type " << ti->name () << ")";
tl_assert (false);
}
}
// TODO: thread-safe? Unlikely that multiple threads access this member -
// we do a initial scan and after this no more write access here.
static std::map<const std::type_info *, const ClassBase *, type_info_compare> s_ti_to_class;
const ClassBase *class_by_typeinfo_no_assert (const std::type_info &ti)
{
if (s_ti_to_class.empty ()) {
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes (); ++c) {
if (c->declaration () != c.operator-> ()) {
continue;
}
const std::type_info *ti = c->adapted_type_info ();
if (! ti) {
ti = &c->type ();
}
if (ti && c->is_of_type (*ti) && !s_ti_to_class.insert (std::make_pair (ti, c.operator-> ())).second) {
// Duplicate registration of this class
tl::error << "Duplicate registration of class " << c->name () << " (type " << ti->name () << ")";
tl_assert (false);
}
add_class_to_map (c.operator-> ());
}
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_new_classes (); c != gsi::ClassBase::end_new_classes (); ++c) {
add_class_to_map (c.operator-> ());
}
}
std::map<const std::type_info *, const ClassBase *, type_info_compare>::const_iterator c = s_ti_to_class.find (&ti);

View File

@ -220,6 +220,23 @@ public:
return collection ().end ();
}
/**
* @brief Iterates all freshly registered classes (begin)
* This collection is emptied on "merge_declarations".
*/
static class_iterator begin_new_classes ()
{
return new_collection ().begin ();
}
/**
* @brief Iterates all freshly registered classes (begin)
*/
static class_iterator end_new_classes ()
{
return new_collection ().end ();
}
/**
* @brief Iterates the methods (begin)
*/
@ -557,6 +574,7 @@ public:
protected:
static const class_collection &collection ();
static const class_collection &new_collection ();
const tl::weak_collection<ClassBase> &subclasses () const
{
@ -587,7 +605,7 @@ private:
mutable std::auto_ptr<PerClassClientSpecificData> mp_data[ClientIndex::MaxClientIndex];
static class_collection *mp_class_collection;
static unsigned int m_class_count;
static class_collection *mp_new_class_collection;
// No copying
ClassBase (const ClassBase &other);

View File

@ -1073,15 +1073,10 @@ private:
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;
// just in case this did not happen yet ...
gsi::initialize ();
// Go through all classes (maybe again)
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes (); ++c) {
// install the method table:

View File

@ -2355,7 +2355,7 @@ PythonModule::python_doc (const gsi::MethodBase *method)
}
void
PythonModule::make_classes ()
PythonModule::make_classes (const char *mod_name)
{
PyObject *module = mp_module.get ();
@ -2407,6 +2407,11 @@ PythonModule::make_classes ()
more_classes = false;
for (gsi::ClassBase::class_iterator c = gsi::ClassBase::begin_classes (); c != gsi::ClassBase::end_classes (); ++c) {
if (mod_name && c->module () != mod_name) {
// don't handle classes outside this module
continue;
}
if (m_rev_cls_map.find (&*c) != m_rev_cls_map.end ()) {
// don't handle classes twice
continue;

View File

@ -124,7 +124,7 @@ public:
/**
* @brief Creates the classes after init has been called
*/
void make_classes ();
void make_classes (const char *mod_name = 0);
/**
* @brief Gets the GSI class for a Python class

View File

@ -10,9 +10,9 @@ SOURCES = \
HEADERS += \
INCLUDEPATH += $$PYTHONINCLUDE $$TL_INC $$GSI_INC $$DB_INC $$GSI_INC $$PYA_INC
DEPENDPATH += $$PYTHONINCLUDE $$TL_INC $$GSI_INC $$DB_INC $$GSI_INC $$PYA_INC
LIBS += $$PYTHONLIBFILE -L$$LIBDIR -lklayout_tl -lklayout_gsi -lklayout_db -lklayout_gsi -lklayout_pya
INCLUDEPATH += $$PYTHONINCLUDE $$TL_INC $$GSI_INC $$PYA_INC
DEPENDPATH += $$PYTHONINCLUDE $$TL_INC $$GSI_INC $$PYA_INC
LIBS += $$PYTHONLIBFILE -L$$LIBDIR -lklayout_tl -lklayout_gsi -lklayout_db -lklayout_pya
# Python is somewhat sloppy and relies on the compiler initializing fields
# of strucs to 0:

View File

@ -28,11 +28,13 @@
static PyObject *module_init ()
{
gsi::initialize ();
// required for the tiling processor for example
gsi::initialize_expressions ();
static pya::PythonModule module;
module.init ("klayout.db", "KLayout core module (db)");
module.make_classes ();
module.make_classes ("db");
return module.module ();
}

21
src/pymod/lay/lay.pro Normal file
View File

@ -0,0 +1,21 @@
LIBDIR = $$OUT_PWD/../..
DESTDIR = $$LIBDIR/pymod
TARGET = lay
include($$PWD/../../lib.pri)
SOURCES = \
layMain.cc \
HEADERS += \
INCLUDEPATH += $$PYTHONINCLUDE $$TL_INC $$GSI_INC $$PYA_INC
DEPENDPATH += $$PYTHONINCLUDE $$TL_INC $$GSI_INC $$PYA_INC
LIBS += $$PYTHONLIBFILE -L$$LIBDIR -lklayout_tl -lklayout_db -lklayout_gsi -lklayout_pya -lklayout_lay -lklayout_laybasic
# Python is somewhat sloppy and relies on the compiler initializing fields
# of strucs to 0:
QMAKE_CXXFLAGS_WARN_ON += \
-Wno-missing-field-initializers

56
src/pymod/lay/layMain.cc Normal file
View File

@ -0,0 +1,56 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2018 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 <Python.h>
#include "pya.h"
#include "gsi.h"
#include "gsiExpression.h"
static PyObject *module_init ()
{
gsi::initialize ();
// required for the tiling processor for example
gsi::initialize_expressions ();
static pya::PythonModule module;
module.init ("klayout.lay", "KLayout core module (lay)");
module.make_classes ("lay");
return module.module ();
}
#if PY_MAJOR_VERSION < 3
PyMODINIT_FUNC
DEF_INSIDE_PUBLIC
initlay ()
{
module_init ();
}
#else
PyMODINIT_FUNC
DEF_INSIDE_PUBLIC
PyInit_lay ()
{
return module_init();
}
#endif

View File

@ -1,4 +1,7 @@
TEMPLATE = subdirs
SUBDIRS = db
SUBDIRS = \
db \
tl \
lay \

21
src/pymod/tl/tl.pro Normal file
View File

@ -0,0 +1,21 @@
LIBDIR = $$OUT_PWD/../..
DESTDIR = $$LIBDIR/pymod
TARGET = tl
include($$PWD/../../lib.pri)
SOURCES = \
tlMain.cc \
HEADERS += \
INCLUDEPATH += $$PYTHONINCLUDE $$TL_INC $$GSI_INC $$PYA_INC
DEPENDPATH += $$PYTHONINCLUDE $$TL_INC $$GSI_INC $$PYA_INC
LIBS += $$PYTHONLIBFILE -L$$LIBDIR -lklayout_tl -lklayout_gsi -lklayout_pya
# Python is somewhat sloppy and relies on the compiler initializing fields
# of strucs to 0:
QMAKE_CXXFLAGS_WARN_ON += \
-Wno-missing-field-initializers

56
src/pymod/tl/tlMain.cc Normal file
View File

@ -0,0 +1,56 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2018 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 <Python.h>
#include "pya.h"
#include "gsi.h"
#include "gsiExpression.h"
static PyObject *module_init ()
{
gsi::initialize ();
// required for the tiling processor for example
gsi::initialize_expressions ();
static pya::PythonModule module;
module.init ("klayout.tl", "KLayout core module (tl)");
module.make_classes ("tl");
return module.module ();
}
#if PY_MAJOR_VERSION < 3
PyMODINIT_FUNC
DEF_INSIDE_PUBLIC
inittl ()
{
module_init ();
}
#else
PyMODINIT_FUNC
DEF_INSIDE_PUBLIC
PyInit_tl ()
{
return module_init();
}
#endif