Merge pull request #1744 from KLayout/wip

OASIS reader: avoiding slight rounding of DBU In python read/write cy…
This commit is contained in:
Matthias Köfferlein 2024-06-29 20:11:40 +02:00 committed by GitHub
commit 6e2c11435b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 554 additions and 122 deletions

View File

@ -758,6 +758,9 @@ bool run_deep_xor (const XORData &xor_data)
{ {
db::DeepShapeStore dss; db::DeepShapeStore dss;
dss.set_threads (xor_data.threads); dss.set_threads (xor_data.threads);
// TODO: this conflicts with the "set_for_merged_input" optimization below.
// It seems not to be very effective then. Why?
dss.set_wants_all_cells (true); // saves time for less cell mapping operations dss.set_wants_all_cells (true); // saves time for less cell mapping operations
double dbu = std::min (xor_data.layout_a->dbu (), xor_data.layout_b->dbu ()); double dbu = std::min (xor_data.layout_a->dbu (), xor_data.layout_b->dbu ());

View File

@ -407,7 +407,7 @@ DeepRegion::begin_iter () const
} else { } else {
const db::Cell &top_cell = layout.cell (*layout.begin_top_down ()); const db::Cell &top_cell = layout.cell (*layout.begin_top_down ());
db::RecursiveShapeIterator iter (deep_layer ().layout (), top_cell, deep_layer ().layer ()); db::RecursiveShapeIterator iter (layout, top_cell, deep_layer ().layer ());
return std::make_pair (iter, db::ICplxTrans ()); return std::make_pair (iter, db::ICplxTrans ());
} }
@ -803,7 +803,7 @@ DeepRegion::and_with (const Region &other, PropertyConstraint property_constrain
} else { } else {
return new DeepRegion (and_or_not_with (other_deep, true, property_constraint)); return new DeepRegion (and_with_impl (other_deep, property_constraint));
} }
} }
@ -827,7 +827,7 @@ DeepRegion::not_with (const Region &other, PropertyConstraint property_constrain
} else { } else {
return new DeepRegion (and_or_not_with (other_deep, false, property_constraint)); return new DeepRegion (not_with_impl (other_deep, property_constraint));
} }
} }
@ -875,13 +875,13 @@ DeepRegion::andnot_with (const Region &other, PropertyConstraint property_constr
} }
DeepLayer DeepLayer
DeepRegion::and_or_not_with (const DeepRegion *other, bool and_op, db::PropertyConstraint property_constraint) const DeepRegion::and_with_impl (const DeepRegion *other, db::PropertyConstraint property_constraint) const
{ {
DeepLayer dl_out (deep_layer ().derived ()); DeepLayer dl_out (deep_layer ().derived ());
if (pc_skip (property_constraint)) { if (pc_skip (property_constraint)) {
db::BoolAndOrNotLocalOperation op (and_op); db::BoolAndOrNotLocalOperation op (true);
db::local_processor<db::PolygonRef, db::PolygonRef, db::PolygonRef> proc (const_cast<db::Layout *> (&deep_layer ().layout ()), const_cast<db::Cell *> (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ()); db::local_processor<db::PolygonRef, db::PolygonRef, db::PolygonRef> proc (const_cast<db::Layout *> (&deep_layer ().layout ()), const_cast<db::Cell *> (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ());
configure_proc (proc); configure_proc (proc);
@ -893,7 +893,7 @@ DeepRegion::and_or_not_with (const DeepRegion *other, bool and_op, db::PropertyC
} else { } else {
db::BoolAndOrNotLocalOperationWithProperties op (and_op, &dl_out.layout ().properties_repository (), &deep_layer ().layout ().properties_repository (), &other->deep_layer ().layout ().properties_repository (), property_constraint); db::BoolAndOrNotLocalOperationWithProperties op (true, &dl_out.layout ().properties_repository (), &deep_layer ().layout ().properties_repository (), &other->deep_layer ().layout ().properties_repository (), property_constraint);
db::local_processor<db::PolygonRefWithProperties, db::PolygonRefWithProperties, db::PolygonRefWithProperties> proc (const_cast<db::Layout *> (&deep_layer ().layout ()), const_cast<db::Cell *> (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ()); db::local_processor<db::PolygonRefWithProperties, db::PolygonRefWithProperties, db::PolygonRefWithProperties> proc (const_cast<db::Layout *> (&deep_layer ().layout ()), const_cast<db::Cell *> (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ());
configure_proc (proc); configure_proc (proc);
@ -908,6 +908,109 @@ DeepRegion::and_or_not_with (const DeepRegion *other, bool and_op, db::PropertyC
return dl_out; return dl_out;
} }
DeepLayer
DeepRegion::not_with_impl (const DeepRegion *other, db::PropertyConstraint property_constraint) const
{
DeepLayer dl_out (deep_layer ().derived ());
DeepLayer dl_prep;
// first run a local-only task, which is trivial if the layouts are the same
// TODO: consider property constraints too.
if (deep_layer ().layout_index () == other->deep_layer ().layout_index () && pc_skip (property_constraint)) {
dl_prep = deep_layer ().derived ();
unsigned int layer_this = deep_layer ().layer ();
unsigned int layer_other = other->deep_layer ().layer ();
unsigned int layer_out = dl_prep.layer ();
db::Layout &layout = dl_prep.layout ();
bool any = false;
if (layer_this != layer_other) {
std::set<db::cell_index_type> called;
deep_layer ().initial_cell ().collect_called_cells (called);
called.insert (deep_layer ().initial_cell ().cell_index ());
for (auto ci = called.begin (); ci != called.end (); ++ci) {
db::Cell &cell = layout.cell (*ci);
db::Shapes &shapes_out = cell.shapes (layer_out);
const db::Shapes &shapes_this = cell.shapes (layer_this);
const db::Shapes &shapes_other = cell.shapes (layer_other);
db::Box overlap = (shapes_this.bbox () & shapes_other.bbox ());
if (! overlap.empty ()) {
std::set<db::PolygonRef> others;
for (auto si = shapes_other.begin (db::ShapeIterator::Polygons); ! si.at_end (); ++si) {
if (si->type () == db::Shape::PolygonRef) {
others.insert (si->polygon_ref ());
}
}
for (auto si = shapes_this.begin (db::ShapeIterator::Polygons); ! si.at_end (); ++si) {
if (si->type () == db::Shape::PolygonRef && others.find (si->polygon_ref ()) != others.end ()) {
// drop duplicate
} else {
any = true;
shapes_out.insert (*si);
}
}
} else if (! shapes_this.empty ()) {
any = true;
shapes_out = shapes_this;
}
}
}
// stop if empty
if (! any) {
return dl_prep;
}
} else {
dl_prep = deep_layer ();
}
if (pc_skip (property_constraint)) {
db::BoolAndOrNotLocalOperation op (false);
db::local_processor<db::PolygonRef, db::PolygonRef, db::PolygonRef> proc (const_cast<db::Layout *> (&deep_layer ().layout ()), const_cast<db::Cell *> (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ());
configure_proc (proc);
proc.set_threads (deep_layer ().store ()->threads ());
proc.set_area_ratio (deep_layer ().store ()->max_area_ratio ());
proc.set_max_vertex_count (deep_layer ().store ()->max_vertex_count ());
proc.run (&op, dl_prep.layer (), other->deep_layer ().layer (), dl_out.layer ());
} else {
db::BoolAndOrNotLocalOperationWithProperties op (false, &dl_out.layout ().properties_repository (), &deep_layer ().layout ().properties_repository (), &other->deep_layer ().layout ().properties_repository (), property_constraint);
db::local_processor<db::PolygonRefWithProperties, db::PolygonRefWithProperties, db::PolygonRefWithProperties> proc (const_cast<db::Layout *> (&deep_layer ().layout ()), const_cast<db::Cell *> (&deep_layer ().initial_cell ()), &other->deep_layer ().layout (), &other->deep_layer ().initial_cell (), deep_layer ().breakout_cells (), other->deep_layer ().breakout_cells ());
configure_proc (proc);
proc.set_threads (deep_layer ().store ()->threads ());
proc.set_area_ratio (deep_layer ().store ()->max_area_ratio ());
proc.set_max_vertex_count (deep_layer ().store ()->max_vertex_count ());
proc.run (&op, dl_prep.layer (), other->deep_layer ().layer (), dl_out.layer ());
}
return dl_out;
}
std::pair<DeepLayer, DeepLayer> std::pair<DeepLayer, DeepLayer>
DeepRegion::and_and_not_with (const DeepRegion *other, PropertyConstraint property_constraint) const DeepRegion::and_and_not_with (const DeepRegion *other, PropertyConstraint property_constraint) const
{ {
@ -1008,8 +1111,8 @@ DeepRegion::xor_with (const Region &other, db::PropertyConstraint property_const
other_deep_mapped->disable_progress (); other_deep_mapped->disable_progress ();
} }
DeepLayer n1 (and_or_not_with (other_deep_mapped.get (), false, property_constraint)); DeepLayer n1 (not_with_impl (other_deep_mapped.get (), property_constraint));
DeepLayer n2 (other_deep_mapped->and_or_not_with (this, false, property_constraint)); DeepLayer n2 (other_deep_mapped->not_with_impl (this, property_constraint));
n1.add_from (n2); n1.add_from (n2);
return new DeepRegion (n1); return new DeepRegion (n1);

View File

@ -178,7 +178,8 @@ private:
void init (); void init ();
void ensure_merged_polygons_valid () const; void ensure_merged_polygons_valid () const;
DeepLayer and_or_not_with(const DeepRegion *other, bool and_op, PropertyConstraint property_constraint) const; DeepLayer not_with_impl (const DeepRegion *other, PropertyConstraint property_constraint) const;
DeepLayer and_with_impl (const DeepRegion *other, PropertyConstraint property_constraint) const;
std::pair<DeepLayer, DeepLayer> and_and_not_with (const DeepRegion *other, PropertyConstraint property_constraint) const; std::pair<DeepLayer, DeepLayer> and_and_not_with (const DeepRegion *other, PropertyConstraint property_constraint) const;
DeepRegion *apply_filter (const PolygonFilterBase &filter) const; DeepRegion *apply_filter (const PolygonFilterBase &filter) const;

View File

@ -1737,12 +1737,14 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts_unlocked) CRONOLOGY_COLLECTION_BRACKET(event_compute_contexts_unlocked)
std::map<unsigned int, const db::Shapes *> intruder_shapes; std::map<unsigned int, const db::Shapes *> intruder_shapes;
if (intruder_cell) { if (intruder_cell) {
for (std::vector<unsigned int>::const_iterator l = contexts.intruder_layers ().begin (); l != contexts.intruder_layers ().end (); ++l) { for (std::vector<unsigned int>::const_iterator l = contexts.intruder_layers ().begin (); l != contexts.intruder_layers ().end (); ++l) {
const db::Shapes *s = &intruder_cell->shapes (contexts.actual_intruder_layer (*l)); const db::Shapes *s = &intruder_cell->shapes (contexts.actual_intruder_layer (*l));
if (! s->empty ()) { if (! s->empty ()) {
intruder_shapes.insert (std::make_pair (*l, s)); intruder_shapes.insert (std::make_pair (*l, s));
} }
} }
} }
db::box_convert <db::CellInstArray, true> inst_bcs (*mp_subject_layout, contexts.subject_layer ()); db::box_convert <db::CellInstArray, true> inst_bcs (*mp_subject_layout, contexts.subject_layer ());
@ -1824,8 +1826,8 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
} }
// TODO: can we shortcut this if interactions is empty? if (! intruders.second.empty () || ! intruder_shapes.empty ()) {
{
db::box_scanner2<db::CellInstArray, int, TI, int> scanner; db::box_scanner2<db::CellInstArray, int, TI, int> scanner;
db::addressable_object_from_shape<TI> heap; db::addressable_object_from_shape<TI> heap;
interaction_registration_inst2shape<TS, TI, TR> rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions); interaction_registration_inst2shape<TS, TI, TR> rec (mp_subject_layout, contexts.subject_layer (), dist, &interactions);
@ -1849,6 +1851,7 @@ void local_processor<TS, TI, TR>::compute_contexts (local_processor_contexts<TS,
} }
scanner.process (rec, dist, inst_bcs, db::box_convert<TI> ()); scanner.process (rec, dist, inst_bcs, db::box_convert<TI> ());
} }
// produce the tasks for computing the next-level interactions // produce the tasks for computing the next-level interactions

View File

@ -1008,7 +1008,7 @@ std::string LayoutToNetlist::name (const ShapeCollection &coll) const
} }
} }
void LayoutToNetlist::register_layer (const ShapeCollection &collection, const std::string &n_in) unsigned int LayoutToNetlist::register_layer (const ShapeCollection &collection, const std::string &n_in)
{ {
if (m_region_by_original.find (tl::id_of (collection.get_delegate ())) != m_region_by_original.end ()) { if (m_region_by_original.find (tl::id_of (collection.get_delegate ())) != m_region_by_original.end ()) {
throw tl::Exception (tl::to_string (tr ("The layer is already registered"))); throw tl::Exception (tl::to_string (tr ("The layer is already registered")));
@ -1042,10 +1042,14 @@ void LayoutToNetlist::register_layer (const ShapeCollection &collection, const s
} }
unsigned int layer = dl.layer ();
m_region_by_original [tl::id_of (collection.get_delegate ())] = dl; m_region_by_original [tl::id_of (collection.get_delegate ())] = dl;
m_region_of_layer [dl.layer ()] = dl; m_region_of_layer [layer] = dl;
m_named_regions [n] = dl; m_named_regions [n] = dl;
m_name_of_layer [dl.layer ()] = n; m_name_of_layer [layer] = n;
return layer;
} }
db::DeepLayer LayoutToNetlist::deep_layer_of (const db::ShapeCollection &coll) const db::DeepLayer LayoutToNetlist::deep_layer_of (const db::ShapeCollection &coll) const

View File

@ -317,7 +317,7 @@ public:
* In addition to regions, text collections can be registered too. * In addition to regions, text collections can be registered too.
* Including texts in "connect" makes net names begin assigned from the text strings. * Including texts in "connect" makes net names begin assigned from the text strings.
*/ */
void register_layer (const ShapeCollection &collection, const std::string &name = std::string ()); unsigned int register_layer (const ShapeCollection &collection, const std::string &name = std::string ());
/** /**
* @brief Gets the name of the given collection * @brief Gets the name of the given collection

View File

@ -51,6 +51,7 @@ class RecursiveShapeReceiver;
* shape classes and shape properties. * shape classes and shape properties.
*/ */
class DB_PUBLIC RecursiveShapeIterator class DB_PUBLIC RecursiveShapeIterator
: public gsi::ObjectBase
{ {
public: public:
typedef db::Layout layout_type; typedef db::Layout layout_type;

View File

@ -330,11 +330,16 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
gsi::method ("layer_name", (std::string (db::LayoutToNetlist::*) (const db::ShapeCollection &region) const) &db::LayoutToNetlist::name, gsi::arg ("l"), gsi::method ("layer_name", (std::string (db::LayoutToNetlist::*) (const db::ShapeCollection &region) const) &db::LayoutToNetlist::name, gsi::arg ("l"),
"@brief Gets the name of the given layer\n" "@brief Gets the name of the given layer\n"
) + ) +
gsi::method ("layer_index", (unsigned int (db::LayoutToNetlist::*) (const db::ShapeCollection &region) const) &db::LayoutToNetlist::layer_of<db::ShapeCollection>, gsi::arg ("l"),
"@brief Gets the layer index for the given data object\n"
"This method has been introduced in version 0.29.3.\n"
) +
gsi::method ("layer_name", (std::string (db::LayoutToNetlist::*) (unsigned int) const) &db::LayoutToNetlist::name, gsi::arg ("l"), gsi::method ("layer_name", (std::string (db::LayoutToNetlist::*) (unsigned int) const) &db::LayoutToNetlist::name, gsi::arg ("l"),
"@brief Gets the name of the given layer (by index)\n" "@brief Gets the name of the given layer (by index)\n"
) + ) +
gsi::method ("register", (void (db::LayoutToNetlist::*) (const db::ShapeCollection &collection, const std::string &)) &db::LayoutToNetlist::register_layer, gsi::arg ("l"), gsi::arg ("n", std::string ()), gsi::method ("register", (unsigned int (db::LayoutToNetlist::*) (const db::ShapeCollection &collection, const std::string &)) &db::LayoutToNetlist::register_layer, gsi::arg ("l"), gsi::arg ("n", std::string ()),
"@brief Names the given layer\n" "@brief Names the given layer\n"
"@return The index of the layer registered\n"
"'l' must be a \\Region or \\Texts object.\n" "'l' must be a \\Region or \\Texts object.\n"
"Flat regions or text collections must be registered with this function, before they can be used in \\connect. " "Flat regions or text collections must be registered with this function, before they can be used in \\connect. "
"Registering will copy the shapes into the LayoutToNetlist object in this step to enable " "Registering will copy the shapes into the LayoutToNetlist object in this step to enable "
@ -346,7 +351,7 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"\n" "\n"
"If required, the system will assign a name automatically." "If required, the system will assign a name automatically."
"\n" "\n"
"This method has been generalized in version 0.27.\n" "This method has been generalized in version 0.27. Starting with version 0.29.3, the index of the layer is returned.\n"
) + ) +
gsi::method_ext ("layer_names", &l2n_layer_names, gsi::method_ext ("layer_names", &l2n_layer_names,
"@brief Returns a list of names of the layers kept inside the LayoutToNetlist object." "@brief Returns a list of names of the layers kept inside the LayoutToNetlist object."

View File

@ -299,6 +299,8 @@ module DRC
# @name name # @name name
# @brief Assigns a name to a layer # @brief Assigns a name to a layer
# @synopsis name(layer, name) # @synopsis name(layer, name)
# @synopsis name(layer, name, layer_number, datatype_number)
# @synopsis name(layer, name, layer_info)
# Layer names are listed in the LayoutToNetlist (L2N) or LVS database. They # Layer names are listed in the LayoutToNetlist (L2N) or LVS database. They
# are used to identify the layers, the net or device terminal geometries are # are used to identify the layers, the net or device terminal geometries are
# on. It is usual to have computed layers, so it is necessary to indicate the # on. It is usual to have computed layers, so it is necessary to indicate the
@ -325,6 +327,12 @@ module DRC
# #
# \name can only be used once on a layer and the layer names must be # \name can only be used once on a layer and the layer names must be
# unique (not taken by another layer). # unique (not taken by another layer).
#
# The layer/datatype or LayerInfo specification is optional and will
# be used to configure the internal layout. This information is also
# persisted inside database files. Specifying a layer/datatype information
# is useful, if a layer is not an original layer, but is to be restored
# to an actual layout layer later.
# %DRC% # %DRC%
# @name name_prefix # @name name_prefix
@ -332,13 +340,33 @@ module DRC
# @synopsis name_prefix(prefix) # @synopsis name_prefix(prefix)
# See \\name for details. The default prefix is "l". # See \\name for details. The default prefix is "l".
def name(l, name) def name(*args)
@engine._context("name") do @engine._context("name") do
if args.size < 2 || args.size > 4
raise("Two to four arguments expected (layer, name, [layer, datatype / layer properties])")
end
l = args[0]
l.is_a?(DRC::DRCLayer) || raise("First argument must be a layer") l.is_a?(DRC::DRCLayer) || raise("First argument must be a layer")
name = args[1]
(name.is_a?(String) && name != "") || raise("Second argument must be a non-empty string") (name.is_a?(String) && name != "") || raise("Second argument must be a non-empty string")
lp = args[2]
if lp
if !lp.is_a?(RBA::LayerInfo)
lnum = args[2]
dnum = args[3] || 0
lnum.is_a?(1.class) || raise("Layer argument needs to be a RBA::LayerInfo object or a layer number")
dnum.is_a?(1.class) || raise("Datatype argument needs to be an integer")
lp = RBA::LayerInfo::new(lnum, dnum)
elsif args[3]
raise("Three arguments are enough with RBA::LayerInfo")
end
end
id = l.data.data_id id = l.data.data_id
if @layers && @layers[id] if @layers && @layers[id]
@ -349,7 +377,7 @@ module DRC
return return
end end
self._register_layer(l.data, "name", name) self._register_layer(l.data, "name", name, lp)
end end
@ -828,7 +856,7 @@ module DRC
protected protected
def _register_layer(data, context, name = nil) def _register_layer(data, context, name = nil, lp = nil)
id = data.data_id id = data.data_id
ensure_data ensure_data
@ -846,7 +874,12 @@ module DRC
@layers[id] = [ data, name, context ] @layers[id] = [ data, name, context ]
# every layer gets registered and intra-layer connections are made # every layer gets registered and intra-layer connections are made
@l2n.register(data, name) index = @l2n.register(data, name)
# set the layer properties if requested
if lp
@l2n.internal_layout.set_info(index, lp)
end
end end

View File

@ -1911,3 +1911,35 @@ TEST(121_ShapesOfTerminal)
db::compare_layouts (_this, layout, au, db::NoNormalization); db::compare_layouts (_this, layout, au, db::NoNormalization);
} }
TEST(122_NamedLayers)
{
std::string rs = tl::testdata ();
rs += "/drc/drcSimpleTests_122.drc";
std::string input = tl::testdata ();
input += "/drc/drcSimpleTests_122.gds";
std::string au_output = tl::testdata ();
au_output += "/drc/drcSimpleTests_au122.l2n";
std::string output = this->tmp_file ("tmp.l2n");
{
// Set some variables
lym::Macro config;
config.set_text (tl::sprintf (
"$drc_test_source = '%s'\n"
"$drc_test_target = '%s'\n"
, input, output)
);
config.set_interpreter (lym::Macro::Ruby);
EXPECT_EQ (config.run (), 0);
}
lym::Macro drc;
drc.load_from (rs);
EXPECT_EQ (drc.run (), 0);
compare_text_files (output, au_output);
}

View File

@ -1297,10 +1297,10 @@ static gsi::Class<B> decl_b ("", "B",
gsi::method ("an_cref", &B::an_cref) + gsi::method ("an_cref", &B::an_cref) +
// implemented by extension below: // implemented by extension below:
// gsi::iterator_ext ("b10", &b10b_ext, &b10e_ext) + // gsi::iterator_ext ("b10", &b10b_ext, &b10e_ext) +
gsi::iterator ("b10_nc", &B::b10b_nc, &B::b10e_nc) + gsi::iterator ("b10_nc|each_a_be_nc", &B::b10b_nc, &B::b10e_nc) +
gsi::iterator ("b11", &B::b11b, &B::b11e) + gsi::iterator ("b11|each_a_be_v", &B::b11b, &B::b11e) +
gsi::iterator ("b12", &B::b12b, &B::b12e) + gsi::iterator ("b12|each_a_be_p", &B::b12b, &B::b12e) +
gsi::iterator ("b13", &B::b13b, &B::b13e) + gsi::iterator ("b13|each_a_be_cp", &B::b13b, &B::b13e) +
gsi::method ("amember_or_nil_alt|amember_or_nil", &B::amember_or_nil) + gsi::method ("amember_or_nil_alt|amember_or_nil", &B::amember_or_nil) +
gsi::method ("amember_ptr_alt|amember_ptr", &B::amember_ptr) + gsi::method ("amember_ptr_alt|amember_ptr", &B::amember_ptr) +
gsi::method ("xxx|amember_cptr", &B::amember_cptr) + gsi::method ("xxx|amember_cptr", &B::amember_cptr) +
@ -1444,8 +1444,8 @@ static gsi::Class<B> decl_b ("", "B",
// extending B // extending B
static gsi::ClassExt<B> b_ext ( static gsi::ClassExt<B> b_ext (
gsi::iterator_ext ("b10", &b10b_ext, &b10e_ext) + gsi::iterator_ext ("b10|each_a_be", &b10b_ext, &b10e_ext) +
gsi::iterator_ext ("b10p", &b10bp_ext, &b10ep_ext) gsi::iterator_ext ("b10p|each_a_be_pp", &b10bp_ext, &b10ep_ext)
); );
CopyDetector *new_cd (int x) CopyDetector *new_cd (int x)

View File

@ -516,7 +516,7 @@ MacroController::sync_macro_sources ()
if (! t->base_path ().empty ()) { if (! t->base_path ().empty ()) {
QDir base_dir (tl::to_qstring (t->base_path ())); QDir base_dir (tl::to_qstring (t->base_path ()));
if (base_dir.exists ()) { if (base_dir.exists ()) {
std::string path = tl::to_string (base_dir.absolutePath ()); std::string path = tl::to_string (base_dir.canonicalPath ());
tech_names_by_path [path].push_back (t->name ()); tech_names_by_path [path].push_back (t->name ());
if (t->is_readonly ()) { if (t->is_readonly ()) {
readonly_paths.insert (path); readonly_paths.insert (path);
@ -559,7 +559,7 @@ MacroController::sync_macro_sources ()
description += " - " + tl::to_string (tr ("%1 branch").arg (tl::to_qstring (*f))); description += " - " + tl::to_string (tr ("%1 branch").arg (tl::to_qstring (*f)));
} }
external_paths.push_back (ExternalPathDescriptor (tl::to_string (macro_dir.path ()), description, macro_categories () [c].name, lym::MacroCollection::TechFolder, readonly_paths.find (t->first) != readonly_paths.end ())); external_paths.push_back (ExternalPathDescriptor (tl::to_string (macro_dir.canonicalPath ()), description, macro_categories () [c].name, lym::MacroCollection::TechFolder, readonly_paths.find (t->first) != readonly_paths.end ()));
} }
@ -591,7 +591,7 @@ MacroController::sync_macro_sources ()
if (*f != macro_categories () [c].name) { if (*f != macro_categories () [c].name) {
description += " - " + tl::to_string (tr ("%1 branch").arg (tl::to_qstring (*f))); description += " - " + tl::to_string (tr ("%1 branch").arg (tl::to_qstring (*f)));
} }
external_paths.push_back (ExternalPathDescriptor (tl::to_string (macro_dir.path ()), description, macro_categories () [c].name, lym::MacroCollection::SaltFolder, g->is_readonly (), g->version ())); external_paths.push_back (ExternalPathDescriptor (tl::to_string (macro_dir.canonicalPath ()), description, macro_categories () [c].name, lym::MacroCollection::SaltFolder, g->is_readonly (), g->version ()));
} }

View File

@ -1507,29 +1507,39 @@ MacroEditorDialog::eventFilter (QObject *obj, QEvent *event)
return false; return false;
} }
if (lay::BusySection::is_busy () && (m_in_breakpoint || m_in_exec) && (dynamic_cast <QInputEvent *> (event) != 0 || dynamic_cast <QPaintEvent *> (event) != 0)) { if (m_in_exec) {
if (lay::BusySection::is_busy ()) {
#if 0
if (dynamic_cast <QInputEvent *> (event) != 0 || dynamic_cast <QPaintEvent *> (event) != 0) {
// In breakpoint or execution mode and while processing the events inside the debugger,
// ignore all input or paint events targeted to widgets which are not children of this or the assistant dialog.
// Ignoring the paint event is required because otherwise a repaint action would be triggered on a layout which
// is potentially unstable or inconsistent.
// We nevertheless allow events send to a HelpDialog or ProgressWidget since those are vital for the application's
// functionality and are known not to cause any interference.
QObject *rec = obj;
while (rec && (rec != this && !dynamic_cast<lay::HelpDialog *> (rec) && !dynamic_cast<lay::ProgressWidget *> (rec))) {
rec = rec->parent ();
}
if (! rec) {
// TODO: reschedule the paint events (?)
return true;
}
}
#endif
} else {
// While no explicit event processing is in progress and we are executing, this is an indication that
// "real" events are processed. In that case, we can postpone excplit processing. This avoids interference
// with GUI code run in the debugger.
m_last_process_events = tl::Clock::current ();
// In breakpoint or execution mode and while processing the events from the debugger,
// ignore all input or paint events targeted to widgets which are not children of this or the assistant dialog.
// Ignoring the paint event is required because otherwise a repaint action would be triggered on a layout which
// is potentially unstable or inconsistent.
// We nevertheless allow events send to a HelpDialog or ProgressWidget since those are vital for the application's
// functionality and are known not to cause any interference.
QObject *rec = obj;
while (rec && (rec != this && !dynamic_cast<lay::HelpDialog *> (rec) && !dynamic_cast<lay::ProgressWidget *> (rec))) {
rec = rec->parent ();
} }
if (! rec) {
// TODO: reschedule the paint events (?)
return true;
}
} else if (! lay::BusySection::is_busy () && m_in_exec) {
// While no explicit event processing is in progress and we are executing, this is an indication that
// "real" events are processed. In that case, we can postpone excplit processing. This avoids interference
// with GUI code run in the debugger.
m_last_process_events = tl::Clock::current ();
} }
@ -3054,6 +3064,8 @@ MacroEditorDialog::start_exec (gsi::Interpreter *ec)
return; return;
} else if (m_ignore_exec_events) { } else if (m_ignore_exec_events) {
return; return;
} else if (lay::BusySection::is_busy () || m_in_breakpoint) {
return;
} }
// prevents recursion // prevents recursion
@ -3215,7 +3227,7 @@ MacroEditorDialog::exception_thrown (gsi::Interpreter *interpreter, size_t file_
exit_if_needed (); exit_if_needed ();
// avoid recursive breakpoints and exception catches from the console while in a breakpoint or exception stop // avoid recursive breakpoints and exception catches from the console while in a breakpoint or exception stop
if (lay::BusySection::is_busy ()) { if (lay::BusySection::is_busy () || m_in_breakpoint) {
return; return;
} }
@ -3317,7 +3329,7 @@ MacroEditorDialog::trace (gsi::Interpreter *interpreter, size_t file_id, int lin
exit_if_needed (); exit_if_needed ();
// avoid recursive breakpoints and exception catches from the console while in a breakpoint or exception stop // avoid recursive breakpoints and exception catches from the console while in a breakpoint or exception stop
if (lay::BusySection::is_busy ()) { if (lay::BusySection::is_busy () || m_in_breakpoint) {
return; return;
} }
@ -3682,7 +3694,7 @@ MacroEditorDialog::stop_button_clicked ()
{ {
if (QApplication::activeModalWidget () == this) { if (QApplication::activeModalWidget () == this) {
// close this window if it was shown in modal mode // close this window if it was shown in modal mode
accept (); QDialog::accept ();
} }
m_in_exec = false; m_in_exec = false;
@ -3748,12 +3760,9 @@ MacroEditorDialog::run (int stop_stack_depth, lym::Macro *macro)
if (QApplication::activeModalWidget () == this) { if (QApplication::activeModalWidget () == this) {
// close this window if it was shown in modal mode // close this window if it was shown in modal mode
accept (); QDialog::accept ();
} }
// in a breakpoint
m_in_breakpoint = false;
} else { } else {
if (! macro) { if (! macro) {

View File

@ -42,7 +42,6 @@ namespace db
OASISReader::OASISReader (tl::InputStream &s) OASISReader::OASISReader (tl::InputStream &s)
: m_stream (s), : m_stream (s),
m_progress (tl::to_string (tr ("Reading OASIS file")), 10000), m_progress (tl::to_string (tr ("Reading OASIS file")), 10000),
m_dbu (0.001),
m_expect_strict_mode (-1), m_expect_strict_mode (-1),
mm_repetition (this, "repetition"), mm_repetition (this, "repetition"),
mm_placement_cell (this, "placement-cell"), mm_placement_cell (this, "placement-cell"),
@ -648,8 +647,7 @@ OASISReader::do_read (db::Layout &layout)
} }
// compute database unit in pixel per meter // compute database unit in pixel per meter
m_dbu = 1.0e-6 / res; layout.dbu (1.0 / res);
layout.dbu (m_dbu * 1e6);
// read over table offsets if required // read over table offsets if required
bool table_offsets_at_end = get_uint (); bool table_offsets_at_end = get_uint ();

View File

@ -124,7 +124,6 @@ private:
tl::InputStream &m_stream; tl::InputStream &m_stream;
tl::AbsoluteProgress m_progress; tl::AbsoluteProgress m_progress;
std::string m_cellname; std::string m_cellname;
double m_dbu;
int m_expect_strict_mode; int m_expect_strict_mode;
size_t m_first_cellname; size_t m_first_cellname;
size_t m_first_propname; size_t m_first_propname;

View File

@ -869,6 +869,12 @@ handle_exception (const std::string & /*where*/, rba::RubyError &ex)
handle_exception (ex.exc (), ex.first_chance ()); handle_exception (ex.exc (), ex.first_chance ());
} }
static void
handle_exception (rba::RubyContinueException &ex)
{
rb_jump_tag (ex.state ());
}
static void static void
handle_exception (const std::string &where, tl::Exception &ex) handle_exception (const std::string &where, tl::Exception &ex)
{ {
@ -897,6 +903,8 @@ handle_exception (const std::string &where)
handle_exception ((where), ex); \ handle_exception ((where), ex); \
} catch (tl::ExitException &ex) { \ } catch (tl::ExitException &ex) { \
handle_exception ((where), ex); \ handle_exception ((where), ex); \
} catch (rba::RubyContinueException &ex) { \
handle_exception (ex); \
} catch (rba::RubyError &ex) { \ } catch (rba::RubyError &ex) { \
handle_exception ((where), ex); \ handle_exception ((where), ex); \
} catch (tl::Exception &ex) { \ } catch (tl::Exception &ex) { \
@ -1381,23 +1389,17 @@ method_adaptor (int mid, int argc, VALUE *argv, VALUE self, bool ctor)
std::unique_ptr<gsi::IterAdaptorAbstractBase> iter ((gsi::IterAdaptorAbstractBase *) retlist.read<void *> (heap)); std::unique_ptr<gsi::IterAdaptorAbstractBase> iter ((gsi::IterAdaptorAbstractBase *) retlist.read<void *> (heap));
if (iter.get ()) { if (iter.get ()) {
try { gsi::SerialArgs rr (iter->serial_size ());
while (! iter->at_end ()) {
gsi::SerialArgs rr (iter->serial_size ()); rr.reset ();
while (! iter->at_end ()) { iter->get (rr);
rr.reset (); VALUE value = pull_arg (meth->ret_type (), p, rr, heap);
iter->get (rr); rba_yield_checked (value);
VALUE value = pull_arg (meth->ret_type (), p, rr, heap); iter->inc ();
rba_yield_checked (value);
iter->inc ();
}
} catch (tl::CancelException &) {
// break encountered
} }
} }
@ -2402,7 +2404,7 @@ RubyInterpreter::require (const std::string &filename_utf8)
RUBY_END_EXEC RUBY_END_EXEC
if (error) { if (error) {
rba_check_error (); rba_check_error (error);
} }
} }
@ -2423,7 +2425,7 @@ RubyInterpreter::load_file (const std::string &filename_utf8)
RUBY_END_EXEC RUBY_END_EXEC
if (error) { if (error) {
rba_check_error (); rba_check_error (error);
} }
} }

View File

@ -65,6 +65,26 @@ private:
VALUE m_exc; VALUE m_exc;
}; };
/**
* @brief A class responsible for forwarding exceptions raised from "break", "return" and other flow control functions
*/
class RubyContinueException
: public tl::CancelException
{
public:
RubyContinueException (int state)
: tl::CancelException (), m_state (state)
{ }
int state () const
{
return m_state;
}
private:
int m_state;
};
/** /**
* @brief The proxy object that represents the C++ object on the Ruby side * @brief The proxy object that represents the C++ object on the Ruby side
*/ */

