| // Decoding metadata from a single crate's metadata |
| |
| use std::ebml; |
| use std::map; |
| use std::map::HashMap; |
| use std::serialization::deserialize; |
| use io::WriterUtil; |
| use dvec::DVec; |
| use syntax::{ast, ast_util}; |
| use syntax::attr; |
| use middle::ty; |
| use syntax::ast_map; |
| use tydecode::{parse_ty_data, parse_def_id, parse_bounds_data, |
| parse_ident}; |
| use syntax::print::pprust; |
| use cmd=cstore::crate_metadata; |
| use util::ppaux::ty_to_str; |
| use syntax::diagnostic::span_handler; |
| use common::*; |
| use syntax::parse::token::ident_interner; |
| use hash::{Hash, HashUtil}; |
| |
| export class_dtor; |
| export get_class_fields; |
| export get_symbol; |
| export get_enum_variants; |
| export get_type; |
| export get_region_param; |
| export get_type_param_count; |
| export get_impl_traits; |
| export get_class_method; |
| export get_impl_method; |
| export lookup_def; |
| export resolve_path; |
| export get_crate_attributes; |
| export list_crate_metadata; |
| export crate_dep; |
| export get_crate_deps; |
| export get_crate_hash; |
| 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; |
| export dl_impl; |
| export dl_field; |
| export path_entry; |
| export each_path; |
| export get_item_path; |
| export maybe_find_item; // sketchy |
| export item_type; // sketchy |
| export maybe_get_item_ast; |
| export decode_inlined_item; |
| export method_info, _impl; |
| |
| // Used internally by astencode: |
| export translate_def_id; |
| |
| // A function that takes a def_id relative to the crate being searched and |
| // returns a def_id relative to the compilation environment, i.e. if we hit a |
| // def_id for an item defined in another crate, somebody needs to figure out |
| // what crate that's in and give us a def_id that makes sense for the current |
| // build. |
| |
| fn lookup_hash(d: ebml::Doc, eq_fn: fn(x:&[u8]) -> bool, hash: uint) -> |
| Option<ebml::Doc> { |
| let index = ebml::get_doc(d, tag_index); |
| let table = ebml::get_doc(index, tag_index_table); |
| let hash_pos = table.start + hash % 256u * 4u; |
| let pos = io::u64_from_be_bytes(*d.data, hash_pos, 4u) as uint; |
| let tagged_doc = ebml::doc_at(d.data, pos); |
| |
| let belt = tag_index_buckets_bucket_elt; |
| for ebml::tagged_docs(tagged_doc.doc, belt) |elt| { |
| let pos = io::u64_from_be_bytes(*elt.data, elt.start, 4u) as uint; |
| if eq_fn(vec::view(*elt.data, elt.start + 4u, elt.end)) { |
| return Some(ebml::doc_at(d.data, pos).doc); |
| } |
| }; |
| None |
| } |
| |
| fn maybe_find_item(item_id: int, items: ebml::Doc) -> Option<ebml::Doc> { |
| fn eq_item(bytes: &[u8], item_id: int) -> bool { |
| return io::u64_from_be_bytes(vec::view(bytes, 0u, 4u), 0u, 4u) as int |
| == item_id; |
| } |
| lookup_hash(items, |
| |a| eq_item(a, item_id), |
| item_id.hash() as uint) |
| } |
| |
| fn find_item(item_id: int, items: ebml::Doc) -> ebml::Doc { |
| return maybe_find_item(item_id, items).get(); |
| } |
| |
| // Looks up an item in the given metadata and returns an ebml doc pointing |
| // to the item data. |
| fn lookup_item(item_id: int, data: @~[u8]) -> ebml::Doc { |
| let items = ebml::get_doc(ebml::Doc(data), tag_items); |
| match maybe_find_item(item_id, items) { |
| None => fail(fmt!("lookup_item: id not found: %d", item_id)), |
| Some(d) => d |
| } |
| } |
| |
| enum Family { |
| Const, // c |
| Fn, // f |
| UnsafeFn, // u |
| PureFn, // p |
| StaticMethod, // F |
| UnsafeStaticMethod, // U |
| PureStaticMethod, // P |
| ForeignFn, // e |
| Type, // y |
| ForeignType, // T |
| Mod, // m |
| ForeignMod, // n |
| Enum, // t |
| Variant, // v |
| Impl, // i |
| Trait, // I |
| Class, // C |
| Struct, // S |
| PublicField, // g |
| PrivateField, // j |
| InheritedField // N |
| } |
| |
| impl Family : cmp::Eq { |
| pure fn eq(other: &Family) -> bool { |
| (self as uint) == ((*other) as uint) |
| } |
| pure fn ne(other: &Family) -> bool { !self.eq(other) } |
| } |
| |
| fn item_family(item: ebml::Doc) -> Family { |
| let fam = ebml::get_doc(item, tag_items_data_item_family); |
| match ebml::doc_as_u8(fam) as char { |
| 'c' => Const, |
| 'f' => Fn, |
| 'u' => UnsafeFn, |
| 'p' => PureFn, |
| 'F' => StaticMethod, |
| 'U' => UnsafeStaticMethod, |
| 'P' => PureStaticMethod, |
| 'e' => ForeignFn, |
| 'y' => Type, |
| 'T' => ForeignType, |
| 'm' => Mod, |
| 'n' => ForeignMod, |
| 't' => Enum, |
| 'v' => Variant, |
| 'i' => Impl, |
| 'I' => Trait, |
| 'C' => Class, |
| 'S' => Struct, |
| 'g' => PublicField, |
| 'j' => PrivateField, |
| 'N' => InheritedField, |
| c => fail (#fmt("unexpected family char: %c", c)) |
| } |
| } |
| |
| fn item_symbol(item: ebml::Doc) -> ~str { |
| let sym = ebml::get_doc(item, tag_items_data_item_symbol); |
| return str::from_bytes(ebml::doc_data(sym)); |
| } |
| |
| fn item_parent_item(d: ebml::Doc) -> Option<ast::def_id> { |
| for ebml::tagged_docs(d, tag_items_data_parent_item) |did| { |
| return Some(ebml::with_doc_data(did, |d| parse_def_id(d))); |
| } |
| None |
| } |
| |
| fn item_def_id(d: ebml::Doc, cdata: cmd) -> ast::def_id { |
| let tagdoc = ebml::get_doc(d, tag_def_id); |
| return translate_def_id(cdata, ebml::with_doc_data(tagdoc, |
| |d| parse_def_id(d))); |
| } |
| |
| fn each_reexport(d: ebml::Doc, f: fn(ebml::Doc) -> bool) { |
| for ebml::tagged_docs(d, tag_items_data_item_reexport) |reexport_doc| { |
| if !f(reexport_doc) { |
| return; |
| } |
| } |
| } |
| |
| fn field_mutability(d: ebml::Doc) -> ast::class_mutability { |
| // Use maybe_get_doc in case it's a method |
| option::map_default( |
| &ebml::maybe_get_doc(d, tag_class_mut), |
| ast::class_immutable, |
| |d| { |
| match ebml::doc_as_u8(*d) as char { |
| 'm' => ast::class_mutable, |
| _ => ast::class_immutable |
| } |
| }) |
| } |
| |
| fn variant_disr_val(d: ebml::Doc) -> Option<int> { |
| do option::chain(ebml::maybe_get_doc(d, tag_disr_val)) |val_doc| { |
| int::parse_bytes(ebml::doc_data(val_doc), 10u) |
| } |
| } |
| |
| fn doc_type(doc: ebml::Doc, tcx: ty::ctxt, cdata: cmd) -> ty::t { |
| let tp = ebml::get_doc(doc, tag_items_data_item_type); |
| parse_ty_data(tp.data, cdata.cnum, tp.start, tcx, |did| { |
| translate_def_id(cdata, did) |
| }) |
| } |
| |
| fn item_type(item_id: ast::def_id, item: ebml::Doc, |
| tcx: ty::ctxt, cdata: cmd) -> ty::t { |
| let t = doc_type(item, tcx, cdata); |
| if family_names_type(item_family(item)) { |
| ty::mk_with_id(tcx, t, item_id) |
| } else { t } |
| } |
| |
| fn item_impl_traits(item: ebml::Doc, tcx: ty::ctxt, cdata: cmd) -> ~[ty::t] { |
| let mut results = ~[]; |
| for ebml::tagged_docs(item, tag_impl_trait) |ity| { |
| results.push(doc_type(ity, tcx, cdata)); |
| }; |
| results |
| } |
| |
| fn item_ty_param_bounds(item: ebml::Doc, tcx: ty::ctxt, cdata: cmd) |
| -> @~[ty::param_bounds] { |
| let mut bounds = ~[]; |
| for ebml::tagged_docs(item, tag_items_data_item_ty_param_bounds) |p| { |
| let bd = parse_bounds_data(p.data, p.start, cdata.cnum, tcx, |did| { |
| translate_def_id(cdata, did) |
| }); |
| bounds.push(bd); |
| } |
| @bounds |
| } |
| |
| fn item_ty_region_param(item: ebml::Doc) -> Option<ty::region_variance> { |
| ebml::maybe_get_doc(item, tag_region_param).map(|doc| { |
| deserialize(&ebml::Deserializer(*doc)) |
| }) |
| } |
| |
| fn item_ty_param_count(item: ebml::Doc) -> uint { |
| let mut n = 0u; |
| ebml::tagged_docs(item, tag_items_data_item_ty_param_bounds, |
| |_p| { n += 1u; true } ); |
| n |
| } |
| |
| fn enum_variant_ids(item: ebml::Doc, cdata: cmd) -> ~[ast::def_id] { |
| let mut ids: ~[ast::def_id] = ~[]; |
| let v = tag_items_data_item_variant; |
| for ebml::tagged_docs(item, v) |p| { |
| let ext = ebml::with_doc_data(p, |d| parse_def_id(d)); |
| ids.push({crate: cdata.cnum, node: ext.node}); |
| }; |
| return ids; |
| } |
| |
| fn item_path(intr: @ident_interner, item_doc: ebml::Doc) -> ast_map::path { |
| let path_doc = ebml::get_doc(item_doc, tag_path); |
| |
| let len_doc = ebml::get_doc(path_doc, tag_path_len); |
| let len = ebml::doc_as_u32(len_doc) as uint; |
| |
| let mut result = vec::with_capacity(len); |
| for ebml::docs(path_doc) |tag, elt_doc| { |
| if tag == tag_path_elt_mod { |
| let str = ebml::doc_as_str(elt_doc); |
| result.push(ast_map::path_mod(intr.intern(@str))); |
| } else if tag == tag_path_elt_name { |
| let str = ebml::doc_as_str(elt_doc); |
| result.push(ast_map::path_name(intr.intern(@str))); |
| } else { |
| // ignore tag_path_len element |
| } |
| } |
| |
| return result; |
| } |
| |
| fn item_name(intr: @ident_interner, item: ebml::Doc) -> ast::ident { |
| let name = ebml::get_doc(item, tag_paths_data_name); |
| intr.intern(@str::from_bytes(ebml::doc_data(name))) |
| } |
| |
| fn item_to_def_like(item: ebml::Doc, did: ast::def_id, cnum: ast::crate_num) |
| -> def_like { |
| let fam = item_family(item); |
| match fam { |
| Const => dl_def(ast::def_const(did)), |
| Class => dl_def(ast::def_class(did, true)), |
| Struct => dl_def(ast::def_class(did, false)), |
| UnsafeFn => dl_def(ast::def_fn(did, ast::unsafe_fn)), |
| Fn => dl_def(ast::def_fn(did, ast::impure_fn)), |
| PureFn => dl_def(ast::def_fn(did, ast::pure_fn)), |
| ForeignFn => dl_def(ast::def_fn(did, ast::extern_fn)), |
| UnsafeStaticMethod => dl_def(ast::def_static_method(did, |
| ast::unsafe_fn)), |
| StaticMethod => dl_def(ast::def_static_method(did, ast::impure_fn)), |
| PureStaticMethod => dl_def(ast::def_static_method(did, ast::pure_fn)), |
| Type | ForeignType => dl_def(ast::def_ty(did)), |
| Mod => dl_def(ast::def_mod(did)), |
| ForeignMod => dl_def(ast::def_foreign_mod(did)), |
| Variant => { |
| match item_parent_item(item) { |
| Some(t) => { |
| let tid = {crate: cnum, node: t.node}; |
| dl_def(ast::def_variant(tid, did)) |
| } |
| None => fail ~"item_to_def_like: enum item has no parent" |
| } |
| } |
| Trait | Enum => dl_def(ast::def_ty(did)), |
| Impl => dl_impl(did), |
| PublicField | PrivateField | InheritedField => dl_field, |
| } |
| } |
| |
| fn lookup_def(cnum: ast::crate_num, data: @~[u8], did_: ast::def_id) -> |
| ast::def { |
| let item = lookup_item(did_.node, data); |
| let did = {crate: cnum, node: did_.node}; |
| // We treat references to enums as references to types. |
| return def_like_to_def(item_to_def_like(item, did, cnum)); |
| } |
| |
| fn get_type(cdata: cmd, id: ast::node_id, tcx: ty::ctxt) |
| -> ty::ty_param_bounds_and_ty { |
| |
| let item = lookup_item(id, cdata.data); |
| let t = item_type({crate: cdata.cnum, node: id}, item, tcx, cdata); |
| let tp_bounds = if family_has_type_params(item_family(item)) { |
| item_ty_param_bounds(item, tcx, cdata) |
| } else { @~[] }; |
| let rp = item_ty_region_param(item); |
| return {bounds: tp_bounds, |
| region_param: rp, |
| ty: t}; |
| } |
| |
| fn get_region_param(cdata: cmd, id: ast::node_id) |
| -> Option<ty::region_variance> { |
| |
| let item = lookup_item(id, cdata.data); |
| return item_ty_region_param(item); |
| } |
| |
| fn get_type_param_count(data: @~[u8], id: ast::node_id) -> uint { |
| item_ty_param_count(lookup_item(id, data)) |
| } |
| |
| fn get_impl_traits(cdata: cmd, id: ast::node_id, tcx: ty::ctxt) -> ~[ty::t] { |
| item_impl_traits(lookup_item(id, cdata.data), tcx, cdata) |
| } |
| |
| fn get_impl_method(intr: @ident_interner, cdata: cmd, id: ast::node_id, |
| name: ast::ident) -> ast::def_id { |
| let items = ebml::get_doc(ebml::Doc(cdata.data), tag_items); |
| let mut found = None; |
| for ebml::tagged_docs(find_item(id, items), tag_item_impl_method) |mid| { |
| let m_did = ebml::with_doc_data(mid, |d| parse_def_id(d)); |
| if item_name(intr, find_item(m_did.node, items)) == name { |
| found = Some(translate_def_id(cdata, m_did)); |
| } |
| } |
| found.get() |
| } |
| |
| fn get_class_method(intr: @ident_interner, cdata: cmd, id: ast::node_id, |
| name: ast::ident) -> ast::def_id { |
| let items = ebml::get_doc(ebml::Doc(cdata.data), tag_items); |
| let mut found = None; |
| let cls_items = match maybe_find_item(id, items) { |
| Some(it) => it, |
| None => fail (fmt!("get_class_method: class id not found \ |
| when looking up method %s", *intr.get(name))) |
| }; |
| for ebml::tagged_docs(cls_items, tag_item_trait_method) |mid| { |
| let m_did = item_def_id(mid, cdata); |
| if item_name(intr, mid) == name { |
| found = Some(m_did); |
| } |
| } |
| match found { |
| Some(found) => found, |
| None => fail (fmt!("get_class_method: no method named %s", |
| *intr.get(name))) |
| } |
| } |
| |
| fn class_dtor(cdata: cmd, id: ast::node_id) -> Option<ast::def_id> { |
| let items = ebml::get_doc(ebml::Doc(cdata.data), tag_items); |
| let mut found = None; |
| let cls_items = match maybe_find_item(id, items) { |
| Some(it) => it, |
| None => fail (fmt!("class_dtor: class id not found \ |
| when looking up dtor for %d", id)) |
| }; |
| for ebml::tagged_docs(cls_items, tag_item_dtor) |doc| { |
| let doc1 = ebml::get_doc(doc, tag_def_id); |
| let did = ebml::with_doc_data(doc1, |d| parse_def_id(d)); |
| found = Some(translate_def_id(cdata, did)); |
| }; |
| found |
| } |
| |
| fn get_symbol(data: @~[u8], id: ast::node_id) -> ~str { |
| return item_symbol(lookup_item(id, data)); |
| } |
| |
| // Something that a name can resolve to. |
| enum def_like { |
| dl_def(ast::def), |
| dl_impl(ast::def_id), |
| dl_field |
| } |
| |
| fn def_like_to_def(def_like: def_like) -> ast::def { |
| match def_like { |
| dl_def(def) => return def, |
| dl_impl(*) => fail ~"found impl in def_like_to_def", |
| dl_field => fail ~"found field in def_like_to_def" |
| } |
| } |
| |
| // A path. |
| struct path_entry { |
| // The full path, separated by '::'. |
| path_string: ~str, |
| // The definition, implementation, or field that this path corresponds to. |
| def_like: def_like, |
| } |
| |
| fn path_entry(path_string: ~str, def_like: def_like) -> path_entry { |
| path_entry { |
| path_string: path_string, |
| def_like: def_like |
| } |
| } |
| |
| /// Iterates over all the paths in the given crate. |
| fn each_path(intr: @ident_interner, cdata: cmd, f: fn(path_entry) -> bool) { |
| let root = ebml::Doc(cdata.data); |
| let items = ebml::get_doc(root, tag_items); |
| let items_data = ebml::get_doc(items, tag_items_data); |
| |
| let mut broken = false; |
| |
| // First, go through all the explicit items. |
| for ebml::tagged_docs(items_data, tag_items_data_item) |item_doc| { |
| if !broken { |
| let path = ast_map::path_to_str_with_sep( |
| item_path(intr, item_doc), ~"::", intr); |
| if path != ~"" { |
| // Extract the def ID. |
| let def_id = item_def_id(item_doc, cdata); |
| |
| // Construct the def for this item. |
| debug!("(each_path) yielding explicit item: %s", path); |
| let def_like = item_to_def_like(item_doc, def_id, cdata.cnum); |
| |
| // Hand the information off to the iteratee. |
| let this_path_entry = path_entry(path, def_like); |
| if !f(this_path_entry) { |
| broken = true; // XXX: This is awful. |
| } |
| } |
| |
| // If this is a module, find the reexports. |
| for each_reexport(item_doc) |reexport_doc| { |
| if !broken { |
| let def_id_doc = |
| ebml::get_doc(reexport_doc, |
| tag_items_data_item_reexport_def_id); |
| let def_id = |
| ebml::with_doc_data(def_id_doc, |d| parse_def_id(d)); |
| let def_id = translate_def_id(cdata, def_id); |
| |
| let reexport_name_doc = |
| ebml::get_doc(reexport_doc, |
| tag_items_data_item_reexport_name); |
| let reexport_name = ebml::doc_as_str(reexport_name_doc); |
| |
| let reexport_path; |
| if path == ~"" { |
| reexport_path = reexport_name; |
| } else { |
| reexport_path = path + ~"::" + reexport_name; |
| } |
| |
| // Get the item. |
| match maybe_find_item(def_id.node, items) { |
| None => {} |
| Some(item_doc) => { |
| // Construct the def for this item. |
| let def_like = item_to_def_like(item_doc, |
| def_id, |
| cdata.cnum); |
| |
| // Hand the information off to the iteratee. |
| debug!("(each_path) yielding reexported \ |
| item: %s", reexport_path); |
| |
| let this_path_entry = |
| path_entry(reexport_path, def_like); |
| if (!f(this_path_entry)) { |
| broken = true; // XXX: This is awful. |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| // If broken, stop here. |
| if broken { |
| return; |
| } |
| } |
| |
| fn get_item_path(intr: @ident_interner, cdata: cmd, id: ast::node_id) |
| -> ast_map::path { |
| item_path(intr, lookup_item(id, cdata.data)) |
| } |
| |
| type decode_inlined_item = fn( |
| cdata: cstore::crate_metadata, |
| tcx: ty::ctxt, |
| path: ast_map::path, |
| par_doc: ebml::Doc) -> Option<ast::inlined_item>; |
| |
| fn maybe_get_item_ast(intr: @ident_interner, cdata: cmd, tcx: ty::ctxt, |
| id: ast::node_id, |
| decode_inlined_item: decode_inlined_item |
| ) -> csearch::found_ast { |
| debug!("Looking up item: %d", id); |
| let item_doc = lookup_item(id, cdata.data); |
| let path = vec::init(item_path(intr, item_doc)); |
| match decode_inlined_item(cdata, tcx, path, item_doc) { |
| Some(ii) => csearch::found(ii), |
| None => { |
| match item_parent_item(item_doc) { |
| Some(did) => { |
| let did = translate_def_id(cdata, did); |
| let parent_item = lookup_item(did.node, cdata.data); |
| match decode_inlined_item(cdata, tcx, path, |
| parent_item) { |
| Some(ii) => csearch::found_parent(did, ii), |
| None => csearch::not_found |
| } |
| } |
| None => csearch::not_found |
| } |
| } |
| } |
| } |
| |
| fn get_enum_variants(intr: @ident_interner, cdata: cmd, id: ast::node_id, |
| tcx: ty::ctxt) -> ~[ty::variant_info] { |
| let data = cdata.data; |
| let items = ebml::get_doc(ebml::Doc(data), tag_items); |
| let item = find_item(id, items); |
| let mut infos: ~[ty::variant_info] = ~[]; |
| let variant_ids = enum_variant_ids(item, cdata); |
| let mut disr_val = 0; |
| for variant_ids.each |did| { |
| let item = find_item(did.node, items); |
| let ctor_ty = item_type({crate: cdata.cnum, node: id}, item, |
| tcx, cdata); |
| let name = item_name(intr, item); |
| let mut arg_tys: ~[ty::t] = ~[]; |
| match ty::get(ctor_ty).sty { |
| ty::ty_fn(f) => { |
| for f.sig.inputs.each |a| { arg_tys.push(a.ty); } |
| } |
| _ => { /* Nullary enum variant. */ } |
| } |
| match variant_disr_val(item) { |
| Some(val) => { disr_val = val; } |
| _ => { /* empty */ } |
| } |
| infos.push(@{args: arg_tys, ctor_ty: ctor_ty, name: name, |
| id: *did, disr_val: disr_val}); |
| disr_val += 1; |
| } |
| return infos; |
| } |
| |
| // NB: These types are duplicated in resolve.rs |
| type method_info = { |
| did: ast::def_id, |
| n_tps: uint, |
| ident: ast::ident, |
| self_type: ast::self_ty_ |
| }; |
| |
| type _impl = {did: ast::def_id, ident: ast::ident, methods: ~[@method_info]}; |
| |
| fn get_self_ty(item: ebml::Doc) -> ast::self_ty_ { |
| fn get_mutability(ch: u8) -> ast::mutability { |
| match ch as char { |
| 'i' => { ast::m_imm } |
| 'm' => { ast::m_mutbl } |
| 'c' => { ast::m_const } |
| _ => { |
| fail fmt!("unknown mutability character: `%c`", ch as char) |
| } |
| } |
| } |
| |
| let self_type_doc = ebml::get_doc(item, tag_item_trait_method_self_ty); |
| let string = ebml::doc_as_str(self_type_doc); |
| |
| let self_ty_kind = string[0]; |
| match self_ty_kind as char { |
| 's' => { return ast::sty_static; } |
| 'r' => { return ast::sty_by_ref; } |
| 'v' => { return ast::sty_value; } |
| '@' => { return ast::sty_box(get_mutability(string[1])); } |
| '~' => { return ast::sty_uniq(get_mutability(string[1])); } |
| '&' => { return ast::sty_region(get_mutability(string[1])); } |
| _ => { |
| fail fmt!("unknown self type code: `%c`", self_ty_kind as char); |
| } |
| } |
| } |
| |
| fn item_impl_methods(intr: @ident_interner, cdata: cmd, item: ebml::Doc, |
| base_tps: uint) -> ~[@method_info] { |
| let mut rslt = ~[]; |
| for ebml::tagged_docs(item, tag_item_impl_method) |doc| { |
| let m_did = ebml::with_doc_data(doc, |d| parse_def_id(d)); |
| let mth_item = lookup_item(m_did.node, cdata.data); |
| let self_ty = get_self_ty(mth_item); |
| rslt.push(@{did: translate_def_id(cdata, m_did), |
| n_tps: item_ty_param_count(mth_item) - base_tps, |
| ident: item_name(intr, mth_item), |
| self_type: self_ty}); |
| } |
| rslt |
| } |
| |
| fn get_impls_for_mod(intr: @ident_interner, cdata: cmd, |
| m_id: ast::node_id, name: Option<ast::ident>, |
| get_cdata: fn(ast::crate_num) -> cmd) |
| -> @~[@_impl] { |
| |
| let data = cdata.data; |
| let mod_item = lookup_item(m_id, data); |
| let mut result = ~[]; |
| for ebml::tagged_docs(mod_item, tag_mod_impl) |doc| { |
| let did = ebml::with_doc_data(doc, |d| parse_def_id(d)); |
| let local_did = translate_def_id(cdata, did); |
| debug!("(get impls for mod) getting did %? for '%?'", |
| local_did, name); |
| // The impl may be defined in a different crate. Ask the caller |
| // to give us the metadata |
| let impl_cdata = get_cdata(local_did.crate); |
| let impl_data = impl_cdata.data; |
| let item = lookup_item(local_did.node, impl_data); |
| let nm = item_name(intr, item); |
| if match name { Some(n) => { n == nm } None => { true } } { |
| let base_tps = item_ty_param_count(item); |
| result.push(@{ |
| did: local_did, ident: nm, |
| methods: item_impl_methods(intr, impl_cdata, item, base_tps) |
| }); |
| }; |
| } |
| @result |
| } |
| |
| /* Works for both classes and traits */ |
| fn get_trait_methods(intr: @ident_interner, cdata: cmd, id: ast::node_id, |
| tcx: ty::ctxt) -> @~[ty::method] { |
| let data = cdata.data; |
| let item = lookup_item(id, data); |
| let mut result = ~[]; |
| for ebml::tagged_docs(item, tag_item_trait_method) |mth| { |
| let bounds = item_ty_param_bounds(mth, tcx, cdata); |
| let name = item_name(intr, mth); |
| let ty = doc_type(mth, tcx, cdata); |
| let fty = match ty::get(ty).sty { |
| ty::ty_fn(f) => f, |
| _ => { |
| tcx.diag.handler().bug( |
| ~"get_trait_methods: id has non-function type"); |
| } }; |
| let self_ty = get_self_ty(mth); |
| result.push({ident: name, tps: bounds, fty: fty, |
| self_ty: self_ty, |
| vis: ast::public}); |
| } |
| #debug("get_trait_methods: }"); |
| @result |
| } |
| |
| // If the item in question is a trait, returns its set of methods and |
| // their self types. Otherwise, returns none. This overlaps in an |
| // annoying way with get_trait_methods. |
| fn get_method_names_if_trait(intr: @ident_interner, cdata: cmd, |
| node_id: ast::node_id) |
| -> Option<@DVec<(ast::ident, ast::self_ty_)>> { |
| |
| let item = lookup_item(node_id, cdata.data); |
| if item_family(item) != Trait { |
| return None; |
| } |
| |
| let resulting_methods = @DVec(); |
| for ebml::tagged_docs(item, tag_item_trait_method) |method| { |
| resulting_methods.push( |
| (item_name(intr, method), get_self_ty(method))); |
| } |
| return Some(resulting_methods); |
| } |
| |
| fn get_item_attrs(cdata: cmd, |
| node_id: ast::node_id, |
| f: fn(~[@ast::meta_item])) { |
| |
| let item = lookup_item(node_id, cdata.data); |
| for ebml::tagged_docs(item, tag_attributes) |attributes| { |
| for ebml::tagged_docs(attributes, tag_attribute) |attribute| { |
| f(get_meta_items(attribute)); |
| } |
| } |
| } |
| |
| // Helper function that gets either fields or methods |
| fn get_class_members(intr: @ident_interner, cdata: cmd, id: ast::node_id, |
| p: fn(Family) -> bool) -> ~[ty::field_ty] { |
| let data = cdata.data; |
| let item = lookup_item(id, data); |
| let mut result = ~[]; |
| for ebml::tagged_docs(item, tag_item_field) |an_item| { |
| let f = item_family(an_item); |
| if p(f) { |
| let name = item_name(intr, an_item); |
| let did = item_def_id(an_item, cdata); |
| let mt = field_mutability(an_item); |
| result.push({ident: name, id: did, vis: |
| family_to_visibility(f), mutability: mt}); |
| } |
| } |
| result |
| } |
| |
| pure fn family_to_visibility(family: Family) -> ast::visibility { |
| match family { |
| PublicField => ast::public, |
| PrivateField => ast::private, |
| InheritedField => ast::inherited, |
| _ => fail |
| } |
| } |
| |
| fn get_class_fields(intr: @ident_interner, cdata: cmd, id: ast::node_id) |
| -> ~[ty::field_ty] { |
| get_class_members(intr, cdata, id, |f| f == PublicField |
| || f == PrivateField || f == InheritedField) |
| } |
| |
| fn family_has_type_params(fam: Family) -> bool { |
| match fam { |
| Const | ForeignType | Mod | ForeignMod | PublicField | PrivateField |
| | ForeignFn => false, |
| _ => true |
| } |
| } |
| |
| fn family_names_type(fam: Family) -> bool { |
| match fam { Type | Mod | Trait => true, _ => false } |
| } |
| |
| fn read_path(d: ebml::Doc) -> {path: ~str, pos: uint} { |
| let desc = ebml::doc_data(d); |
| let pos = io::u64_from_be_bytes(desc, 0u, 4u) as uint; |
| let pathbytes = vec::slice::<u8>(desc, 4u, vec::len::<u8>(desc)); |
| let path = str::from_bytes(pathbytes); |
| return {path: path, pos: pos}; |
| } |
| |
| fn describe_def(items: ebml::Doc, id: ast::def_id) -> ~str { |
| if id.crate != ast::local_crate { return ~"external"; } |
| let it = match maybe_find_item(id.node, items) { |
| Some(it) => it, |
| None => fail (fmt!("describe_def: item not found %?", id)) |
| }; |
| return item_family_to_str(item_family(it)); |
| } |
| |
| fn item_family_to_str(fam: Family) -> ~str { |
| match fam { |
| Const => ~"const", |
| Fn => ~"fn", |
| UnsafeFn => ~"unsafe fn", |
| PureFn => ~"pure fn", |
| StaticMethod => ~"static method", |
| UnsafeStaticMethod => ~"unsafe static method", |
| PureStaticMethod => ~"pure static method", |
| ForeignFn => ~"foreign fn", |
| Type => ~"type", |
| ForeignType => ~"foreign type", |
| Mod => ~"mod", |
| ForeignMod => ~"foreign mod", |
| Enum => ~"enum", |
| Variant => ~"variant", |
| Impl => ~"impl", |
| Trait => ~"trait", |
| Class => ~"class", |
| Struct => ~"struct", |
| PublicField => ~"public field", |
| PrivateField => ~"private field", |
| InheritedField => ~"inherited field", |
| } |
| } |
| |
| fn get_meta_items(md: ebml::Doc) -> ~[@ast::meta_item] { |
| let mut items: ~[@ast::meta_item] = ~[]; |
| for ebml::tagged_docs(md, tag_meta_item_word) |meta_item_doc| { |
| let nd = ebml::get_doc(meta_item_doc, tag_meta_item_name); |
| let n = str::from_bytes(ebml::doc_data(nd)); |
| items.push(attr::mk_word_item(n)); |
| }; |
| for ebml::tagged_docs(md, tag_meta_item_name_value) |meta_item_doc| { |
| let nd = ebml::get_doc(meta_item_doc, tag_meta_item_name); |
| let vd = ebml::get_doc(meta_item_doc, tag_meta_item_value); |
| let n = str::from_bytes(ebml::doc_data(nd)); |
| let v = str::from_bytes(ebml::doc_data(vd)); |
| // FIXME (#623): Should be able to decode meta_name_value variants, |
| // but currently the encoder just drops them |
| items.push(attr::mk_name_value_item_str(n, v)); |
| }; |
| for ebml::tagged_docs(md, tag_meta_item_list) |meta_item_doc| { |
| let nd = ebml::get_doc(meta_item_doc, tag_meta_item_name); |
| let n = str::from_bytes(ebml::doc_data(nd)); |
| let subitems = get_meta_items(meta_item_doc); |
| items.push(attr::mk_list_item(n, subitems)); |
| }; |
| return items; |
| } |
| |
| fn get_attributes(md: ebml::Doc) -> ~[ast::attribute] { |
| let mut attrs: ~[ast::attribute] = ~[]; |
| match ebml::maybe_get_doc(md, tag_attributes) { |
| option::Some(attrs_d) => { |
| for ebml::tagged_docs(attrs_d, tag_attribute) |attr_doc| { |
| let meta_items = get_meta_items(attr_doc); |
| // Currently it's only possible to have a single meta item on |
| // an attribute |
| assert (vec::len(meta_items) == 1u); |
| let meta_item = meta_items[0]; |
| attrs.push( |
| {node: {style: ast::attr_outer, value: *meta_item, |
| is_sugared_doc: false}, |
| span: ast_util::dummy_sp()}); |
| }; |
| } |
| option::None => () |
| } |
| return attrs; |
| } |
| |
| fn list_meta_items(intr: @ident_interner, |
| meta_items: ebml::Doc, out: io::Writer) { |
| for get_meta_items(meta_items).each |mi| { |
| out.write_str(fmt!("%s\n", pprust::meta_item_to_str(*mi, intr))); |
| } |
| } |
| |
| fn list_crate_attributes(intr: @ident_interner, md: ebml::Doc, hash: ~str, |
| out: io::Writer) { |
| out.write_str(fmt!("=Crate Attributes (%s)=\n", hash)); |
| |
| for get_attributes(md).each |attr| { |
| out.write_str(fmt!("%s\n", pprust::attribute_to_str(*attr, intr))); |
| } |
| |
| out.write_str(~"\n\n"); |
| } |
| |
| fn get_crate_attributes(data: @~[u8]) -> ~[ast::attribute] { |
| return get_attributes(ebml::Doc(data)); |
| } |
| |
| type crate_dep = {cnum: ast::crate_num, name: ast::ident, |
| vers: ~str, hash: ~str}; |
| |
| fn get_crate_deps(intr: @ident_interner, data: @~[u8]) -> ~[crate_dep] { |
| let mut deps: ~[crate_dep] = ~[]; |
| let cratedoc = ebml::Doc(data); |
| let depsdoc = ebml::get_doc(cratedoc, tag_crate_deps); |
| let mut crate_num = 1; |
| fn docstr(doc: ebml::Doc, tag_: uint) -> ~str { |
| str::from_bytes(ebml::doc_data(ebml::get_doc(doc, tag_))) |
| } |
| for ebml::tagged_docs(depsdoc, tag_crate_dep) |depdoc| { |
| deps.push({cnum: crate_num, |
| name: intr.intern(@docstr(depdoc, tag_crate_dep_name)), |
| vers: docstr(depdoc, tag_crate_dep_vers), |
| hash: docstr(depdoc, tag_crate_dep_hash)}); |
| crate_num += 1; |
| }; |
| return deps; |
| } |
| |
| fn list_crate_deps(intr: @ident_interner, data: @~[u8], out: io::Writer) { |
| out.write_str(~"=External Dependencies=\n"); |
| |
| for get_crate_deps(intr, data).each |dep| { |
| out.write_str( |
| fmt!("%d %s-%s-%s\n", |
| dep.cnum, *intr.get(dep.name), dep.hash, dep.vers)); |
| } |
| |
| out.write_str(~"\n"); |
| } |
| |
| fn get_crate_hash(data: @~[u8]) -> ~str { |
| let cratedoc = ebml::Doc(data); |
| let hashdoc = ebml::get_doc(cratedoc, tag_crate_hash); |
| return str::from_bytes(ebml::doc_data(hashdoc)); |
| } |
| |
| fn get_crate_vers(data: @~[u8]) -> ~str { |
| let attrs = decoder::get_crate_attributes(data); |
| return match attr::last_meta_item_value_str_by_name( |
| attr::find_linkage_metas(attrs), ~"vers") { |
| Some(ver) => ver, |
| None => ~"0.0" |
| }; |
| } |
| |
| fn iter_crate_items(intr: @ident_interner, |
| cdata: cmd, proc: fn(~str, ast::def_id)) { |
| for each_path(intr, cdata) |path_entry| { |
| match path_entry.def_like { |
| dl_impl(*) | dl_field => {} |
| dl_def(def) => { |
| proc(path_entry.path_string, ast_util::def_id_of_def(def)) |
| } |
| } |
| } |
| } |
| |
| fn get_crate_module_paths(intr: @ident_interner, cdata: cmd) |
| -> ~[(ast::def_id, ~str)] { |
| fn mod_of_path(p: ~str) -> ~str { |
| str::connect(vec::init(str::split_str(p, ~"::")), ~"::") |
| } |
| |
| // find all module (path, def_ids), which are not |
| // fowarded path due to renamed import or reexport |
| let mut res = ~[]; |
| let mods = map::HashMap(); |
| do iter_crate_items(intr, cdata) |path, did| { |
| let m = mod_of_path(path); |
| if str::is_not_empty(m) { |
| // if m has a sub-item, it must be a module |
| mods.insert(m, true); |
| } |
| // Collect everything by now. There might be multiple |
| // paths pointing to the same did. Those will be |
| // unified later by using the mods map |
| res.push((did, path)); |
| } |
| return do vec::filter(res) |x| { |
| let (_, xp) = *x; |
| mods.contains_key(xp) |
| } |
| } |
| |
| fn list_crate_metadata(intr: @ident_interner, bytes: @~[u8], |
| out: io::Writer) { |
| let hash = get_crate_hash(bytes); |
| let md = ebml::Doc(bytes); |
| list_crate_attributes(intr, md, hash, out); |
| list_crate_deps(intr, bytes, out); |
| } |
| |
| // Translates a def_id from an external crate to a def_id for the current |
| // compilation environment. We use this when trying to load types from |
| // external crates - if those types further refer to types in other crates |
| // then we must translate the crate number from that encoded in the external |
| // crate to the correct local crate number. |
| fn translate_def_id(cdata: cmd, did: ast::def_id) -> ast::def_id { |
| if did.crate == ast::local_crate { |
| return {crate: cdata.cnum, node: did.node}; |
| } |
| |
| match cdata.cnum_map.find(did.crate) { |
| option::Some(n) => return {crate: n, node: did.node}, |
| option::None => fail ~"didn't find a crate in the cnum_map" |
| } |
| } |
| |
| // Local Variables: |
| // mode: rust |
| // fill-column: 78; |
| // indent-tabs-mode: nil |
| // c-basic-offset: 4 |
| // buffer-file-coding-system: utf-8-unix |
| // End: |