Merge pull request #1820 from KLayout/wip

WIP branch
This commit is contained in:
Matthias Köfferlein 2024-08-10 13:33:12 +02:00 committed by GitHub
commit 9ef35d120e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 586 additions and 40 deletions

View File

@ -854,6 +854,11 @@ static db::Cell *cell_from_index (db::Layout *ly, db::cell_index_type ci)
return &ly->cell (ci); return &ly->cell (ci);
} }
static const db::Cell *cell_from_index_const (const db::Layout *layout, db::cell_index_type ci)
{
return cell_from_index (const_cast <db::Layout *> (layout), ci);
}
static db::Cell *cell_from_name (db::Layout *ly, const std::string &name) static db::Cell *cell_from_name (db::Layout *ly, const std::string &name)
{ {
std::pair<bool, db::cell_index_type> cn = ly->cell_by_name (name.c_str ()); std::pair<bool, db::cell_index_type> cn = ly->cell_by_name (name.c_str ());
@ -864,6 +869,11 @@ static db::Cell *cell_from_name (db::Layout *ly, const std::string &name)
} }
} }
static const db::Cell *cell_from_name_const (const db::Layout *layout, const std::string &name)
{
return cell_from_name (const_cast <db::Layout *> (layout), name);
}
static std::vector<db::Cell *> cells_from_name (db::Layout *layout, const std::string &filter) static std::vector<db::Cell *> cells_from_name (db::Layout *layout, const std::string &filter)
{ {
tl::GlobPattern gp (filter); tl::GlobPattern gp (filter);
@ -880,6 +890,12 @@ static std::vector<db::Cell *> cells_from_name (db::Layout *layout, const std::s
return result; return result;
} }
static std::vector<const db::Cell *> cells_from_name_const (const db::Layout *layout, const std::string &filter)
{
std::vector<db::Cell *> tcs = cells_from_name (const_cast <db::Layout *> (layout), filter);
return std::vector<const db::Cell *> (tcs.begin (), tcs.end ());
}
static std::vector<db::Cell *> top_cells (db::Layout *layout) static std::vector<db::Cell *> top_cells (db::Layout *layout)
{ {
std::vector<db::Cell *> tc; std::vector<db::Cell *> tc;
@ -891,6 +907,12 @@ static std::vector<db::Cell *> top_cells (db::Layout *layout)
return tc; return tc;
} }
static std::vector<const db::Cell *> top_cells_const (const db::Layout *layout)
{
std::vector<db::Cell *> tcs = top_cells (const_cast <db::Layout *> (layout));
return std::vector<const db::Cell *> (tcs.begin (), tcs.end ());
}
static db::Cell *top_cell (db::Layout *layout) static db::Cell *top_cell (db::Layout *layout)
{ {
db::Cell *tc = 0; db::Cell *tc = 0;
@ -906,6 +928,11 @@ static db::Cell *top_cell (db::Layout *layout)
return tc; return tc;
} }
static const db::Cell *top_cell_const (const db::Layout *layout)
{
return top_cell (const_cast <db::Layout *> (layout));
}
static db::Cell *create_cell (db::Layout *layout, const std::string &name) static db::Cell *create_cell (db::Layout *layout, const std::string &name)
{ {
return &layout->cell (layout->add_cell (name.c_str ())); return &layout->cell (layout->add_cell (name.c_str ()));
@ -1328,7 +1355,16 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"\n" "\n"
"This method has been introduced in version 0.23." "This method has been introduced in version 0.23."
) + ) +
gsi::method_ext ("top_cells", &top_cells, gsi::method_ext ("top_cell", &top_cell_const,
"@brief Returns the top cell object (const version)\n"
"@return The \\Cell object of the top cell\n"
"If the layout has a single top cell, this method returns the top cell's \\Cell object.\n"
"If the layout does not have a top cell, this method returns \"nil\". If the layout has multiple\n"
"top cells, this method raises an error.\n"
"\n"
"This variant has been introduced in version 0.29.6."
) +
gsi::method_ext ("top_cells", &top_cells,
"@brief Returns the top cell objects\n" "@brief Returns the top cell objects\n"
"@return The \\Cell objects of the top cells\n" "@return The \\Cell objects of the top cells\n"
"This method returns and array of \\Cell objects representing the top cells of the layout.\n" "This method returns and array of \\Cell objects representing the top cells of the layout.\n"
@ -1336,6 +1372,14 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"\n" "\n"
"This method has been introduced in version 0.23." "This method has been introduced in version 0.23."
) + ) +
gsi::method_ext ("top_cells", &top_cells_const,
"@brief Returns the top cell objects (const version)\n"
"@return The \\Cell objects of the top cells\n"
"This method returns and array of \\Cell objects representing the top cells of the layout.\n"
"This array can be empty, if the layout does not have a top cell (i.e. no cell at all).\n"
"\n"
"This variant has been introduced in version 0.29.6."
) +
gsi::method ("has_cell?", &db::Layout::has_cell, gsi::arg ("name"), gsi::method ("has_cell?", &db::Layout::has_cell, gsi::arg ("name"),
"@brief Returns true if a cell with a given name exists\n" "@brief Returns true if a cell with a given name exists\n"
"Returns true, if the layout has a cell with the given name" "Returns true, if the layout has a cell with the given name"
@ -1780,6 +1824,16 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"\n" "\n"
"This method has been introduced in version 0.27.3.\n" "This method has been introduced in version 0.27.3.\n"
) + ) +
gsi::method_ext ("cells", &cells_from_name_const, gsi::arg ("name_filter"),
"@brief Gets the cell objects for a given name filter (const version)\n"
"\n"
"@param name_filter The cell name filter (glob pattern)\n"
"@return A list of \\Cell object of the cells matching the pattern\n"
"\n"
"This method has been introduced in version 0.27.3.\n"
"\n"
"This variant has been introduced in version 0.29.6."
) +
gsi::method_ext ("cell", &cell_from_name, gsi::arg ("name"), gsi::method_ext ("cell", &cell_from_name, gsi::arg ("name"),
"@brief Gets a cell object from the cell name\n" "@brief Gets a cell object from the cell name\n"
"\n" "\n"
@ -1789,6 +1843,17 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"If name is not a valid cell name, this method will return \"nil\".\n" "If name is not a valid cell name, this method will return \"nil\".\n"
"This method has been introduced in version 0.23 and replaces \\cell_by_name.\n" "This method has been introduced in version 0.23 and replaces \\cell_by_name.\n"
) + ) +
gsi::method_ext ("cell", &cell_from_name_const, gsi::arg ("name"),
"@brief Gets a cell object from the cell name (const version)\n"
"\n"
"@param name The cell name\n"
"@return A reference to the cell (a \\Cell object)\n"
"\n"
"If name is not a valid cell name, this method will return \"nil\".\n"
"This method has been introduced in version 0.23 and replaces \\cell_by_name.\n"
"\n"
"This variant has been introduced in version 0.29.6."
) +
gsi::method_ext ("cell", &cell_from_index, gsi::arg ("i"), gsi::method_ext ("cell", &cell_from_index, gsi::arg ("i"),
"@brief Gets a cell object from the cell index\n" "@brief Gets a cell object from the cell index\n"
"\n" "\n"
@ -1798,6 +1863,17 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"If the cell index is not a valid cell index, this method will raise an error. " "If the cell index is not a valid cell index, this method will raise an error. "
"Use \\is_valid_cell_index? to test whether a given cell index is valid.\n" "Use \\is_valid_cell_index? to test whether a given cell index is valid.\n"
) + ) +
gsi::method_ext ("cell", &cell_from_index_const, gsi::arg ("i"),
"@brief Gets a cell object from the cell index (const version)\n"
"\n"
"@param i The cell index\n"
"@return A reference to the cell (a \\Cell object)\n"
"\n"
"If the cell index is not a valid cell index, this method will raise an error. "
"Use \\is_valid_cell_index? to test whether a given cell index is valid.\n"
"\n"
"This variant has been introduced in version 0.29.6."
) +
gsi::iterator ("each_cell", (db::Layout::iterator (db::Layout::*) ()) &db::Layout::begin, (db::Layout::iterator (db::Layout::*) ()) &db::Layout::end, gsi::iterator ("each_cell", (db::Layout::iterator (db::Layout::*) ()) &db::Layout::begin, (db::Layout::iterator (db::Layout::*) ()) &db::Layout::end,
"@brief Iterates the unsorted cell list\n" "@brief Iterates the unsorted cell list\n"
) + ) +

View File

@ -339,7 +339,7 @@ Class<db::ParameterState> decl_PCellParameterState ("db", "PCellParameterState",
gsi::method("icon=", &db::ParameterState::set_icon, gsi::arg ("i"), gsi::method("icon=", &db::ParameterState::set_icon, gsi::arg ("i"),
"@brief Sets the icon for the parameter\n" "@brief Sets the icon for the parameter\n"
) + ) +
gsi::method("tooltip", &db::ParameterState::tooltip, gsi::method("icon", &db::ParameterState::icon,
"@brief Gets the icon for the parameter\n" "@brief Gets the icon for the parameter\n"
), ),
"@brief Provides access to the attributes of a single parameter within \\PCellParameterStates.\n" "@brief Provides access to the attributes of a single parameter within \\PCellParameterStates.\n"

View File

@ -2308,7 +2308,7 @@ CODE
def netter def netter
self._context("netter") do self._context("netter") do
DRC::DRCNetter::new DRC::DRCNetter::new(self)
end end
end end

View File

@ -5186,6 +5186,7 @@ CODE
# @brief Pulls net shapes from selected or all nets, optionally annotating nets with properties # @brief Pulls net shapes from selected or all nets, optionally annotating nets with properties
# @synopsis layer.nets # @synopsis layer.nets
# @synopsis layer.nets(net_filter) # @synopsis layer.nets(net_filter)
# @synopsis layer.nets(net_object)
# @synopsis layer.nets(circuit_filter, net_filter) # @synopsis layer.nets(circuit_filter, net_filter)
# @synopsis layer.nets(netter, ...) # @synopsis layer.nets(netter, ...)
# @synopsis layer.nets(prop(key), ...) # @synopsis layer.nets(prop(key), ...)
@ -5202,7 +5203,7 @@ CODE
# complete - subnets from subcircuits are not selected. The net name is taken from # complete - subnets from subcircuits are not selected. The net name is taken from
# the net's home circuit (to topmost location where all net connections are formed). # the net's home circuit (to topmost location where all net connections are formed).
# You can specify a circuit filter to select nets from certain circuits only or # You can specify a circuit filter to select nets from certain circuits only or
# give a RBA::Circuit object explicitly. # give a RBA::Circuit object explicitly. You can also specify RBA::Net objects directly.
# #
# @code # @code
# connect(metal1, via1) # connect(metal1, via1)
@ -5230,6 +5231,8 @@ CODE
@engine._context("nets") do @engine._context("nets") do
nets = nil
# parse arguments # parse arguments
filters = nil filters = nil
circuits = nil circuits = nil
@ -5241,6 +5244,9 @@ CODE
filters << a filters << a
elsif a.is_a?(1.class) elsif a.is_a?(1.class)
prop_id = a prop_id = a
elsif a.is_a?(RBA::Net)
nets ||= []
nets << a
elsif a.is_a?(RBA::Circuit) elsif a.is_a?(RBA::Circuit)
circuits ||= [] circuits ||= []
circuits << a circuits << a
@ -5272,13 +5278,14 @@ CODE
circuits ||= [] circuits ||= []
circuits += netlist.circuits_by_name(circuit_filter) circuits += netlist.circuits_by_name(circuit_filter)
end end
nets = nil
if !circuits if !circuits
if filters if filters
nets = filters.collect { |f| netlist.nets_by_name(f) }.flatten nets ||= []
nets += filters.collect { |f| netlist.nets_by_name(f) }.flatten
end end
else else
nets = circuits.collect do |circuit| nets ||= []
nets += circuits.collect do |circuit|
(filters || ["*"]).collect { |f| circuit.nets_by_name(f) }.flatten (filters || ["*"]).collect { |f| circuit.nets_by_name(f) }.flatten
end.flatten end.flatten
end end

View File

@ -1568,6 +1568,16 @@ TEST(70d_props)
run_test (_this, "70", true); run_test (_this, "70", true);
} }
TEST(71_netter)
{
run_test (_this, "71", false);
}
TEST(71d_netter)
{
run_test (_this, "71", true);
}
TEST(80_deep_with_mag_width) TEST(80_deep_with_mag_width)
{ {
run_test (_this, "80", true); run_test (_this, "80", true);

View File

@ -353,6 +353,48 @@ sm_is_const (const char *name)
return sm; return sm;
} }
static SpecialMethod *
sm_to_const (const char *name, const gsi::ClassBase *cls)
{
SpecialMethod *sm = new SpecialMethod (name,
tl::to_string (tr ("@hide")), // provided for test purposes mainly
true, // const
false, // non-static
MethodBase::ToConst);
gsi::ArgType ret;
ret.set_is_cptr (true);
ret.set_type (gsi::T_object);
ret.set_pass_obj (false);
ret.set_cls (cls);
sm->set_return (ret);
return sm;
}
static SpecialMethod *
sm_const_cast (const char *name, const gsi::ClassBase *cls)
{
SpecialMethod *sm = new SpecialMethod (name,
tl::to_string (tr ("@brief Returns a non-const reference to self.\n"
"Basically, this method allows turning a const object reference to a non-const one. "
"This method is provided as last resort to remove the constness from an object. Usually there is a good reason for a const object reference, so using this method may have undesired side effects.\n"
"\n"
"This method has been introduced in version 0.29.6.")),
true, // const
false, // non-static
MethodBase::ConstCast);
gsi::ArgType ret;
ret.set_is_ptr (true);
ret.set_type (gsi::T_object);
ret.set_pass_obj (false);
ret.set_cls (cls);
sm->set_return (ret);
return sm;
}
static SpecialMethod * static SpecialMethod *
sm_destroyed (const char *name) sm_destroyed (const char *name)
{ {
@ -598,6 +640,9 @@ ClassBase::merge_declarations ()
non_const_decl->add_method (sm_is_const ("_is_const_object?")); non_const_decl->add_method (sm_is_const ("_is_const_object?"));
} }
non_const_decl->add_method (sm_to_const ("_to_const_object", &*c));
non_const_decl->add_method (sm_const_cast ("_const_cast", &*c));
} }
// finally merge the new classes into the existing ones // finally merge the new classes into the existing ones

