From 08a77e06a8b7e76466a5c177159a5ecdf3cad31b Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Wed, 25 Jul 2012 14:05:06 -0700 Subject: Rewrite task-comm-NN to use pipes --- src/libcore/pipes.rs | 59 ++++++---------------------------------------------- 1 file changed, 6 insertions(+), 53 deletions(-) (limited to 'src/libcore') diff --git a/src/libcore/pipes.rs b/src/libcore/pipes.rs index 27cd8ef7caf..a999d615e31 100644 --- a/src/libcore/pipes.rs +++ b/src/libcore/pipes.rs @@ -30,59 +30,6 @@ macro_rules! move { // places. Once there is unary move, it can be removed. fn move(-x: T) -> T { x } -/** - -Some thoughts about fixed buffers. - -The idea is if a protocol is bounded, we will synthesize a record that -has a field for each state. Each of these states contains a packet for -the messages that are legal to be sent in that state. Then, instead of -allocating, the send code just finds a pointer to the right field and -uses that instead. - -Unforunately, this makes things kind of tricky. We need to be able to -find the buffer, which means we need to pass it around. This could -either be associated with the (send|recv)_packet classes, or with the -packet itself. We will also need some form of reference counting so we -can track who has the responsibility of freeing the buffer. - -We want to preserve the ability to do things like optimistic buffer -re-use, and skipping over to a new buffer when necessary. What I mean -is, suppose we had the typical stream protocol. It'd make sense to -amortize allocation costs by allocating a buffer with say 16 -messages. When the sender gets to the end of the buffer, it could -check if the receiver is done with the packet in slot 0. If so, it can -just reuse that one, checking if the receiver is done with the next -one in each case. If it is ever not done, it just allocates a new -buffer and skips over to that. - -Also, since protocols are in libcore, we have to do this in a way that -maintains backwards compatibility. - -buffer header and buffer. Cast as c_void when necessary. - -=== - -Okay, here are some new ideas. - -It'd be nice to keep the bounded/unbounded case as uniform as -possible. It leads to less code duplication, and less things that can -go sublty wrong. For the bounded case, we could either have a struct -with a bunch of unique pointers to pre-allocated packets, or we could -lay them out inline. Inline layout is better, if for no other reason -than that we don't have to allocate each packet -individually. Currently we pass unique packets around as unsafe -pointers, but they are actually unique pointers. We should instead use -real unsafe pointers. This makes freeing data and running destructors -trickier though. Thus, we should allocate all packets in parter of a -higher level buffer structure. Packets can maintain a pointer to their -buffer, and this is the part that gets freed. - -It might be helpful to have some idea of a semi-unique pointer (like -being partially pregnant, also like an ARC). - -*/ - enum state { empty, full, @@ -805,6 +752,12 @@ class port_set : recv { vec::push(self.ports, port) } + fn chan() -> chan { + let (ch, po) = stream(); + self.add(po); + ch + } + fn try_recv() -> option { let mut result = none; while result == none && self.ports.len() > 0 { -- cgit 1.4.1-3-g733a5 From 531ea695f64e8d7105f904c515a6ff84fa32dc77 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Wed, 25 Jul 2012 14:33:18 -0700 Subject: Remove shared_arc (unused) and fix trivial-message --- src/libcore/arc.rs | 65 +----------------------------------- src/test/run-pass/trivial-message.rs | 2 +- 2 files changed, 2 insertions(+), 65 deletions(-) (limited to 'src/libcore') diff --git a/src/libcore/arc.rs b/src/libcore/arc.rs index 28f07264410..9c46df9731b 100644 --- a/src/libcore/arc.rs +++ b/src/libcore/arc.rs @@ -3,10 +3,9 @@ * share immutable data between tasks. */ -import comm::{port, chan, methods}; import sys::methods; -export arc, get, clone, shared_arc, get_arc; +export arc, get, clone; export exclusive, methods; @@ -122,49 +121,6 @@ impl methods for exclusive { } } -// Convenience code for sharing arcs between tasks - -type get_chan = chan>>; - -// (terminate, get) -type shared_arc = (shared_arc_res, get_chan); - -class shared_arc_res { - let c: comm::chan<()>; - new(c: comm::chan<()>) { self.c = c; } - drop { self.c.send(()); } -} - -fn shared_arc(-data: T) -> shared_arc { - let a = arc::arc(data); - let p = port(); - let c = chan(p); - do task::spawn() |move a| { - let mut live = true; - let terminate = port(); - let get = port(); - - c.send((chan(terminate), chan(get))); - - while live { - alt comm::select2(terminate, get) { - either::left(()) { live = false; } - either::right(cc) { - comm::send(cc, arc::clone(&a)); - } - } - } - }; - let (terminate, get) = p.recv(); - (shared_arc_res(terminate), get) -} - -fn get_arc(c: get_chan) -> arc::arc { - let p = port(); - c.send(chan(p)); - p.recv() -} - #[cfg(test)] mod tests { import comm::*; @@ -196,25 +152,6 @@ mod tests { log(info, arc_v); } - #[test] - fn auto_share_arc() { - let v = ~[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - let (_res, arc_c) = shared_arc(v); - - let p = port(); - let c = chan(p); - - do task::spawn() { - let arc_v = get_arc(arc_c); - let v = *get(&arc_v); - assert v[2] == 3; - - c.send(()); - }; - - assert p.recv() == (); - } - #[test] #[ignore] // this can probably infinite loop too. fn exclusive_arc() { diff --git a/src/test/run-pass/trivial-message.rs b/src/test/run-pass/trivial-message.rs index 8e92e8f2020..ab3efd6b22e 100644 --- a/src/test/run-pass/trivial-message.rs +++ b/src/test/run-pass/trivial-message.rs @@ -1,4 +1,4 @@ -import pipes::{port, chan} +import pipes::{port, chan}; /* This is about the simplest program that can successfully send a -- cgit 1.4.1-3-g733a5 From 62d4f8fe825c907bd03c275f85aeeaf7b25c4336 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Wed, 25 Jul 2012 14:46:15 -0700 Subject: Added a select2 trait. Fixes #2898 --- src/libcore/pipes.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'src/libcore') diff --git a/src/libcore/pipes.rs b/src/libcore/pipes.rs index a999d615e31..4ffa040250f 100644 --- a/src/libcore/pipes.rs +++ b/src/libcore/pipes.rs @@ -822,3 +822,52 @@ impl chan of channel for shared_chan { fn shared_chan(+c: chan) -> shared_chan { arc::exclusive(c) } + +trait select2 { + fn try_select() -> either, option>; + fn select() -> either; +} + +impl, Right: selectable recv> + of select2 for (Left, Right) { + + fn select() -> either { + alt self { + (lp, rp) { + alt select2i(lp, rp) { + left(()) { left (lp.recv()) } + right(()) { right(rp.recv()) } + } + } + } + } + + fn try_select() -> either, option> { + alt self { + (lp, rp) { + alt select2i(lp, rp) { + left(()) { left (lp.try_recv()) } + right(()) { right(rp.try_recv()) } + } + } + } + } +} + +#[cfg(test)] +mod test { + #[test] + fn test_select2() { + let (c1, p1) = pipes::stream(); + let (c2, p2) = pipes::stream(); + + c1.send("abc"); + + alt (p1, p2).select() { + right(_) { fail } + _ { } + } + + c2.send(123); + } +} -- cgit 1.4.1-3-g733a5 From f8dc9283ad13f990d1ee5ac814eac49189edcd59 Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Wed, 25 Jul 2012 14:58:48 -0700 Subject: Reject non-UTF-8 files when reading as str. Close #2918. --- src/libcore/io.rs | 6 +++++- src/test/compile-fail/not-utf8.bin | Bin 0 -> 3036 bytes src/test/compile-fail/not-utf8.rs | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 src/test/compile-fail/not-utf8.bin create mode 100644 src/test/compile-fail/not-utf8.rs (limited to 'src/libcore') diff --git a/src/libcore/io.rs b/src/libcore/io.rs index 25d1b5e6680..3704f2b70da 100644 --- a/src/libcore/io.rs +++ b/src/libcore/io.rs @@ -687,7 +687,11 @@ fn seek_in_buf(offset: int, pos: uint, len: uint, whence: seek_style) -> fn read_whole_file_str(file: ~str) -> result<~str, ~str> { result::chain(read_whole_file(file), |bytes| { - result::ok(str::from_bytes(bytes)) + if str::is_utf8(bytes) { + result::ok(str::from_bytes(bytes)) + } else { + result::err(file + ~" is not UTF-8") + } }) } diff --git a/src/test/compile-fail/not-utf8.bin b/src/test/compile-fail/not-utf8.bin new file mode 100644 index 00000000000..4148e5b88fe Binary files /dev/null and b/src/test/compile-fail/not-utf8.bin differ diff --git a/src/test/compile-fail/not-utf8.rs b/src/test/compile-fail/not-utf8.rs new file mode 100644 index 00000000000..2038f139359 --- /dev/null +++ b/src/test/compile-fail/not-utf8.rs @@ -0,0 +1,5 @@ +// error-pattern: is not UTF-8 + +fn foo() { + #include("not-utf8.bin") +} -- cgit 1.4.1-3-g733a5 From 3aee39a6ec910bde6ae9a5423b11aaaad4a1b089 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Wed, 25 Jul 2012 17:29:34 -0700 Subject: Add #[inline(never)], and also fixed inlining on vec::push --- src/libcore/dvec.rs | 5 +++++ src/libcore/vec.rs | 20 ++++++++++++++------ src/libstd/smallintmap.rs | 4 +++- src/libsyntax/attr.rs | 6 +++++- src/rustc/metadata/encoder.rs | 4 ++-- src/rustc/middle/trans/base.rs | 1 + 6 files changed, 30 insertions(+), 10 deletions(-) (limited to 'src/libcore') diff --git a/src/libcore/dvec.rs b/src/libcore/dvec.rs index bdbdb6cef16..60cb1b51be6 100644 --- a/src/libcore/dvec.rs +++ b/src/libcore/dvec.rs @@ -108,6 +108,11 @@ impl private_methods for dvec { // almost nothing works without the copy bound due to limitations // around closures. impl extensions for dvec { + /// Reserves space for N elements + fn reserve(count: uint) { + vec::reserve(self.data, count) + } + /** * Swaps out the current vector and hands it off to a user-provided * function `f`. The function should transform it however is desired diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs index 5aa2f35029a..c07dd9b8224 100644 --- a/src/libcore/vec.rs +++ b/src/libcore/vec.rs @@ -509,10 +509,7 @@ fn push(&v: ~[const T], +initval: T) { let repr: **unsafe::vec_repr = ::unsafe::reinterpret_cast(addr_of(v)); let fill = (**repr).fill; if (**repr).alloc > fill { - (**repr).fill += sys::size_of::(); - let p = ptr::addr_of((**repr).data); - let p = ptr::offset(p, fill) as *mut T; - rusti::move_val_init(*p, initval); + push_fast(v, initval); } else { push_slow(v, initval); @@ -520,9 +517,21 @@ fn push(&v: ~[const T], +initval: T) { } } +// This doesn't bother to make sure we have space. +#[inline(always)] // really pretty please +unsafe fn push_fast(&v: ~[const T], +initval: T) { + let repr: **unsafe::vec_repr = ::unsafe::reinterpret_cast(addr_of(v)); + let fill = (**repr).fill; + (**repr).fill += sys::size_of::(); + let p = ptr::addr_of((**repr).data); + let p = ptr::offset(p, fill) as *mut T; + rusti::move_val_init(*p, initval); +} + +#[inline(never)] fn push_slow(&v: ~[const T], +initval: T) { reserve_at_least(v, v.len() + 1u); - push(v, initval); + unsafe { push_fast(v, initval) } } // Unchecked vector indexing @@ -644,7 +653,6 @@ fn grow_fn(&v: ~[const T], n: uint, op: init_op) { * of the vector, expands the vector by replicating `initval` to fill the * intervening space. */ -#[inline(always)] fn grow_set(&v: ~[mut T], index: uint, initval: T, val: T) { if index >= len(v) { grow(v, index - len(v) + 1u, initval); } v[index] = val; diff --git a/src/libstd/smallintmap.rs b/src/libstd/smallintmap.rs index 112a55ab67d..09f14f4f63e 100644 --- a/src/libstd/smallintmap.rs +++ b/src/libstd/smallintmap.rs @@ -17,7 +17,8 @@ enum smallintmap { /// Create a smallintmap fn mk() -> smallintmap { - ret smallintmap_(@{v: dvec()}); + let v = dvec(); + ret smallintmap_(@{v: v}); } /** @@ -26,6 +27,7 @@ fn mk() -> smallintmap { */ #[inline(always)] fn insert(self: smallintmap, key: uint, val: T) { + //#error("inserting key %?", key); self.v.grow_set_elt(key, none, some(val)); } diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index 859bc70bfd6..4587a5b8b29 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -364,7 +364,8 @@ fn foreign_abi(attrs: ~[ast::attribute]) -> either<~str, ast::foreign_abi> { enum inline_attr { ia_none, ia_hint, - ia_always + ia_always, + ia_never, } /// True if something like #[inline] is found in the list of attrs. @@ -376,6 +377,9 @@ fn find_inline_attr(attrs: ~[ast::attribute]) -> inline_attr { ast::meta_list(@~"inline", items) { if !vec::is_empty(find_meta_items_by_name(items, ~"always")) { ia_always + } else if !vec::is_empty( + find_meta_items_by_name(items, ~"never")) { + ia_never } else { ia_hint } diff --git a/src/rustc/metadata/encoder.rs b/src/rustc/metadata/encoder.rs index 69e97f7e9db..b2847b3acc5 100644 --- a/src/rustc/metadata/encoder.rs +++ b/src/rustc/metadata/encoder.rs @@ -545,8 +545,8 @@ fn purity_fn_family(p: purity) -> char { fn should_inline(attrs: ~[attribute]) -> bool { alt attr::find_inline_attr(attrs) { - attr::ia_none { false } - attr::ia_hint | attr::ia_always { true } + attr::ia_none | attr::ia_never { false } + attr::ia_hint | attr::ia_always { true } } } diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index 209bb6ee7c2..94a4a30021d 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -456,6 +456,7 @@ fn set_inline_hint_if_appr(attrs: ~[ast::attribute], alt attr::find_inline_attr(attrs) { attr::ia_hint { set_inline_hint(llfn); } attr::ia_always { set_always_inline(llfn); } + attr::ia_never { set_no_inline(llfn); } attr::ia_none { /* fallthrough */ } } } -- cgit 1.4.1-3-g733a5 From da80bd17c30db599de43355f07783ee0bf846162 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 25 Jul 2012 18:36:18 -0700 Subject: rustc: Introduce a lang_items pass, part of coherence and operator overloading. This will also help us remove kinds. --- src/libcore/core.rc | 1 + src/libcore/core.rs | 5 + src/rustc/driver/driver.rs | 3 + src/rustc/metadata/csearch.rs | 9 ++ src/rustc/metadata/decoder.rs | 13 +++ src/rustc/metadata/encoder.rs | 1 + src/rustc/middle/lang_items.rs | 209 +++++++++++++++++++++++++++++++++++++++++ src/rustc/rustc.rc | 1 + 8 files changed, 242 insertions(+) create mode 100644 src/rustc/middle/lang_items.rs (limited to 'src/libcore') diff --git a/src/libcore/core.rc b/src/libcore/core.rc index d298ff15a9e..11e305e22e8 100644 --- a/src/libcore/core.rc +++ b/src/libcore/core.rc @@ -161,6 +161,7 @@ mod tuple; // Ubiquitous-utility-type modules +mod ops; mod cmp; mod num; mod hash; diff --git a/src/libcore/core.rs b/src/libcore/core.rs index 4738574fdb4..fdf53524188 100644 --- a/src/libcore/core.rs +++ b/src/libcore/core.rs @@ -30,6 +30,8 @@ import float::num; import f32::num; import f64::num; import num::num; +import ops::{const, copy, send, owned}; +import ops::{add, sub, mul, div, modulo, neg, bitops, index}; export path, option, some, none, unreachable; export extensions; @@ -42,6 +44,9 @@ export immutable_copyable_vector, iter_trait_extensions, vec_concat; export base_iter, copyable_iter, extended_iter; export tuple_ops, extended_tuple_ops; export ptr; +// The following exports are the core operators and kinds +export const, copy, send, owned; +export add, sub, mul, div, modulo, neg, bitops, index; // Export the log levels as global constants. Higher levels mean // more-verbosity. Error is the bottom level, default logging level is diff --git a/src/rustc/driver/driver.rs b/src/rustc/driver/driver.rs index 369eccc6bed..a6734b285a1 100644 --- a/src/rustc/driver/driver.rs +++ b/src/rustc/driver/driver.rs @@ -170,6 +170,9 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg, session::sess_os_to_meta_os(sess.targ_cfg.os), sess.opts.static)); + time(time_passes, ~"language item collection", || + middle::lang_items::collect_language_items(crate, sess)); + let { def_map: def_map, exp_map: exp_map, impl_map: impl_map, diff --git a/src/rustc/metadata/csearch.rs b/src/rustc/metadata/csearch.rs index 7f97583fe3d..0ba76c49246 100644 --- a/src/rustc/metadata/csearch.rs +++ b/src/rustc/metadata/csearch.rs @@ -25,6 +25,7 @@ export get_enum_variants; export get_impls_for_mod; export get_trait_methods; export get_method_names_if_trait; +export get_item_attrs; export each_path; export get_type; export get_impl_traits; @@ -149,6 +150,14 @@ fn get_method_names_if_trait(cstore: cstore::cstore, def: ast::def_id) ret decoder::get_method_names_if_trait(cdata, def.node); } +fn get_item_attrs(cstore: cstore::cstore, + def_id: ast::def_id, + f: fn(~[@ast::meta_item])) { + + let cdata = cstore::get_crate_data(cstore, def_id.crate); + decoder::get_item_attrs(cdata, def_id.node, f) +} + fn get_class_fields(tcx: ty::ctxt, def: ast::def_id) -> ~[ty::field_ty] { let cstore = tcx.cstore; let cdata = cstore::get_crate_data(cstore, def.crate); diff --git a/src/rustc/metadata/decoder.rs b/src/rustc/metadata/decoder.rs index 3ff9ded3f1e..a5261d039c1 100644 --- a/src/rustc/metadata/decoder.rs +++ b/src/rustc/metadata/decoder.rs @@ -39,6 +39,7 @@ export get_crate_vers; export get_impls_for_mod; export get_trait_methods; export get_method_names_if_trait; +export get_item_attrs; export get_crate_module_paths; export def_like; export dl_def; @@ -659,6 +660,18 @@ fn get_method_names_if_trait(cdata: cmd, node_id: ast::node_id) ret some(resulting_method_names); } +fn get_item_attrs(cdata: cmd, + node_id: ast::node_id, + f: fn(~[@ast::meta_item])) { + + let item = lookup_item(node_id, cdata.data); + do ebml::tagged_docs(item, tag_attributes) |attributes| { + do ebml::tagged_docs(attributes, tag_attribute) |attribute| { + f(get_meta_items(attribute)); + } + } +} + // Helper function that gets either fields or methods fn get_class_members(cdata: cmd, id: ast::node_id, p: fn(char) -> bool) -> ~[ty::field_ty] { diff --git a/src/rustc/metadata/encoder.rs b/src/rustc/metadata/encoder.rs index b2847b3acc5..b4818959344 100644 --- a/src/rustc/metadata/encoder.rs +++ b/src/rustc/metadata/encoder.rs @@ -759,6 +759,7 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item, encode_type_param_bounds(ebml_w, ecx, tps); encode_type(ecx, ebml_w, node_id_to_type(tcx, item.id)); encode_name(ebml_w, item.ident); + encode_attributes(ebml_w, item.attrs); let mut i = 0u; for vec::each(*ty::trait_methods(tcx, local_def(item.id))) |mty| { alt ms[i] { diff --git a/src/rustc/middle/lang_items.rs b/src/rustc/middle/lang_items.rs new file mode 100644 index 00000000000..2353da51f98 --- /dev/null +++ b/src/rustc/middle/lang_items.rs @@ -0,0 +1,209 @@ +// Detecting language items. +// +// Language items are items that represent concepts intrinsic to the language +// itself. Examples are: +// +// * Traits that specify "kinds"; e.g. "const", "copy", "send". +// +// * Traits that represent operators; e.g. "add", "sub", "index". +// +// * Functions called by the compiler itself. + +import driver::session::session; +import metadata::csearch::{each_path, get_item_attrs}; +import metadata::cstore::{iter_crate_data}; +import metadata::decoder::{dl_def, dl_field, dl_impl}; +import syntax::ast::{crate, def_id, def_ty, lit_str, meta_item, meta_list}; +import syntax::ast::{meta_name_value, meta_word}; +import syntax::ast_util::{local_def}; +import syntax::visit::{default_simple_visitor, mk_simple_visitor}; +import syntax::visit::{visit_crate, visit_item}; + +import std::map::{hashmap, str_hash}; +import str_eq = str::eq; + +class LanguageItems { + let mut const_trait: option; + let mut copy_trait: option; + let mut send_trait: option; + let mut owned_trait: option; + + let mut add_trait: option; + let mut sub_trait: option; + let mut mul_trait: option; + let mut div_trait: option; + let mut modulo_trait: option; + let mut neg_trait: option; + let mut bitops_trait: option; + let mut index_trait: option; + + new() { + self.const_trait = none; + self.copy_trait = none; + self.send_trait = none; + self.owned_trait = none; + + self.add_trait = none; + self.sub_trait = none; + self.mul_trait = none; + self.div_trait = none; + self.modulo_trait = none; + self.neg_trait = none; + self.bitops_trait = none; + self.index_trait = none; + } +} + +class LanguageItemCollector { + let items: LanguageItems; + + let crate: @crate; + let session: session; + + let item_refs: hashmap<~str,&mut option>; + + new(crate: @crate, session: session) { + self.crate = crate; + self.session = session; + + self.items = LanguageItems(); + + self.item_refs = str_hash(); + } + + // XXX: Needed to work around an issue with constructors. + fn init() { + self.item_refs.insert(~"const", &mut self.items.const_trait); + self.item_refs.insert(~"copy", &mut self.items.copy_trait); + self.item_refs.insert(~"send", &mut self.items.send_trait); + self.item_refs.insert(~"owned", &mut self.items.owned_trait); + + self.item_refs.insert(~"add", &mut self.items.add_trait); + self.item_refs.insert(~"sub", &mut self.items.sub_trait); + self.item_refs.insert(~"mul", &mut self.items.mul_trait); + self.item_refs.insert(~"div", &mut self.items.div_trait); + self.item_refs.insert(~"modulo", &mut self.items.modulo_trait); + self.item_refs.insert(~"neg", &mut self.items.neg_trait); + self.item_refs.insert(~"bitops", &mut self.items.bitops_trait); + self.item_refs.insert(~"index", &mut self.items.index_trait); + } + + fn match_and_collect_meta_item(item_def_id: def_id, + meta_item: meta_item) { + + alt meta_item.node { + meta_name_value(key, literal) => { + alt literal.node { + lit_str(value) => { + self.match_and_collect_item(item_def_id, + *key, + *value); + } + _ => { + // Skip. + } + } + } + meta_word(*) | meta_list(*) { + // Skip. + } + } + } + + fn match_and_collect_item(item_def_id: def_id, key: ~str, value: ~str) { + if !str_eq(key, ~"lang") { + ret; // Didn't match. + } + + alt self.item_refs.find(value) { + none => { + // Didn't match. + } + some(item_ref) => { + // Check for duplicates. + alt copy *item_ref { + some(original_def_id) + if original_def_id != item_def_id => { + + self.session.warn(#fmt("duplicate entry for `%s`", + value)); + } + some(_) | none => { + // OK. + } + } + + // Matched. + *item_ref = some(item_def_id); + } + } + } + + fn collect_local_language_items() { + let this = unsafe { ptr::addr_of(self) }; + visit_crate(*self.crate, (), mk_simple_visitor(@{ + visit_item: |item| { + for item.attrs.each |attribute| { + unsafe { + (*this).match_and_collect_meta_item(local_def(item + .id), + attribute.node + .value); + } + } + } + with *default_simple_visitor() + })); + } + + fn collect_external_language_items() { + let crate_store = self.session.cstore; + do iter_crate_data(crate_store) |crate_number, _crate_metadata| { + for each_path(crate_store, crate_number) |path_entry| { + let def_id; + alt path_entry.def_like { + dl_def(def_ty(did)) => { + def_id = did; + } + dl_def(_) | dl_impl(_) | dl_field { + // Skip this. + again; + } + } + + do get_item_attrs(crate_store, def_id) |meta_items| { + for meta_items.each |meta_item| { + self.match_and_collect_meta_item(def_id, *meta_item); + } + } + } + } + } + + fn check_completeness() { + for self.item_refs.each |key, item_ref| { + alt copy *item_ref { + none => { + self.session.warn(#fmt("no item found for `%s`", key)); + } + some(did) => { + // OK. + } + } + } + } + + fn collect() { + self.init(); + self.collect_local_language_items(); + self.collect_external_language_items(); + self.check_completeness(); + } +} + +fn collect_language_items(crate: @crate, session: session) -> LanguageItems { + let collector = LanguageItemCollector(crate, session); + collector.collect(); + copy collector.items +} + diff --git a/src/rustc/rustc.rc b/src/rustc/rustc.rc index 406c46cc6c6..b2cce508e57 100644 --- a/src/rustc/rustc.rc +++ b/src/rustc/rustc.rc @@ -88,6 +88,7 @@ mod middle { mod region; mod const_eval; mod astencode; + mod lang_items; } mod front { -- cgit 1.4.1-3-g733a5 From 10d8a68791ff2103a84c02783db4e3fd28f2cd87 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 25 Jul 2012 19:03:55 -0700 Subject: libcore: Add missing ops.rs --- src/libcore/ops.rs | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 src/libcore/ops.rs (limited to 'src/libcore') diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs new file mode 100644 index 00000000000..61efe704974 --- /dev/null +++ b/src/libcore/ops.rs @@ -0,0 +1,66 @@ +// Core operators and kinds. + +#[lang="const"] +trait const { + // Empty. +} + +#[lang="copy"] +trait copy { + // Empty. +} + +#[lang="send"] +trait send { + // Empty. +} + +#[lang="owned"] +trait owned { + // Empty. +} + +#[lang="add"] +trait add { + pure fn add(rhs: RHS) -> Result; +} + +#[lang="sub"] +trait sub { + pure fn sub(rhs: RHS) -> Result; +} + +#[lang="mul"] +trait mul { + pure fn mul(rhs: RHS) -> Result; +} + +#[lang="div"] +trait div { + pure fn div(rhs: RHS) -> Result; +} + +#[lang="modulo"] +trait modulo { + pure fn modulo(rhs: RHS) -> Result; +} + +#[lang="neg"] +trait neg { + pure fn neg(rhs: RHS) -> Result; +} + +#[lang="bitops"] +trait bitops { + pure fn and(rhs: RHS) -> Result; + pure fn or(rhs: RHS) -> Result; + pure fn xor(rhs: RHS) -> Result; + pure fn shl(n: BitCount) -> Result; + pure fn shr(n: BitCount) -> Result; +} + +#[lang="index"] +trait index { + pure fn index(index: Index) -> Result; +} + -- cgit 1.4.1-3-g733a5 From 1dd8acd56acc189db2c0bb3266536d35646380b4 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 26 Jul 2012 14:42:44 -0700 Subject: core: Mark a bunch of numeric functions as pure --- src/libcore/cmp.rs | 8 ++++---- src/libcore/f32.rs | 18 ++++++++--------- src/libcore/f64.rs | 18 ++++++++--------- src/libcore/float.rs | 48 ++++++++++++++++++++++---------------------- src/libcore/int-template.rs | 22 ++++++++++---------- src/libcore/num.rs | 18 ++++++++--------- src/libcore/uint-template.rs | 22 ++++++++++---------- src/libstd/cmp.rs | 10 ++++----- 8 files changed, 82 insertions(+), 82 deletions(-) (limited to 'src/libcore') diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index 1bdf3b9909a..d10b9603af0 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -1,10 +1,10 @@ /// Interfaces used for comparison. -iface ord { - fn lt(&&other: self) -> bool; +trait ord { + pure fn lt(&&other: self) -> bool; } -iface eq { - fn eq(&&other: self) -> bool; +trait eq { + pure fn eq(&&other: self) -> bool; } diff --git a/src/libcore/f32.rs b/src/libcore/f32.rs index c72aa6e3aef..3e7bc0097f7 100644 --- a/src/libcore/f32.rs +++ b/src/libcore/f32.rs @@ -168,15 +168,15 @@ pure fn log2(n: f32) -> f32 { } impl num of num::num for f32 { - fn add(&&other: f32) -> f32 { ret self + other; } - fn sub(&&other: f32) -> f32 { ret self - other; } - fn mul(&&other: f32) -> f32 { ret self * other; } - fn div(&&other: f32) -> f32 { ret self / other; } - fn modulo(&&other: f32) -> f32 { ret self % other; } - fn neg() -> f32 { ret -self; } - - fn to_int() -> int { ret self as int; } - fn from_int(n: int) -> f32 { ret n as f32; } + pure fn add(&&other: f32) -> f32 { ret self + other; } + pure fn sub(&&other: f32) -> f32 { ret self - other; } + pure fn mul(&&other: f32) -> f32 { ret self * other; } + pure fn div(&&other: f32) -> f32 { ret self / other; } + pure fn modulo(&&other: f32) -> f32 { ret self % other; } + pure fn neg() -> f32 { ret -self; } + + pure fn to_int() -> int { ret self as int; } + pure fn from_int(n: int) -> f32 { ret n as f32; } } // diff --git a/src/libcore/f64.rs b/src/libcore/f64.rs index 40488d9f8f4..9e84c432bad 100644 --- a/src/libcore/f64.rs +++ b/src/libcore/f64.rs @@ -195,15 +195,15 @@ pure fn log2(n: f64) -> f64 { } impl num of num::num for f64 { - fn add(&&other: f64) -> f64 { ret self + other; } - fn sub(&&other: f64) -> f64 { ret self - other; } - fn mul(&&other: f64) -> f64 { ret self * other; } - fn div(&&other: f64) -> f64 { ret self / other; } - fn modulo(&&other: f64) -> f64 { ret self % other; } - fn neg() -> f64 { ret -self; } - - fn to_int() -> int { ret self as int; } - fn from_int(n: int) -> f64 { ret n as f64; } + pure fn add(&&other: f64) -> f64 { ret self + other; } + pure fn sub(&&other: f64) -> f64 { ret self - other; } + pure fn mul(&&other: f64) -> f64 { ret self * other; } + pure fn div(&&other: f64) -> f64 { ret self / other; } + pure fn modulo(&&other: f64) -> f64 { ret self % other; } + pure fn neg() -> f64 { ret -self; } + + pure fn to_int() -> int { ret self as int; } + pure fn from_int(n: int) -> f64 { ret n as f64; } } // diff --git a/src/libcore/float.rs b/src/libcore/float.rs index 0139c60873b..8b8cc4664dc 100644 --- a/src/libcore/float.rs +++ b/src/libcore/float.rs @@ -403,32 +403,32 @@ fn pow_with_uint(base: uint, pow: uint) -> float { ret total; } -fn is_positive(x: float) -> bool { f64::is_positive(x as f64) } -fn is_negative(x: float) -> bool { f64::is_negative(x as f64) } -fn is_nonpositive(x: float) -> bool { f64::is_nonpositive(x as f64) } -fn is_nonnegative(x: float) -> bool { f64::is_nonnegative(x as f64) } -fn is_zero(x: float) -> bool { f64::is_zero(x as f64) } -fn is_infinite(x: float) -> bool { f64::is_infinite(x as f64) } -fn is_finite(x: float) -> bool { f64::is_finite(x as f64) } -fn is_NaN(x: float) -> bool { f64::is_NaN(x as f64) } - -fn abs(x: float) -> float { f64::abs(x as f64) as float } -fn sqrt(x: float) -> float { f64::sqrt(x as f64) as float } -fn atan(x: float) -> float { f64::atan(x as f64) as float } -fn sin(x: float) -> float { f64::sin(x as f64) as float } -fn cos(x: float) -> float { f64::cos(x as f64) as float } -fn tan(x: float) -> float { f64::tan(x as f64) as float } +pure fn is_positive(x: float) -> bool { f64::is_positive(x as f64) } +pure fn is_negative(x: float) -> bool { f64::is_negative(x as f64) } +pure fn is_nonpositive(x: float) -> bool { f64::is_nonpositive(x as f64) } +pure fn is_nonnegative(x: float) -> bool { f64::is_nonnegative(x as f64) } +pure fn is_zero(x: float) -> bool { f64::is_zero(x as f64) } +pure fn is_infinite(x: float) -> bool { f64::is_infinite(x as f64) } +pure fn is_finite(x: float) -> bool { f64::is_finite(x as f64) } +pure fn is_NaN(x: float) -> bool { f64::is_NaN(x as f64) } + +pure fn abs(x: float) -> float { f64::abs(x as f64) as float } +pure fn sqrt(x: float) -> float { f64::sqrt(x as f64) as float } +pure fn atan(x: float) -> float { f64::atan(x as f64) as float } +pure fn sin(x: float) -> float { f64::sin(x as f64) as float } +pure fn cos(x: float) -> float { f64::cos(x as f64) as float } +pure fn tan(x: float) -> float { f64::tan(x as f64) as float } impl num of num::num for float { - fn add(&&other: float) -> float { ret self + other; } - fn sub(&&other: float) -> float { ret self - other; } - fn mul(&&other: float) -> float { ret self * other; } - fn div(&&other: float) -> float { ret self / other; } - fn modulo(&&other: float) -> float { ret self % other; } - fn neg() -> float { ret -self; } - - fn to_int() -> int { ret self as int; } - fn from_int(n: int) -> float { ret n as float; } + pure fn add(&&other: float) -> float { ret self + other; } + pure fn sub(&&other: float) -> float { ret self - other; } + pure fn mul(&&other: float) -> float { ret self * other; } + pure fn div(&&other: float) -> float { ret self / other; } + pure fn modulo(&&other: float) -> float { ret self % other; } + pure fn neg() -> float { ret -self; } + + pure fn to_int() -> int { ret self as int; } + pure fn from_int(n: int) -> float { ret n as float; } } #[test] diff --git a/src/libcore/int-template.rs b/src/libcore/int-template.rs index 02ce125e35c..2b950e4a797 100644 --- a/src/libcore/int-template.rs +++ b/src/libcore/int-template.rs @@ -112,27 +112,27 @@ fn to_str_bytes(n: T, radix: uint, f: fn(v: &[u8]) -> U) -> U { fn str(i: T) -> ~str { ret to_str(i, 10u); } impl ord of ord for T { - fn lt(&&other: T) -> bool { + pure fn lt(&&other: T) -> bool { ret self < other; } } impl eq of eq for T { - fn eq(&&other: T) -> bool { + pure fn eq(&&other: T) -> bool { ret self == other; } } impl num of num::num for T { - fn add(&&other: T) -> T { ret self + other; } - fn sub(&&other: T) -> T { ret self - other; } - fn mul(&&other: T) -> T { ret self * other; } - fn div(&&other: T) -> T { ret self / other; } - fn modulo(&&other: T) -> T { ret self % other; } - fn neg() -> T { ret -self; } - - fn to_int() -> int { ret self as int; } - fn from_int(n: int) -> T { ret n as T; } + pure fn add(&&other: T) -> T { ret self + other; } + pure fn sub(&&other: T) -> T { ret self - other; } + pure fn mul(&&other: T) -> T { ret self * other; } + pure fn div(&&other: T) -> T { ret self / other; } + pure fn modulo(&&other: T) -> T { ret self % other; } + pure fn neg() -> T { ret -self; } + + pure fn to_int() -> int { ret self as int; } + pure fn from_int(n: int) -> T { ret n as T; } } impl times of iter::times for T { diff --git a/src/libcore/num.rs b/src/libcore/num.rs index 130b259c1df..03868527655 100644 --- a/src/libcore/num.rs +++ b/src/libcore/num.rs @@ -1,17 +1,17 @@ /// An interface for numbers. -iface num { +trait num { // FIXME: Cross-crate overloading doesn't work yet. (#2615) // FIXME: Interface inheritance. (#2616) - fn add(&&other: self) -> self; - fn sub(&&other: self) -> self; - fn mul(&&other: self) -> self; - fn div(&&other: self) -> self; - fn modulo(&&other: self) -> self; - fn neg() -> self; + pure fn add(&&other: self) -> self; + pure fn sub(&&other: self) -> self; + pure fn mul(&&other: self) -> self; + pure fn div(&&other: self) -> self; + pure fn modulo(&&other: self) -> self; + pure fn neg() -> self; - fn to_int() -> int; - fn from_int(n: int) -> self; // FIXME (#2376) Static functions. + pure fn to_int() -> int; + pure fn from_int(n: int) -> self; // FIXME (#2376) Static functions. // n.b. #2376 is for classes, not ifaces, but it could be generalized... } diff --git a/src/libcore/uint-template.rs b/src/libcore/uint-template.rs index 75e17cd6a9b..9561ed4e65f 100644 --- a/src/libcore/uint-template.rs +++ b/src/libcore/uint-template.rs @@ -53,27 +53,27 @@ pure fn compl(i: T) -> T { } impl ord of ord for T { - fn lt(&&other: T) -> bool { + pure fn lt(&&other: T) -> bool { ret self < other; } } impl eq of eq for T { - fn eq(&&other: T) -> bool { + pure fn eq(&&other: T) -> bool { ret self == other; } } impl num of num::num for T { - fn add(&&other: T) -> T { ret self + other; } - fn sub(&&other: T) -> T { ret self - other; } - fn mul(&&other: T) -> T { ret self * other; } - fn div(&&other: T) -> T { ret self / other; } - fn modulo(&&other: T) -> T { ret self % other; } - fn neg() -> T { ret -self; } - - fn to_int() -> int { ret self as int; } - fn from_int(n: int) -> T { ret n as T; } + pure fn add(&&other: T) -> T { ret self + other; } + pure fn sub(&&other: T) -> T { ret self - other; } + pure fn mul(&&other: T) -> T { ret self * other; } + pure fn div(&&other: T) -> T { ret self / other; } + pure fn modulo(&&other: T) -> T { ret self % other; } + pure fn neg() -> T { ret -self; } + + pure fn to_int() -> int { ret self as int; } + pure fn from_int(n: int) -> T { ret n as T; } } /** diff --git a/src/libstd/cmp.rs b/src/libstd/cmp.rs index a89148ecec9..f74cbba23ce 100644 --- a/src/libstd/cmp.rs +++ b/src/libstd/cmp.rs @@ -2,24 +2,24 @@ const fuzzy_epsilon: float = 1.0e-6; -iface fuzzy_eq { - fn fuzzy_eq(&&other: self) -> bool; +trait fuzzy_eq { + pure fn fuzzy_eq(&&other: self) -> bool; } impl fuzzy_eq of fuzzy_eq for float { - fn fuzzy_eq(&&other: float) -> bool { + pure fn fuzzy_eq(&&other: float) -> bool { ret float::abs(self - other) < fuzzy_epsilon; } } impl fuzzy_eq of fuzzy_eq for f32 { - fn fuzzy_eq(&&other: f32) -> bool { + pure fn fuzzy_eq(&&other: f32) -> bool { ret f32::abs(self - other) < (fuzzy_epsilon as f32); } } impl fuzzy_eq of fuzzy_eq for f64 { - fn fuzzy_eq(&&other: f64) -> bool { + pure fn fuzzy_eq(&&other: f64) -> bool { ret f64::abs(self - other) < (fuzzy_epsilon as f64); } } -- cgit 1.4.1-3-g733a5 From de48b7d4c4bba0212080c9aeb63ac8a13bb04b06 Mon Sep 17 00:00:00 2001 From: Ben Blum Date: Wed, 25 Jul 2012 19:51:12 -0400 Subject: dlist: cleanup a little; pretend to implement "cycle-collecting" destructor --- src/libcore/dlist.rs | 74 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 24 deletions(-) (limited to 'src/libcore') diff --git a/src/libcore/dlist.rs b/src/libcore/dlist.rs index 087194f721d..d2215ea528a 100644 --- a/src/libcore/dlist.rs +++ b/src/libcore/dlist.rs @@ -18,12 +18,28 @@ enum dlist_node = @{ mut next: dlist_link }; -// Needs to be an @-box so nodes can back-reference it. -enum dlist = @{ - mut size: uint, - mut hd: dlist_link, - mut tl: dlist_link -}; +class dlist_root { + let mut size: uint; + let mut hd: dlist_link; + let mut tl: dlist_link; + new() { + self.size = 0; self.hd = none; self.tl = none; + } + drop { + /* FIXME (#????) This doesn't work during task failure - the box + * annihilator might have killed some of our nodes already. This will + * be safe to uncomment when the box annihilator is safer. As is, + * this makes test_dlist_cyclic_link below crash the runtime. + // Empty the list. Not doing this explicitly would leave cyclic links + // around, not to be freed until cycle collection at task exit. + while self.hd.is_some() { + self.unlink(self.hd.get()); + } + */ + } +} + +type dlist = @dlist_root; impl private_methods for dlist_node { pure fn assert_links() { @@ -91,7 +107,7 @@ pure fn new_dlist_node(+data: T) -> dlist_node { /// Creates a new, empty dlist. pure fn new_dlist() -> dlist { - dlist(@{mut size: 0, mut hd: none, mut tl: none}) + @unchecked { dlist_root() } } /// Creates a new dlist with a single element @@ -118,7 +134,7 @@ fn concat(lists: dlist>) -> dlist { result } -impl private_methods for dlist { +impl private_methods for dlist_root { pure fn new_link(-data: T) -> dlist_link { some(dlist_node(@{data: data, mut linked: true, mut prev: none, mut next: none})) @@ -334,7 +350,7 @@ impl extensions for dlist { * to the other list's head. O(1). */ fn append(them: dlist) { - if box::ptr_eq(*self, *them) { + if box::ptr_eq(self, them) { fail ~"Cannot append a dlist to itself!" } if them.len() > 0 { @@ -351,7 +367,7 @@ impl extensions for dlist { * list's tail to this list's head. O(1). */ fn prepend(them: dlist) { - if box::ptr_eq(*self, *them) { + if box::ptr_eq(self, them) { fail ~"Cannot prepend a dlist to itself!" } if them.len() > 0 { @@ -366,15 +382,25 @@ impl extensions for dlist { /// Reverse the list's elements in place. O(n). fn reverse() { - let temp = new_dlist::(); + do option::while_some(self.hd) |nobe| { + let next_nobe = nobe.next; + self.remove(nobe); + self.make_mine(nobe); + self.add_head(some(nobe)); + next_nobe + } + } + + /** + * Remove everything from the list. This is important because the cyclic + * links won't otherwise be automatically refcounted-collected. O(n). + */ + fn clear() { + // Cute as it would be to simply detach the list and proclaim "O(1)!", + // the GC would still be a hidden O(n). Better to be honest about it. while !self.is_empty() { - let nobe = self.pop_n().get(); - nobe.linked = true; // meh, kind of disorganised. - temp.add_head(some(nobe)); + let _ = self.pop(); } - self.hd = temp.hd; - self.tl = temp.tl; - self.size = temp.size; } /// Iterate over nodes. @@ -847,7 +873,7 @@ mod tests { l.assert_consistent(); assert l.is_empty(); } #[test] #[should_fail] #[ignore(cfg(windows))] - fn test_asymmetric_link() { + fn test_dlist_asymmetric_link() { let l = new_dlist::(); let _one = l.push_n(1); let two = l.push_n(2); @@ -855,7 +881,7 @@ mod tests { l.assert_consistent(); } #[test] #[should_fail] #[ignore(cfg(windows))] - fn test_cyclic_list() { + fn test_dlist_cyclic_list() { let l = new_dlist::(); let one = l.push_n(1); let _two = l.push_n(2); @@ -865,32 +891,32 @@ mod tests { l.assert_consistent(); } #[test] #[should_fail] #[ignore(cfg(windows))] - fn test_headless() { + fn test_dlist_headless() { new_dlist::().head(); } #[test] #[should_fail] #[ignore(cfg(windows))] - fn test_insert_already_present_before() { + fn test_dlist_insert_already_present_before() { let l = new_dlist::(); let one = l.push_n(1); let two = l.push_n(2); l.insert_n_before(two, one); } #[test] #[should_fail] #[ignore(cfg(windows))] - fn test_insert_already_present_after() { + fn test_dlist_insert_already_present_after() { let l = new_dlist::(); let one = l.push_n(1); let two = l.push_n(2); l.insert_n_after(one, two); } #[test] #[should_fail] #[ignore(cfg(windows))] - fn test_insert_before_orphan() { + fn test_dlist_insert_before_orphan() { let l = new_dlist::(); let one = new_dlist_node(1); let two = new_dlist_node(2); l.insert_n_before(one, two); } #[test] #[should_fail] #[ignore(cfg(windows))] - fn test_insert_after_orphan() { + fn test_dlist_insert_after_orphan() { let l = new_dlist::(); let one = new_dlist_node(1); let two = new_dlist_node(2); -- cgit 1.4.1-3-g733a5 From 5cf99c585ac16ad8c990c134333e61ea3bf591fb Mon Sep 17 00:00:00 2001 From: Ben Blum Date: Thu, 26 Jul 2012 00:23:42 -0400 Subject: dlist pop needs copy after all (#3024) --- src/libcore/dlist.rs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'src/libcore') diff --git a/src/libcore/dlist.rs b/src/libcore/dlist.rs index d2215ea528a..cd36d14816c 100644 --- a/src/libcore/dlist.rs +++ b/src/libcore/dlist.rs @@ -304,20 +304,6 @@ impl extensions for dlist { tl.map(|nobe| self.unlink(nobe)); tl } - /// Remove data from the head of the list. O(1). - fn pop() -> option { - do option::map_consume(self.pop_n()) |nobe| { - let dlist_node(@{ data: x, _ }) <- nobe; - x - } - } - /// Remove data from the tail of the list. O(1). - fn pop_tail() -> option { - do option::map_consume(self.pop_tail_n()) |nobe| { - let dlist_node(@{ data: x, _ }) <- nobe; - x - } - } /// Get the node at the list's head. O(1). pure fn peek_n() -> option> { self.hd } /// Get the node at the list's tail. O(1). @@ -399,7 +385,7 @@ impl extensions for dlist { // Cute as it would be to simply detach the list and proclaim "O(1)!", // the GC would still be a hidden O(n). Better to be honest about it. while !self.is_empty() { - let _ = self.pop(); + let _ = self.pop_n(); } } @@ -457,6 +443,10 @@ impl extensions for dlist { } impl extensions for dlist { + /// Remove data from the head of the list. O(1). + fn pop() -> option { self.pop_n().map (|nobe| nobe.data) } + /// Remove data from the tail of the list. O(1). + fn pop_tail() -> option { self.pop_tail_n().map (|nobe| nobe.data) } /// Get data at the list's head. O(1). pure fn peek() -> option { self.peek_n().map (|nobe| nobe.data) } /// Get data at the list's tail. O(1). @@ -622,6 +612,13 @@ mod tests { a.assert_consistent(); assert a.is_empty(); } #[test] + fn test_dlist_clear() { + let a = from_vec(~[5,4,3,2,1]); + a.clear(); + assert a.len() == 0; + a.assert_consistent(); + } + #[test] fn test_dlist_is_empty() { let empty = new_dlist::(); let full1 = from_vec(~[1,2,3]); -- cgit 1.4.1-3-g733a5