creader: Load parts of plugin metadata on demand
This commit is contained in:
parent
677b7cad3d
commit
f314e2c4ea
2 changed files with 127 additions and 84 deletions
|
@ -16,11 +16,10 @@ use back::svh::Svh;
|
|||
use session::{config, Session};
|
||||
use session::search_paths::PathKind;
|
||||
use metadata::cstore;
|
||||
use metadata::cstore::{CStore, CrateSource};
|
||||
use metadata::cstore::{CStore, CrateSource, MetadataBlob};
|
||||
use metadata::decoder;
|
||||
use metadata::loader;
|
||||
use metadata::loader::CratePaths;
|
||||
use plugin::load::PluginMetadata;
|
||||
use util::nodemap::FnvHashMap;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
@ -154,6 +153,29 @@ fn register_native_lib(sess: &Session,
|
|||
sess.cstore.add_used_library(name, kind);
|
||||
}
|
||||
|
||||
pub struct PluginMetadata<'a> {
|
||||
sess: &'a Session,
|
||||
metadata: PMDSource,
|
||||
dylib: Option<Path>,
|
||||
info: CrateInfo,
|
||||
vi_span: Span,
|
||||
target_only: bool,
|
||||
}
|
||||
|
||||
enum PMDSource {
|
||||
Registered(Rc<cstore::crate_metadata>),
|
||||
Owned(MetadataBlob),
|
||||
}
|
||||
|
||||
impl PMDSource {
|
||||
pub fn as_slice<'a>(&'a self) -> &'a [u8] {
|
||||
match *self {
|
||||
PMDSource::Registered(ref cmd) => cmd.data(),
|
||||
PMDSource::Owned(ref mdb) => mdb.as_slice(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> CrateReader<'a> {
|
||||
pub fn new(sess: &'a Session) -> CrateReader<'a> {
|
||||
CrateReader {
|
||||
|
@ -450,17 +472,20 @@ impl<'a> CrateReader<'a> {
|
|||
}).collect()
|
||||
}
|
||||
|
||||
pub fn read_plugin_metadata(&mut self,
|
||||
krate: &ast::ViewItem) -> PluginMetadata {
|
||||
let info = self.extract_crate_info(krate).unwrap();
|
||||
pub fn read_plugin_metadata<'b>(&'b mut self,
|
||||
vi: &'b ast::ViewItem) -> PluginMetadata<'b> {
|
||||
let info = self.extract_crate_info(vi).unwrap();
|
||||
let target_triple = self.sess.opts.target_triple[];
|
||||
let is_cross = target_triple != config::host_triple();
|
||||
let mut should_link = info.should_link && !is_cross;
|
||||
let mut target_only = false;
|
||||
let ident = info.ident.clone();
|
||||
let name = info.name.clone();
|
||||
let mut load_ctxt = loader::Context {
|
||||
sess: self.sess,
|
||||
span: krate.span,
|
||||
ident: info.ident[],
|
||||
crate_name: info.name[],
|
||||
span: vi.span,
|
||||
ident: ident[],
|
||||
crate_name: name[],
|
||||
hash: None,
|
||||
filesearch: self.sess.host_filesearch(PathKind::Crate),
|
||||
triple: config::host_triple(),
|
||||
|
@ -472,32 +497,49 @@ impl<'a> CrateReader<'a> {
|
|||
let library = match load_ctxt.maybe_load_library_crate() {
|
||||
Some(l) => l,
|
||||
None if is_cross => {
|
||||
// try loading from target crates (only valid if there are
|
||||
// no syntax extensions)
|
||||
// Try loading from target crates. This will abort later if we try to
|
||||
// load a plugin registrar function,
|
||||
target_only = true;
|
||||
should_link = info.should_link;
|
||||
|
||||
load_ctxt.triple = target_triple;
|
||||
load_ctxt.filesearch = self.sess.target_filesearch(PathKind::Crate);
|
||||
let lib = load_ctxt.load_library_crate();
|
||||
if decoder::get_plugin_registrar_fn(lib.metadata.as_slice()).is_some() {
|
||||
let message = format!("crate `{}` contains a plugin_registrar fn but \
|
||||
only a version for triple `{}` could be found (need {})",
|
||||
info.ident, target_triple, config::host_triple());
|
||||
self.sess.span_err(krate.span, message[]);
|
||||
// need to abort now because the syntax expansion
|
||||
// code will shortly attempt to load and execute
|
||||
// code from the found library.
|
||||
self.sess.abort_if_errors();
|
||||
}
|
||||
should_link = info.should_link;
|
||||
lib
|
||||
load_ctxt.load_library_crate()
|
||||
}
|
||||
None => { load_ctxt.report_load_errs(); unreachable!() },
|
||||
};
|
||||
|
||||
// Read exported macros
|
||||
let imported_from = Some(token::intern(info.ident[]).ident());
|
||||
let source_name = format!("<{} macros>", info.ident[]);
|
||||
let dylib = library.dylib.clone();
|
||||
let register = should_link && self.existing_match(info.name[], None).is_none();
|
||||
let metadata = if register {
|
||||
// Register crate now to avoid double-reading metadata
|
||||
let (_, cmd, _) = self.register_crate(&None, info.ident[],
|
||||
info.name[], vi.span, library);
|
||||
PMDSource::Registered(cmd)
|
||||
} else {
|
||||
// Not registering the crate; just hold on to the metadata
|
||||
PMDSource::Owned(library.metadata)
|
||||
};
|
||||
|
||||
PluginMetadata {
|
||||
sess: self.sess,
|
||||
metadata: metadata,
|
||||
dylib: dylib,
|
||||
info: info,
|
||||
vi_span: vi.span,
|
||||
target_only: target_only,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PluginMetadata<'a> {
|
||||
/// Read exported macros
|
||||
pub fn exported_macros(&self) -> Vec<ast::MacroDef> {
|
||||
let imported_from = Some(token::intern(self.info.ident[]).ident());
|
||||
let source_name = format!("<{} macros>", self.info.ident[]);
|
||||
let mut macros = vec![];
|
||||
decoder::each_exported_macro(library.metadata.as_slice(), &*self.sess.cstore.intr,
|
||||
decoder::each_exported_macro(self.metadata.as_slice(),
|
||||
&*self.sess.cstore.intr,
|
||||
|name, attrs, body| {
|
||||
// NB: Don't use parse::parse_tts_from_source_str because it parses with
|
||||
// quote_depth > 0.
|
||||
|
@ -520,31 +562,37 @@ impl<'a> CrateReader<'a> {
|
|||
true
|
||||
}
|
||||
);
|
||||
macros
|
||||
}
|
||||
|
||||
// Look for a plugin registrar
|
||||
let registrar = decoder::get_plugin_registrar_fn(library.metadata.as_slice()).map(|id| {
|
||||
decoder::get_symbol(library.metadata.as_slice(), id)
|
||||
});
|
||||
if library.dylib.is_none() && registrar.is_some() {
|
||||
let message = format!("plugin crate `{}` only found in rlib format, \
|
||||
but must be available in dylib format",
|
||||
info.ident);
|
||||
self.sess.span_err(krate.span, message[]);
|
||||
// No need to abort because the loading code will just ignore this
|
||||
// empty dylib.
|
||||
/// Look for a plugin registrar. Returns library path and symbol name.
|
||||
pub fn plugin_registrar(&self) -> Option<(Path, String)> {
|
||||
if self.target_only {
|
||||
// Need to abort before syntax expansion.
|
||||
let message = format!("plugin crate `{}` is not available for triple `{}` \
|
||||
(only found {})",
|
||||
self.info.ident,
|
||||
config::host_triple(),
|
||||
self.sess.opts.target_triple);
|
||||
self.sess.span_err(self.vi_span, message[]);
|
||||
self.sess.abort_if_errors();
|
||||
}
|
||||
let pc = PluginMetadata {
|
||||
macros: macros,
|
||||
registrar: match (library.dylib.as_ref(), registrar) {
|
||||
(Some(dylib), Some(reg)) => Some((dylib.clone(), reg)),
|
||||
_ => None,
|
||||
},
|
||||
};
|
||||
if should_link && self.existing_match(info.name[], None).is_none() {
|
||||
// register crate now to avoid double-reading metadata
|
||||
self.register_crate(&None, info.ident[],
|
||||
info.name[], krate.span, library);
|
||||
|
||||
let registrar = decoder::get_plugin_registrar_fn(self.metadata.as_slice())
|
||||
.map(|id| decoder::get_symbol(self.metadata.as_slice(), id));
|
||||
|
||||
match (self.dylib.as_ref(), registrar) {
|
||||
(Some(dylib), Some(reg)) => Some((dylib.clone(), reg)),
|
||||
(None, Some(_)) => {
|
||||
let message = format!("plugin crate `{}` only found in rlib format, \
|
||||
but must be available in dylib format",
|
||||
self.info.ident);
|
||||
self.sess.span_err(self.vi_span, message[]);
|
||||
// No need to abort because the loading code will just ignore this
|
||||
// empty dylib.
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
pc
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,14 +23,6 @@ use syntax::visit;
|
|||
use syntax::visit::Visitor;
|
||||
use syntax::attr::AttrMetaMethods;
|
||||
|
||||
/// Metadata for a single plugin crate.
|
||||
pub struct PluginMetadata {
|
||||
/// Macros exported by the crate.
|
||||
pub macros: Vec<ast::MacroDef>,
|
||||
/// Path to the shared library file, and registrar function symbol.
|
||||
pub registrar: Option<(Path, String)>,
|
||||
}
|
||||
|
||||
/// Pointer to a registrar function.
|
||||
pub type PluginRegistrarFun =
|
||||
fn(&mut Registry);
|
||||
|
@ -86,37 +78,40 @@ pub fn load_plugins(sess: &Session, krate: &ast::Crate,
|
|||
// note that macros aren't expanded yet, and therefore macros can't add plugins.
|
||||
impl<'a, 'v> Visitor<'v> for PluginLoader<'a> {
|
||||
fn visit_view_item(&mut self, vi: &ast::ViewItem) {
|
||||
// We're only interested in `extern crate`.
|
||||
match vi.node {
|
||||
ast::ViewItemExternCrate(_, _, _) => {
|
||||
let mut plugin_phase = false;
|
||||
ast::ViewItemExternCrate(..) => (),
|
||||
_ => return,
|
||||
}
|
||||
|
||||
for attr in vi.attrs.iter().filter(|a| a.check_name("phase")) {
|
||||
let phases = attr.meta_item_list().unwrap_or(&[]);
|
||||
if attr::contains_name(phases, "plugin") {
|
||||
plugin_phase = true;
|
||||
}
|
||||
if attr::contains_name(phases, "syntax") {
|
||||
plugin_phase = true;
|
||||
self.sess.span_warn(attr.span,
|
||||
"phase(syntax) is a deprecated synonym for phase(plugin)");
|
||||
}
|
||||
}
|
||||
|
||||
if !plugin_phase { return; }
|
||||
|
||||
let PluginMetadata { macros, registrar } =
|
||||
self.reader.read_plugin_metadata(vi);
|
||||
|
||||
self.plugins.macros.extend(macros.into_iter());
|
||||
|
||||
match registrar {
|
||||
Some((lib, symbol)) => self.dylink_registrar(vi, lib, symbol),
|
||||
_ => (),
|
||||
}
|
||||
let mut plugin_phase = false;
|
||||
for attr in vi.attrs.iter().filter(|a| a.check_name("phase")) {
|
||||
let phases = attr.meta_item_list().unwrap_or(&[]);
|
||||
if attr::contains_name(phases, "plugin") {
|
||||
plugin_phase = true;
|
||||
}
|
||||
_ => (),
|
||||
if attr::contains_name(phases, "syntax") {
|
||||
plugin_phase = true;
|
||||
self.sess.span_warn(attr.span,
|
||||
"phase(syntax) is a deprecated synonym for phase(plugin)");
|
||||
}
|
||||
}
|
||||
|
||||
let mut macros = vec![];
|
||||
let mut registrar = None;
|
||||
|
||||
if plugin_phase {
|
||||
let pmd = self.reader.read_plugin_metadata(vi);
|
||||
macros = pmd.exported_macros();
|
||||
registrar = pmd.plugin_registrar();
|
||||
}
|
||||
|
||||
self.plugins.macros.extend(macros.into_iter());
|
||||
if let Some((lib, symbol)) = registrar {
|
||||
self.dylink_registrar(vi, lib, symbol);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mac(&mut self, _: &ast::Mac) {
|
||||
// bummer... can't see plugins inside macros.
|
||||
// do nothing.
|
||||
|
|
Loading…
Add table
Reference in a new issue