View File

@ -127,26 +127,23 @@ exceptions_blocked ()
} }
void void
rba_check_error () rba_check_error (int state)
{ {
VALUE lasterr = rb_errinfo (); VALUE lasterr = rb_errinfo ();
// NOTE: this seems to be required to avoid segfaults on Ruby 2.3.1 after // Ruby employs this pseudo-exception to indicate a "break" or "return" of a loop.
// a break was encountered. // As this is an opaque condition, we continue Ruby execution later through a "RubyContinueException".
rb_set_errinfo (Qnil);
// Ruby employs this pseudo-exception to indicate a "break" of a loop
#if HAVE_RUBY_VERSION_CODE < 10900 #if HAVE_RUBY_VERSION_CODE < 10900
if (lasterr == Qnil) { if (lasterr == Qnil) {
throw tl::CancelException (); throw RubyContinueException (state);
} }
#elif HAVE_RUBY_VERSION_CODE < 20300 #elif HAVE_RUBY_VERSION_CODE < 20300
if (TYPE (lasterr) == T_NODE) { if (TYPE (lasterr) == T_NODE) {
throw tl::CancelException (); throw RubyContinueException (state);
} }
#else #else
if (TYPE (lasterr) == T_IMEMO) { if (TYPE (lasterr) == T_IMEMO) {
throw tl::CancelException (); throw RubyContinueException (state);
} }
#endif #endif
@ -405,7 +402,7 @@ rba_class_new_instance_checked (int argc, VALUE *argv, VALUE klass)
RUBY_END_EXEC RUBY_END_EXEC
if (error) { if (error) {
rba_check_error (); rba_check_error (error);
} }
return ret; return ret;
} }
@ -458,7 +455,7 @@ VALUE rba_funcall2_checked (VALUE obj, ID id, int argc, VALUE *args)
RUBY_END_EXEC RUBY_END_EXEC
if (error) { if (error) {
rba_check_error (); rba_check_error (error);
} }
return ret; return ret;
} }
@ -497,7 +494,7 @@ rba_f_eval_checked (int argc, VALUE *argv, VALUE self)
RUBY_END_EXEC RUBY_END_EXEC
if (error) { if (error) {
rba_check_error (); rba_check_error (error);
} }
return ret; return ret;
} }
@ -513,7 +510,7 @@ rba_yield_checked (VALUE value)
RUBY_END_EXEC RUBY_END_EXEC
if (error) { if (error) {
rba_check_error (); rba_check_error (error);
} }
} }