View File

@ -305,13 +305,17 @@ initialize_expressions ()
// install the method table: // install the method table:
ExpressionMethodTable::initialize_class (*c); ExpressionMethodTable::initialize_class (*c);
// register a function that creates a class object (use a function to avoid issues with // Note: skip non-top-level classes
// late destruction of global variables which the class object is already gone) if ((*c)->parent () == 0) {
const tl::VariantUserClassBase *cc = (*c)->var_cls_cls ();
if (cc) {
tl::Eval::define_global_function ((*c)->name (), new EvalClassFunction (cc));
}
// 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));
}
}
} }
} }
@ -630,6 +634,14 @@ special_method_impl (gsi::MethodBase::special_method_type smt, tl::Variant &self
// nothing to do here for GSI objects // nothing to do here for GSI objects
} else if (smt == gsi::MethodBase::IsConst) { } else if (smt == gsi::MethodBase::IsConst) {
return tl::Variant (self.user_is_const ()); return tl::Variant (self.user_is_const ());
} else if (smt == gsi::MethodBase::ToConst) {
tl::Variant res (self);
res.user_change_constness (true);
return res;
} else if (smt == gsi::MethodBase::ConstCast) {
tl::Variant res (self);
res.user_change_constness (false);
return res;
} else if (smt == gsi::MethodBase::Destroyed) { } else if (smt == gsi::MethodBase::Destroyed) {
if (self.type_code () == tl::Variant::t_user) { if (self.type_code () == tl::Variant::t_user) {

View File

@ -83,7 +83,9 @@ public:
Destroy, Destroy,
Create, Create,
IsConst, IsConst,
Destroyed, ConstCast,
ToConst,
Destroyed,
Assign, Assign,
Dup Dup
}; };

View File

@ -829,3 +829,37 @@ TEST(15)
v = e.parse("var bb = BB.new; bb.d4(1, 'a', 2.0, BB.E.E3B, 42)").execute(); v = e.parse("var bb = BB.new; bb.d4(1, 'a', 2.0, BB.E.E3B, 42)").execute();
EXPECT_EQ (v.to_string (), "1,a,2,101,42"); EXPECT_EQ (v.to_string (), "1,a,2,101,42");
} }
// constness
TEST(16)
{
tl::Eval e;
tl::Variant v;
v = e.parse ("var b=B.new(); b._is_const_object").execute ();
EXPECT_EQ (v.to_string (), std::string ("false"));
try {
v = e.parse ("var b=B.new(); var bc=b._to_const_object; bc.set_str('abc')").execute ();
EXPECT_EQ (1, 0);
} catch (tl::Exception &ex) {
EXPECT_EQ (ex.msg (), "Cannot call non-const method set_str, class B on a const reference at position 44 (...set_str('abc'))");
}
v = e.parse ("var e=E.new(); var ec=e.dup; [e._is_const_object, ec._to_const_object._is_const_object]").execute ();
EXPECT_EQ (v.to_string (), std::string ("false,true"));
v = e.parse ("var e=E.new(); var ec=e._to_const_object; e.x=17; [e.x, ec.x]").execute ();
EXPECT_EQ (v.to_string (), std::string ("17,17"));
v = e.parse ("var e=E.new(); var ec=e._to_const_object; ec._is_const_object").execute ();
EXPECT_EQ (v.to_string (), std::string ("true"));
v = e.parse ("var e=E.new(); var ec=e._to_const_object; ec=ec._const_cast; ec._is_const_object").execute ();
EXPECT_EQ (v.to_string (), std::string ("false"));
v = e.parse ("var e=E.new(); var ec=e._to_const_object; ec=ec._const_cast; ec.x=42; e.x").execute ();
EXPECT_EQ (v.to_string (), std::string ("42"));
v = e.parse ("var e=E.new(); var ec=e._to_const_object; e.x=17; ec.x").execute ();
EXPECT_EQ (v.to_string (), std::string ("17"));
try {
v = e.parse ("var e=E.new(); var ec=e._to_const_object; e.x=17; e._destroy; ec.x").execute ();
EXPECT_EQ (1, 0);
} catch (tl::Exception &ex) {
EXPECT_EQ (ex.msg (), "Object has been destroyed already at position 64 (...x)");
}
}