View File

@ -190,7 +190,7 @@ namespace rba
tl::BacktraceElement rba_split_bt_information (const char *m, size_t l); tl::BacktraceElement rba_split_bt_information (const char *m, size_t l);
void rba_get_backtrace_from_array (VALUE backtrace, std::vector<tl::BacktraceElement> &bt, unsigned int skip); void rba_get_backtrace_from_array (VALUE backtrace, std::vector<tl::BacktraceElement> &bt, unsigned int skip);
void rba_check_error (); void rba_check_error (int state);
VALUE rba_string_value_f (VALUE obj); VALUE rba_string_value_f (VALUE obj);
VALUE rba_safe_string_value (VALUE obj); VALUE rba_safe_string_value (VALUE obj);
VALUE rba_safe_obj_as_string (VALUE obj); VALUE rba_safe_obj_as_string (VALUE obj);
@ -265,7 +265,7 @@ R rba_safe_func (R (*f) (A), A arg)
RUBY_END_EXEC RUBY_END_EXEC
if (error) { if (error) {
rba_check_error (); rba_check_error (error);
} }
return cp.r; return cp.r;
} }

View File

@ -62,10 +62,32 @@ public:
virtual void read (Database &db) virtual void read (Database &db)
{ {
try { try {
// TODO: do not allow waivers here?
// (otherwise, description lines like "WE0 ..." would be read as waivers)
do_read (db); do_read (db);
} catch (tl::Exception &ex) { } catch (tl::Exception &ex) {
error (ex.msg ()); error (ex.msg ());
} }
try {
// try to find a waiver database and apply it if possible
std::string waived_source = m_input_stream.source () + ".waived";
tl::InputStream is_waived (waived_source);
try {
Database db_waived;
RVEReader reader_waived (is_waived);
reader_waived.do_read (db_waived);
db.apply (db_waived);
} catch (tl::Exception &ex) {
warn (ex.msg ());
}
} catch (...) {
// ignore stream errors
}
} }
void do_read (Database &db) void do_read (Database &db)
@ -157,7 +179,16 @@ public:
while (*cp && isspace (*cp)) { while (*cp && isspace (*cp)) {
++cp; ++cp;
} }
waivers.insert (std::make_pair (n, std::string ())).first->second = cp;
auto wi = waivers.insert (std::make_pair (n, std::string ()));
// NOTE: first line is skipped (author, time)
if (! wi.second) {
std::string &s = wi.first->second;
if (! s.empty ()) {
s += "\n";
}
s += cp;
}
} else { } else {
@ -176,7 +207,6 @@ public:
std::map<size_t, std::string>::const_iterator w = waivers.find (shape); std::map<size_t, std::string>::const_iterator w = waivers.find (shape);
bool waived = (w != waivers.end ()); bool waived = (w != waivers.end ());
// TODO: add waiver string somehow ...
if (at_end ()) { if (at_end ()) {
warn (tl::to_string (tr ("Unexpected end of file before the specified number of shapes was read - stopping."))); warn (tl::to_string (tr ("Unexpected end of file before the specified number of shapes was read - stopping.")));
@ -406,6 +436,7 @@ public:
Item *item = db.create_item (cell->id (), cath->id ()); Item *item = db.create_item (cell->id (), cath->id ());
if (waived) { if (waived) {
db.add_item_tag (item, waived_tag_id); db.add_item_tag (item, waived_tag_id);
item->set_comment (w->second);
} }
item->values ().swap (values); item->values ().swap (values);

View File

@ -81,3 +81,13 @@ TEST(3)
{ {
run_rve_test (_this, "rve3.db", "rve3_au_2.txt"); run_rve_test (_this, "rve3.db", "rve3_au_2.txt");
} }
TEST(4)
{
run_rve_test (_this, "rve4.db", "rve4_au.txt");
}
TEST(5)
{
run_rve_test (_this, "rve5.db", "rve5_au.txt");
}

View File

@ -19,9 +19,9 @@ gate = active & poly
name(l1, "l1") name(l1, "l1")
name(l2, "l2") name(l2, "l2")
name(l3, "l3") name(l3, "l3")
name(sd, "sd") name(sd, "sd", 17, 0)
name(poly, "poly") name(poly, "poly")
name(gate, "gate") name(gate, "gate", RBA::LayerInfo::new(18, 0))
name(contact, "contact") name(contact, "contact")
mos_ex = RBA::DeviceExtractorMOS3Transistor::new("MOS") mos_ex = RBA::DeviceExtractorMOS3Transistor::new("MOS")

36
testdata/drc/drcSimpleTests_122.drc vendored Normal file
View File

@ -0,0 +1,36 @@
source $drc_test_source
report_netlist $drc_test_target
deep
l1 = input(1, 0)
l2 = input(2, 0)
l3 = input(3, 0)
active = input(10, 0)
poly = input(11, 0)
contact = input(12, 0)
sd = active - poly
gate = active & poly
name(l1, "l1")
name(l2, "l2")
name(l3, "l3", RBA::LayerInfo::new(3, 0, "l3"))
name(sd, "sd", 17, 0)
name(poly, "poly", 11)
name(gate, "gate")
name(contact, "contact")
mos_ex = RBA::DeviceExtractorMOS3Transistor::new("MOS")
extract_devices(mos_ex, { "SD" => sd, "G" => gate, "P" => poly })
connect(contact, poly)
connect(contact, sd)
connect(l1, contact)
connect(l1, l2)
connect(l2, l3)
netlist

BIN
testdata/drc/drcSimpleTests_122.gds vendored Normal file

Binary file not shown.

113
testdata/drc/drcSimpleTests_au122.l2n vendored Normal file
View File

@ -0,0 +1,113 @@
#%l2n-klayout
W(TOP)
U(0.001)
L(l1 '1/0')
L(l2 '2/0')
L(l3 'l3 (3/0)')
L(poly '11/0')
L(contact '12/0')
L(sd '17/0')
C(l1 l1 l2 contact)
C(l2 l1 l2 l3)
C(l3 l2 l3)
C(poly poly contact)
C(contact l1 poly contact sd)
C(sd contact sd)
K(MOS MOS3)
D(D$MOS MOS
T(S
R(sd (-2000 -2600) (4000 800))
)
T(G
R(poly (-2000 -1800) (4000 3600))
)
T(D
R(sd (-2000 1800) (4000 800))
)
)
X(A
R((-2800 -2500) (5600 5200))
N(1
R(contact (-1900 2000) (600 600))
R(contact (-600 -600) (600 600))
R(contact (2600 -600) (600 600))
R(contact (-600 -600) (600 600))
R(contact (-2200 -600) (600 600))
R(contact (-600 -600) (600 600))
R(sd (-2300 -700) (4000 800))
)
N(2
R(contact (-1900 -2400) (600 600))
R(contact (2600 -600) (600 600))
R(contact (-2200 -600) (600 600))
R(sd (-2300 -700) (4000 800))
)
N(3
R(poly (-2800 -1700) (5600 3600))
)
P(1)
P(2)
P(3)
D(1 D$MOS
Y(0 100)
E(L 3.6)
E(W 4)
E(AS 3.2)
E(AD 3.2)
E(PS 9.6)
E(PD 9.6)
T(S 2)
T(G 3)
T(D 1)
)
)
X(AA
R((-2050 -1950) (5600 5200))
N(1)
N(2)
N(3)
P(1)
P(2)
P(3)
X(1 A O(180) Y(750 750)
P(0 2)
P(1 1)
P(2 3)
)
)
X(TOP
R((-3150 -3500) (8050 12650))
N(1 I(B)
R(l1 (1650 5200) (3250 2800))
R(l1 (-3300 -10700) (800 4000))
R(l2 (1100 5100) (700 700))
R(l2 (-2500 -8200) (700 700))
Q(l3 (-790 -810) (0 1000) (1790 0) (0 7510) (1000 0) (0 -8510))
R(l3 (-500 3410) (0 0))
R(contact (-2100 3300) (600 600))
R(contact (-600 1000) (600 600))
)
N(2 I(A)
R(l1 (-1400 1300) (2600 3000))
R(l1 (-3550 -350) (4000 750))
R(l1 (-1650 -1700) (0 0))
R(contact (-1200 -1600) (600 600))
R(contact (1000 -600) (600 600))
)
N(3
R(l1 (-2350 8300) (4000 850))
)
N(4
R(l1 (-2800 -2800) (800 4100))
)
X(1 AA Y(-1100 5900)
P(0 3)
P(1 2)
P(2 1)
)
X(2 A O(90) Y(-100 -700)
P(0 4)
P(1 1)
P(2 2)
)
)

View File

@ -665,7 +665,7 @@ class Basic_TestClass < TestBase
if !$leak_check if !$leak_check
begin begin
b.b10 { |a| arr.push(a.get_n) } # b10 is a const iterator - cannot call a1 on it b.each_a_be { |a| arr.push(a.get_n) } # b10 is a const iterator - cannot call a1 on it
rescue rescue
err_caught = true err_caught = true
end end
@ -679,7 +679,7 @@ class Basic_TestClass < TestBase
if !$leak_check if !$leak_check
begin begin
b.b10p { |a| arr.push(a.get_n) } # b10p is a const iterator - cannot call a1 on it b.each_a_be_pp { |a| arr.push(a.get_n) } # b10p is a const iterator - cannot call a1 on it
rescue rescue
err_caught = true err_caught = true
end end
@ -689,85 +689,85 @@ class Basic_TestClass < TestBase
end end
arr = [] arr = []
b.b10 { |a| arr.push(a.dup.get_n) } b.each_a_be { |a| arr.push(a.dup.get_n) }
assert_equal(arr, [100, 121, 144]) assert_equal(arr, [100, 121, 144])
arr = [] arr = []
b.dup.b10 { |a| arr.push(a.dup.get_n) } b.dup.each_a_be { |a| arr.push(a.dup.get_n) }
assert_equal(arr, [100, 121, 144]) assert_equal(arr, [100, 121, 144])
arr = [] arr = []
b.b10 { |a| arr.push(a.get_n_const) } b.each_a_be { |a| arr.push(a.get_n_const) }
assert_equal(arr, [100, 121, 144]) assert_equal(arr, [100, 121, 144])
arr = [] arr = []
b.b10p { |a| arr.push(a.dup.get_n) } b.each_a_be_pp { |a| arr.push(a.dup.get_n) }
assert_equal(arr, [100, 121, 144]) assert_equal(arr, [100, 121, 144])
arr = [] arr = []
b.dup.b10p { |a| arr.push(a.dup.get_n) } b.dup.each_a_be_pp { |a| arr.push(a.dup.get_n) }
assert_equal(arr, [100, 121, 144]) assert_equal(arr, [100, 121, 144])
arr = [] arr = []
b.b10p { |a| arr.push(a.get_n_const) } b.each_a_be_pp { |a| arr.push(a.get_n_const) }
assert_equal(arr, [100, 121, 144]) assert_equal(arr, [100, 121, 144])
arr = [] arr = []
b.b11 { |a| arr.push(a.get_n) } b.each_a_be_v { |a| arr.push(a.get_n) }
assert_equal(arr, [100, 121, 144]) assert_equal(arr, [100, 121, 144])
arr = [] arr = []
b.dup.b11 { |a| arr.push(a.get_n) } b.dup.each_a_be_v { |a| arr.push(a.get_n) }
assert_equal(arr, [100, 121, 144]) assert_equal(arr, [100, 121, 144])
arr = [] arr = []
b.b12 { |a| arr.push(a.get_n) } b.each_a_be_p { |a| arr.push(a.get_n) }
assert_equal(arr, [7100, 7121, 7144, 7169]) assert_equal(arr, [7100, 7121, 7144, 7169])
arr = [] arr = []
b.dup.b12 { |a| arr.push(a.get_n) } b.dup.each_a_be_p { |a| arr.push(a.get_n) }
assert_equal(arr, [7100, 7121, 7144, 7169]) assert_equal(arr, [7100, 7121, 7144, 7169])
aarr = b.b16a aarr = b.av
arr = [] arr = []
aarr.each { |a| arr.push(a.get_n) } aarr.each { |a| arr.push(a.get_n) }
assert_equal(arr, [100, 121, 144]) assert_equal(arr, [100, 121, 144])
aarr = b.b16b aarr = b.av_cref
arr = [] arr = []
aarr.each { |a| arr.push(a.get_n) } aarr.each { |a| arr.push(a.get_n) }
assert_equal(arr, [100, 121, 144]) assert_equal(arr, [100, 121, 144])
aarr = b.b16c aarr = b.av_ref
arr = [] arr = []
aarr.each { |a| arr.push(a.get_n) } aarr.each { |a| arr.push(a.get_n) }
assert_equal(arr, [100, 121, 144]) assert_equal(arr, [100, 121, 144])
b.b17a( [ RBA::A.new_a( 101 ), RBA::A.new_a( -122 ) ] ) b.av_cref = [ RBA::A.new_a( 101 ), RBA::A.new_a( -122 ) ]
arr = [] arr = []
b.b11 { |a| arr.push(a.get_n) } b.each_a_be_v { |a| arr.push(a.get_n) }
assert_equal(arr, [101, -122]) assert_equal(arr, [101, -122])
b.b17a( [] ) b.av_cref = []
arr = [] arr = []
b.b11 { |a| arr.push(a.get_n) } b.each_a_be_v { |a| arr.push(a.get_n) }
assert_equal(arr, []) assert_equal(arr, [])
b.b17b( [ RBA::A.new_a( 102 ), RBA::A.new_a( -123 ) ] ) b.av_ref = [ RBA::A.new_a( 102 ), RBA::A.new_a( -123 ) ]
arr = [] arr = []
b.b11 { |a| arr.push(a.get_n) } b.each_a_be_v { |a| arr.push(a.get_n) }
assert_equal(arr, [102, -123]) assert_equal(arr, [102, -123])
b.b17c( [ RBA::A.new_a( 100 ), RBA::A.new_a( 121 ), RBA::A.new_a( 144 ) ] ) b.av = [ RBA::A.new_a( 100 ), RBA::A.new_a( 121 ), RBA::A.new_a( 144 ) ]
arr = [] arr = []
b.b11 { |a| arr.push(a.get_n) } b.each_a_be_v { |a| arr.push(a.get_n) }
assert_equal(arr, [100, 121, 144]) assert_equal(arr, [100, 121, 144])
if !$leak_check if !$leak_check
arr = [] arr = []
begin begin
b.b13 { |a| arr.push(a.get_n) } b.each_a_be_cp { |a| arr.push(a.get_n) }
rescue rescue
err_caught = true err_caught = true
end end
@ -777,11 +777,11 @@ class Basic_TestClass < TestBase
end end
arr = [] arr = []
b.b13 { |a| arr.push(a.get_n_const) } b.each_a_be_cp { |a| arr.push(a.get_n_const) }
assert_equal(arr, [-3100, -3121]) assert_equal(arr, [-3100, -3121])
arr = [] arr = []
b.dup.b13 { |a| arr.push(a.get_n_const) } b.dup.each_a_be_cp { |a| arr.push(a.get_n_const) }
assert_equal(arr, [-3100, -3121]) assert_equal(arr, [-3100, -3121])
arr = [] arr = []
@ -938,6 +938,38 @@ class Basic_TestClass < TestBase
end end
def _iter_find(b, find)
b.each_a_be do |a|
if a.get_n_const == find
return true
end
end
return false
end
# Iterator break, return, continue
def test_13c
b = RBA::B.new
arr = []
b.each_a_be { |a| arr.push(a.get_n_const) }
assert_equal(arr, [100, 121, 144])
arr = []
b.each_a_be do |a|
if a.get_n_const == 121
next
end
arr.push(a.get_n_const)
end
assert_equal(arr, [100, 144])
assert_equal(_iter_find(b, 121), true)
assert_equal(_iter_find(b, 122), false)
end
def test_14 def test_14
a = RBA::A.new a = RBA::A.new