View File

@ -1331,6 +1331,18 @@ MainWindow::update_dock_widget_state ()
void void
MainWindow::exit () MainWindow::exit ()
{ {
if (m_busy) {
if (QMessageBox::warning (this,
QObject::tr ("Application Busy - Close Anyway?"),
QObject::tr ("The application is busy.\nYou can close the application now, but this will terminate any running operations.\nDo you want to close anyway?\n\n"
"Press 'Yes' to end the application now."),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::Yes) != QMessageBox::Yes)
{
return;
}
}
m_exited = true; m_exited = true;
// If there is a operation ongoing, request a break and delay execution of the exit operation. // If there is a operation ongoing, request a break and delay execution of the exit operation.
@ -1394,20 +1406,6 @@ MainWindow::dirty_files (std::string &dirty_files)
bool bool
MainWindow::can_close () MainWindow::can_close ()
{ {
if (m_busy) {
bool can_close = false;
can_close = (QMessageBox::warning (this,
QObject::tr ("Application Busy"),
QObject::tr ("The application is busy.\nYou can close the application now, but any unsaved data will be lost.\n\nPress 'Yes' to end the application now."),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::Yes) == QMessageBox::Yes);
return can_close;
}
for (tl::Registrar<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::end (); ++cls) { for (tl::Registrar<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::end (); ++cls) {
lay::PluginDeclaration *pd = const_cast<lay::PluginDeclaration *> (&*cls); lay::PluginDeclaration *pd = const_cast<lay::PluginDeclaration *> (&*cls);
if (! pd->can_exit (dispatcher ())) { if (! pd->can_exit (dispatcher ())) {

View File

@ -379,7 +379,7 @@ PropertiesDialog::update_controls ()
mp_ui->apply_to_all_cbx->setChecked (m_object_indexes.size () > 1); mp_ui->apply_to_all_cbx->setChecked (m_object_indexes.size () > 1);
if (m_index < 0) { if (m_index < 0 || m_index >= int (mp_properties_pages.size ())) {
mp_stack->setCurrentWidget (mp_none); mp_stack->setCurrentWidget (mp_none);
@ -552,6 +552,10 @@ PropertiesDialog::apply ()
{ {
BEGIN_PROTECTED BEGIN_PROTECTED
if (m_index < 0 || m_index >= int (mp_properties_pages.size ())) {
return;
}
db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id); db::Transaction t (mp_manager, tl::to_string (QObject::tr ("Apply changes")), m_transaction_id);
try { try {
@ -633,6 +637,12 @@ PropertiesDialog::reject ()
QDialog::reject (); QDialog::reject ();
} }
void
PropertiesDialog::accept ()
{
// stop handling "Enter" key.
}
} }
#endif #endif

View File

@ -116,6 +116,7 @@ public slots:
protected: protected:
void reject (); void reject ();
void accept ();
private: private:
Ui::PropertiesDialog *mp_ui; Ui::PropertiesDialog *mp_ui;

View File

@ -721,6 +721,50 @@ object_is_const (PyObject *self, PyObject *args)
return c2python (PYAObjectBase::from_pyobject (self)->const_ref ()); return c2python (PYAObjectBase::from_pyobject (self)->const_ref ());
} }
/**
* @brief Implements to_const and const_cast
*/
static PyObject *
object_change_const (PyObject *self, PyObject *args, bool to_const)
{
if (PYAObjectBase::from_pyobject (self)->const_ref () == to_const) {
return self;
}
const gsi::ClassBase *cls_decl_self = PythonModule::cls_for_type (Py_TYPE (self));
tl_assert (cls_decl_self != 0);
if (! PyArg_ParseTuple (args, "")) {
return NULL;
}
PyObject *new_object = Py_TYPE (self)->tp_alloc (Py_TYPE (self), 0);
PythonRef obj (new_object);
PYAObjectBase *new_pya_base = PYAObjectBase::from_pyobject_unsafe (new_object);
new (new_pya_base) PYAObjectBase (cls_decl_self, new_object);
new_pya_base->set (PYAObjectBase::from_pyobject (self)->obj (), false, to_const, false);
return obj.release ();
}
/**
* @brief Implements to_const
*/
static PyObject *
object_to_const (PyObject *self, PyObject *args)
{
return object_change_const (self, args, true);
}
/**
* @brief Implements const_cast
*/
static PyObject *
object_const_cast (PyObject *self, PyObject *args)
{
return object_change_const (self, args, false);
}
static PyObject * static PyObject *
special_method_impl (gsi::MethodBase::special_method_type smt, PyObject *self, PyObject *args) special_method_impl (gsi::MethodBase::special_method_type smt, PyObject *self, PyObject *args)
{ {
@ -734,6 +778,10 @@ special_method_impl (gsi::MethodBase::special_method_type smt, PyObject *self, P
return object_create (self, args); return object_create (self, args);
} else if (smt == gsi::MethodBase::IsConst) { } else if (smt == gsi::MethodBase::IsConst) {
return object_is_const (self, args); return object_is_const (self, args);
} else if (smt == gsi::MethodBase::ToConst) {
return object_to_const (self, args);
} else if (smt == gsi::MethodBase::ConstCast) {
return object_const_cast (self, args);
} else if (smt == gsi::MethodBase::Destroyed) { } else if (smt == gsi::MethodBase::Destroyed) {
return object_destroyed (self, args); return object_destroyed (self, args);
} else if (smt == gsi::MethodBase::Assign) { } else if (smt == gsi::MethodBase::Assign) {

View File

@ -913,6 +913,16 @@ handle_exception (const std::string &where)
handle_exception ((where)); \ handle_exception ((where)); \
} }
static void free_proxy (void *p)
{
delete ((Proxy *) p);
}
static void mark_proxy (void *p)
{
((Proxy *) p)->mark ();
}
static VALUE static VALUE
destroy (VALUE self) destroy (VALUE self)
{ {
@ -971,6 +981,37 @@ is_const (VALUE self)
return c2ruby<bool> (p->const_ref ()); return c2ruby<bool> (p->const_ref ());
} }
static VALUE
to_const (VALUE self)
{
Proxy *p = 0;
Data_Get_Struct (self, Proxy, p);
if (! p->const_ref ()) {
// promote to const object
// NOTE: there is only ONE instance we're going to change this instance
// to const here. This has a global effect and this is the reason why this
// method is not public. It is provided for testing purposes mainly.
p->set_const_ref (true);
}
return self;
}
static VALUE
const_cast_ (VALUE self)
{
Proxy *p = 0;
Data_Get_Struct (self, Proxy, p);
if (p->const_ref ()) {
// promote to non-const object
// NOTE: this is a global change of constness and will affect all references
// that exist for this object.
p->set_const_ref (false);
}
return self;
}
static VALUE static VALUE
assign (VALUE self, VALUE src) assign (VALUE self, VALUE src)
{ {
@ -1024,6 +1065,12 @@ special_method_impl (const gsi::MethodBase *meth, int argc, VALUE *argv, VALUE s
} else if (smt == gsi::MethodBase::IsConst) { } else if (smt == gsi::MethodBase::IsConst) {
tl_assert (!ctor); tl_assert (!ctor);
return is_const (self); return is_const (self);
} else if (smt == gsi::MethodBase::ToConst) {
tl_assert (!ctor);
return to_const (self);
} else if (smt == gsi::MethodBase::ConstCast) {
tl_assert (!ctor);
return const_cast_ (self);
} else if (smt == gsi::MethodBase::Destroyed) { } else if (smt == gsi::MethodBase::Destroyed) {
tl_assert (!ctor); tl_assert (!ctor);
return destroyed (self); return destroyed (self);
@ -1043,16 +1090,6 @@ special_method_impl (const gsi::MethodBase *meth, int argc, VALUE *argv, VALUE s
} }
} }
static void free_proxy (void *p)
{
delete ((Proxy *) p);
}
static void mark_proxy (void *p)
{
((Proxy *) p)->mark ();
}
static VALUE alloc_proxy (VALUE klass) static VALUE alloc_proxy (VALUE klass)
{ {
tl_assert (TYPE (klass) == T_CLASS); tl_assert (TYPE (klass) == T_CLASS);

View File

@ -3192,6 +3192,17 @@ Eval::set_var (const std::string &name, const tl::Variant &var)
m_local_vars.insert (std::make_pair (name, tl::Variant ())).first->second = var; m_local_vars.insert (std::make_pair (name, tl::Variant ())).first->second = var;
} }
tl::Variant *
Eval::var (const std::string &name)
{
auto f = m_local_vars.find (name);
if (f != m_local_vars.end ()) {
return &f->second;
} else {
return 0;
}
}
void void
Eval::define_function (const std::string &name, EvalFunction *function) Eval::define_function (const std::string &name, EvalFunction *function)
{ {
@ -3202,6 +3213,17 @@ Eval::define_function (const std::string &name, EvalFunction *function)
f = function; f = function;
} }
EvalFunction *
Eval::function (const std::string &name)
{
auto f = m_local_functions.find (name);
if (f != m_local_functions.end ()) {
return f->second;
} else {
return 0;
}
}
void void
Eval::eval_top (ExpressionParserContext &ex, std::unique_ptr<ExpressionNode> &n) Eval::eval_top (ExpressionParserContext &ex, std::unique_ptr<ExpressionNode> &n)
{ {

View File

@ -448,6 +448,12 @@ public:
*/ */
void define_function (const std::string &name, EvalFunction *function); void define_function (const std::string &name, EvalFunction *function);
/**
* @brief Gets the function for the given name
* Returns 0 if there is no such function.
*/
EvalFunction *function (const std::string &name);
/** /**
* @brief Define a global variable for use within an expression * @brief Define a global variable for use within an expression
*/ */
@ -461,6 +467,12 @@ public:
*/ */
void set_var (const std::string &name, const tl::Variant &var); void set_var (const std::string &name, const tl::Variant &var);
/**
* @brief Gets the function for the given name
* Returns 0 if there is no such function.
*/
tl::Variant *var (const std::string &name);
/** /**
* @brief Parse an expression from the extractor * @brief Parse an expression from the extractor
* *
@ -543,6 +555,30 @@ public:
return m_match_substrings; return m_match_substrings;
} }
/**
* @brief Gets the global context
*/
static tl::Eval &global_context ()
{
return m_global;
}
/**
* @brief Gets the global context for this context
*/
tl::Eval *global ()
{
return mp_global;
}
/**
* @brief Gets the parent context for this context
*/
tl::Eval *parent ()
{
return mp_parent;
}
private: private:
friend class Expression; friend class Expression;

View File

@ -2738,6 +2738,17 @@ void *Variant::user_unshare () const
return const_cast<void *> (to_user ()); return const_cast<void *> (to_user ());
} }
void Variant::user_change_constness (bool constness)
{
tl_assert (is_user ());
if (m_type == t_user) {
m_var.mp_user.cls = m_var.mp_user.cls->change_constness (constness);
} else if (m_type == t_user_ref) {
m_var.mp_user_ref.cls = m_var.mp_user_ref.cls->change_constness (constness);
}
}
void Variant::user_assign (const tl::Variant &other) void Variant::user_assign (const tl::Variant &other)
{ {
tl_assert (is_user ()); tl_assert (is_user ());

View File

@ -89,6 +89,7 @@ public:
virtual const gsi::ClassBase *gsi_cls () const = 0; virtual const gsi::ClassBase *gsi_cls () const = 0;
virtual const tl::EvalClass *eval_cls () const = 0; virtual const tl::EvalClass *eval_cls () const = 0;
virtual void *deref_proxy (tl::Object *proxy) const = 0; virtual void *deref_proxy (tl::Object *proxy) const = 0;
virtual const tl::VariantUserClassBase *change_constness (bool constness) const = 0;
const void *deref_proxy_const (const tl::Object *proxy) const const void *deref_proxy_const (const tl::Object *proxy) const
{ {
@ -126,6 +127,11 @@ public:
return VariantUserClassBase::instance (typeid (T), is_const); return VariantUserClassBase::instance (typeid (T), is_const);
} }
const tl::VariantUserClassBase *change_constness (bool constness) const
{
return instance (constness);
}
private: private:
static const tl::VariantUserClassBase *ms_instances[4]; static const tl::VariantUserClassBase *ms_instances[4];
@ -1001,6 +1007,11 @@ public:
*/ */
void *user_unshare () const; void *user_unshare () const;
/**
* @brief Changes the constness of the user object
*/
void user_change_constness (bool constness);
/** /**
* @brief Assigns the object stored in other to self * @brief Assigns the object stored in other to self
* *

View File

@ -465,6 +465,7 @@ public:
virtual bool is_ref () const { return false; } virtual bool is_ref () const { return false; }
virtual void *deref_proxy (tl::Object *) const { return 0; } virtual void *deref_proxy (tl::Object *) const { return 0; }
virtual const gsi::ClassBase*gsi_cls() const { return 0; } virtual const gsi::ClassBase*gsi_cls() const { return 0; }
virtual const tl::VariantUserClassBase *change_constness (bool) const { return this; }
static BoxClassClass instance; static BoxClassClass instance;
}; };
@ -533,6 +534,7 @@ public:
virtual bool is_ref () const { return false; } virtual bool is_ref () const { return false; }
virtual void *deref_proxy (tl::Object *) const { return 0; } virtual void *deref_proxy (tl::Object *) const { return 0; }
virtual const gsi::ClassBase*gsi_cls() const { return 0; } virtual const gsi::ClassBase*gsi_cls() const { return 0; }
virtual const tl::VariantUserClassBase *change_constness (bool) const { return this; }
static EdgeClassClass instance; static EdgeClassClass instance;
}; };
@ -1209,3 +1211,4 @@ TEST(20)
v = e.parse ("i2.dtrans.disp.y").execute (); v = e.parse ("i2.dtrans.disp.y").execute ();
EXPECT_EQ (v.to_string (), std::string ("0.3")); EXPECT_EQ (v.to_string (), std::string ("0.3"));
} }

View File

@ -68,11 +68,14 @@ l1.nets(self._netter).output(101, 0)
l1.nets(prop(1)).output(102, 0) l1.nets(prop(1)).output(102, 0)
l1.nets(prop(nil)).output(103, 0) l1.nets(prop(nil)).output(103, 0)
l1.nets("X").output(110, 0) x1 = l1.nets("X")
x1.output(110, 0)
l1.nets("TOP", "X").output(111, 0) l1.nets("TOP", "X").output(111, 0)
l1.nets("TOP", "NOTEXIST").output(112, 0) l1.nets("TOP", "NOTEXIST").output(112, 0)
l1.nets("NOTEXIST", "NOTEXIST").output(113, 0) l1.nets("NOTEXIST", "NOTEXIST").output(113, 0)
x2 = l1.nets(*netlist.nets_by_name("X"))
(x1 ^ x2).output(114, 0)
# checks with property constraints # checks with property constraints

41
testdata/drc/drcSimpleTests_71.drc vendored Normal file
View File

@ -0,0 +1,41 @@
# Moved implementation
source($drc_test_source)
target($drc_test_target)
if $drc_test_deep
deep
end
l1 = input(1, 0)
l2 = input(2, 0)
l3 = input(3, 0)
# dump to output
l1.output(1, 0)
l2.output(2, 0)
l3.output(3, 0)
ctx1 = netter
ctx1.connect(l1, l2)
connect(l1, l3)
l1.nets.output(100, 0)
begin
l2.nets.output(101, 0)
raise "internal error - l2 was not used in connectivity"
rescue => ex
end
l3.nets.output(102, 0)
l1.nets(ctx1).output(110, 0)
l2.nets(ctx1).output(111, 0)
begin
l3.nets(ctx1).output(112, 0)
raise "internal error - l3 was not used in connectivity"
rescue => ex
end

BIN
testdata/drc/drcSimpleTests_71.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au71.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au71d.gds vendored Normal file

Binary file not shown.

View File

@ -3280,6 +3280,44 @@ class BasicTest(unittest.TestCase):
self.assertEqual(bb.d4(1, "a", d=pya.BB.E.E3B, c=2.5), "1,a,2.5,101,nil") self.assertEqual(bb.d4(1, "a", d=pya.BB.E.E3B, c=2.5), "1,a,2.5,101,nil")
self.assertEqual(bb.d4(1, "a", 2.0, pya.BB.E.E3B, 42), "1,a,2,101,42") self.assertEqual(bb.d4(1, "a", 2.0, pya.BB.E.E3B, 42), "1,a,2,101,42")
# constness
def test_93(self):
b = pya.B()
self.assertEqual(b.is_const_object(), False)
bc = b._to_const_object()
self.assertEqual(bc.is_const_object(), True)
m = ""
try:
bc.set_str("abc")
self.assertEqual(1, 0)
except Exception as ex:
m = str(ex)
self.assertEqual(m, "Cannot call non-const method on a const reference in B.set_str")
b = pya.B()
bc = b
self.assertEqual(b._is_const_object(), False)
self.assertEqual(bc._is_const_object(), False)
bc = bc._to_const_object()
b.set_str("abc")
self.assertEqual(b._is_const_object(), False)
self.assertEqual(bc._is_const_object(), True)
self.assertEqual(b.str(), "abc")
self.assertEqual(bc.str(), "abc")
bnc = bc._const_cast()
self.assertEqual(b._is_const_object(), False)
self.assertEqual(bc._is_const_object(), True)
self.assertEqual(bnc._is_const_object(), False)
bnc.set_str("xyz")
self.assertEqual(b.str(), "xyz")
self.assertEqual(bc.str(), "xyz")
self.assertEqual(bnc.str(), "xyz")
# run unit tests # run unit tests
if __name__ == '__main__': if __name__ == '__main__':
suite = unittest.TestSuite() suite = unittest.TestSuite()

View File

@ -75,13 +75,20 @@ class DBLayoutTest(unittest.TestCase):
self.assertEqual( ly.cell_name(ci), "new_cell" ) self.assertEqual( ly.cell_name(ci), "new_cell" )
self.assertEqual( ly.cell_by_name("new_cell"), ci ) self.assertEqual( ly.cell_by_name("new_cell"), ci )
self.assertEqual( ly.cell(ci).cell_index(), ci )
self.assertEqual( ly.cell("new_cell").name, "new_cell" ) self.assertEqual( ly.cell("new_cell").name, "new_cell" )
self.assertEqual( repr(ly.cell("x")), "None" ) self.assertEqual( repr(ly.cell("x")), "None" )
lyc = ly.dup()
self.assertEqual( lyc._to_const_object().cell("new_cell").name, "new_cell" )
self.assertEqual( lyc._to_const_object().cell(ci).cell_index(), ci )
ci2 = ly.add_cell( "new_cell_b" ) ci2 = ly.add_cell( "new_cell_b" )
self.assertEqual( ly.cells(), 2 ) self.assertEqual( ly.cells(), 2 )
self.assertEqual( ly.cell_by_name("new_cell_b"), ci2 ) self.assertEqual( ly.cell_by_name("new_cell_b"), ci2 )
self.assertEqual( sorted([ c.name for c in ly.cells("new*") ]), ['new_cell', 'new_cell_b'] )
self.assertEqual( ci != ci2, True ) self.assertEqual( ci != ci2, True )
lyc = ly.dup()
self.assertEqual( sorted([ c.name for c in lyc._to_const_object().cells("new*") ]), ['new_cell', 'new_cell_b'] )
ly.rename_cell( ci2, "x" ) ly.rename_cell( ci2, "x" )
self.assertEqual( ly.cell_by_name("x"), ci2 ) self.assertEqual( ly.cell_by_name("x"), ci2 )
@ -918,6 +925,8 @@ class DBLayoutTest(unittest.TestCase):
tc.append(l.cell(t).name) tc.append(l.cell(t).name)
self.assertEqual(",".join(tc), "c0") self.assertEqual(",".join(tc), "c0")
self.assertEqual(l.top_cell().name, "c0") self.assertEqual(l.top_cell().name, "c0")
lc = l.dup()
self.assertEqual(lc._to_const_object().top_cell().name, "c0") # const version
tc = [] tc = []
for t in l.top_cells(): for t in l.top_cells():
tc.append(t.name) tc.append(t.name)
@ -939,6 +948,11 @@ class DBLayoutTest(unittest.TestCase):
for t in l.top_cells(): for t in l.top_cells():
tc.append(t.name) tc.append(t.name)
self.assertEqual(",".join(tc), "c0,c1") self.assertEqual(",".join(tc), "c0,c1")
tc = []
lc = l.dup()
for t in lc._to_const_object().top_cells(): # const version
tc.append(t.name)
self.assertEqual(",".join(tc), "c0,c1")
c2 = l.create_cell("c1") c2 = l.create_cell("c1")
self.assertEqual(c2.name, "c1$1") self.assertEqual(c2.name, "c1$1")

View File

@ -3233,4 +3233,45 @@ class Basic_TestClass < TestBase
end end
# constness
def test_82
b = RBA::B::new
assert_equal(b.is_const_object, false)
bc = b._to_const_object
assert_equal(bc.is_const_object, true)
m = ""
begin
bc.set_str("abc")
assert_equal(1, 0)
rescue => ex
m = ex.to_s
end
assert_equal(m, "Cannot call non-const method on a const reference in B::set_str")
b = RBA::B::new
bc = b
assert_equal(b._is_const_object, false)
assert_equal(bc._is_const_object, false)
b.set_str("abc")
bc._to_const_object
assert_equal(b._is_const_object, true) # special
assert_equal(bc._is_const_object, true)
assert_equal(b.str, "abc")
assert_equal(bc.str, "abc")
bnc = bc._const_cast
assert_equal(b._is_const_object, false) # special
assert_equal(bc._is_const_object, false) # special
assert_equal(bnc._is_const_object, false)
bnc.set_str("xyz")
assert_equal(b.str, "xyz")
assert_equal(bc.str, "xyz")
assert_equal(bnc.str, "xyz")
end
end end

View File

@ -83,14 +83,21 @@ class DBLayoutTests2_TestClass < TestBase
assert_equal( ly.cell_name(ci), "new_cell" ) assert_equal( ly.cell_name(ci), "new_cell" )
assert_equal( ly.cell_by_name("new_cell"), ci ) assert_equal( ly.cell_by_name("new_cell"), ci )
assert_equal( ly.cell(ci).cell_index, ci )
assert_equal( ly.cells("A*"), [] ) assert_equal( ly.cells("A*"), [] )
assert_equal( ly.cell("new_cell").name, "new_cell" ) assert_equal( ly.cell("new_cell").name, "new_cell" )
assert_equal( ly.cell("x").inspect, "nil" ) assert_equal( ly.cell("x").inspect, "nil" )
lyc = ly.dup
assert_equal( lyc._to_const_object.cell("new_cell").name, "new_cell" )
assert_equal( lyc._to_const_object.cell(ci).cell_index, ci )
ci2 = ly.add_cell( "new_cell_b" ) ci2 = ly.add_cell( "new_cell_b" )
assert_equal( ly.cells, 2 ) assert_equal( ly.cells, 2 )
assert_equal( ly.cell_by_name("new_cell_b"), ci2 ) assert_equal( ly.cell_by_name("new_cell_b"), ci2 )
assert_equal( ly.cells("new*").collect { |c| c.name }.sort, ['new_cell', 'new_cell_b'] )
assert_equal( ci != ci2, true ) assert_equal( ci != ci2, true )
lyc = ly.dup
assert_equal( lyc._to_const_object.cells("new*").collect { |c| c.name }.sort, ['new_cell', 'new_cell_b'] )
ly.rename_cell( ci2, "x" ) ly.rename_cell( ci2, "x" )
assert_equal( ly.cell_by_name("x"), ci2 ) assert_equal( ly.cell_by_name("x"), ci2 )
@ -900,6 +907,9 @@ class DBLayoutTests2_TestClass < TestBase
assert_equal(tc.collect { |s| s.to_s }.join(","), "c0") assert_equal(tc.collect { |s| s.to_s }.join(","), "c0")
assert_equal(l.top_cell.name, "c0") assert_equal(l.top_cell.name, "c0")
assert_equal(l.top_cells.collect { |t| t.name }.join(","), "c0") assert_equal(l.top_cells.collect { |t| t.name }.join(","), "c0")
lc = l.dup
assert_equal(lc._to_const_object.top_cell.name, "c0")
assert_equal(lc._to_const_object.top_cells.collect { |t| t.name }.join(","), "c0")
c1 = l.create_cell("c1") c1 = l.create_cell("c1")
assert_equal(c1.name, "c1") assert_equal(c1.name, "c1")

View File

@ -900,5 +900,41 @@ class DBPCell_TestClass < TestBase
end end
class DBPCellParameterStates_TestClass < TestBase
def test_1
ps = RBA::PCellParameterState::new
ps.value = 17
assert_equal(ps.value, 17)
ps.value = "u"
assert_equal(ps.value, "u")
ps.visible = true
assert_equal(ps.is_visible?, true)
ps.visible = false
assert_equal(ps.is_visible?, false)
ps.enabled = true
assert_equal(ps.is_enabled?, true)
ps.enabled = false
assert_equal(ps.is_enabled?, false)
ps.readonly = true
assert_equal(ps.is_readonly?, true)
ps.readonly = false
assert_equal(ps.is_readonly?, false)
ps.tooltip = "uvw"
assert_equal(ps.tooltip, "uvw")
ps.icon = RBA::PCellParameterState::InfoIcon
assert_equal(ps.icon, RBA::PCellParameterState::InfoIcon)
end
end
load("test_epilogue.rb") load("test_epilogue.rb")