From 6d15c6749c30d9077c6e12af3be64c5f68fafcff Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Sat, 24 May 2014 16:16:10 -0700 Subject: [PATCH] Implement #[plugin_registrar] See RFC 22. [breaking-change] --- src/librustc/driver/driver.rs | 66 +++++--- src/librustc/driver/session.rs | 4 +- src/librustc/front/feature_gate.rs | 9 +- src/librustc/front/std_inject.rs | 2 +- src/librustc/front/test.rs | 3 - src/librustc/lib.rs | 8 + src/librustc/metadata/common.rs | 2 +- src/librustc/metadata/creader.rs | 24 ++- src/librustc/metadata/cstore.rs | 3 - src/librustc/metadata/decoder.rs | 4 +- src/librustc/metadata/encoder.rs | 40 ++--- .../registrar.rs => librustc/plugin/build.rs} | 36 +++-- src/librustc/plugin/load.rs | 131 +++++++++++++++ src/librustc/plugin/registry.rs | 55 +++++++ src/librustdoc/core.rs | 5 +- src/librustdoc/test.rs | 3 +- src/libsyntax/ext/base.rs | 19 +-- src/libsyntax/ext/expand.rs | 152 ++++-------------- src/libsyntax/lib.rs | 1 - 19 files changed, 331 insertions(+), 236 deletions(-) rename src/{libsyntax/ext/registrar.rs => librustc/plugin/build.rs} (56%) create mode 100644 src/librustc/plugin/load.rs create mode 100644 src/librustc/plugin/registry.rs diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 89c0a381cf9..45e9c7b562d 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -18,12 +18,14 @@ use front; use lib::llvm::{ContextRef, ModuleRef}; use metadata::common::LinkMeta; use metadata::creader; -use metadata::creader::Loader; use middle::cfg; use middle::cfg::graphviz::LabelledCFG; use middle::{trans, freevars, kind, ty, typeck, lint, reachable}; use middle::dependency_format; use middle; +use plugin::load::Plugins; +use plugin::registry::Registry; +use plugin; use util::common::time; use util::ppaux; use util::nodemap::{NodeSet}; @@ -39,7 +41,6 @@ use syntax::ast; use syntax::attr; use syntax::attr::{AttrMetaMethods}; use syntax::crateid::CrateId; -use syntax::ext::base::CrateLoader; use syntax::parse; use syntax::parse::token; use syntax::print::{pp, pprust}; @@ -75,11 +76,10 @@ pub fn compile_input(sess: Session, output, krate.attrs.as_slice(), &sess); - let loader = &mut Loader::new(&sess); let id = link::find_crate_id(krate.attrs.as_slice(), outputs.out_filestem.as_slice()); let (expanded_crate, ast_map) = - phase_2_configure_and_expand(&sess, loader, krate, &id); + phase_2_configure_and_expand(&sess, krate, &id); (outputs, expanded_crate, ast_map) }; write_out_deps(&sess, input, &outputs, &expanded_crate); @@ -172,7 +172,6 @@ pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input) /// harness if one is to be provided and injection of a dependency on the /// standard library and prelude. pub fn phase_2_configure_and_expand(sess: &Session, - loader: &mut CrateLoader, mut krate: ast::Crate, crate_id: &CrateId) -> (ast::Crate, syntax::ast_map::Map) { @@ -197,25 +196,42 @@ pub fn phase_2_configure_and_expand(sess: &Session, krate = time(time_passes, "configuration 1", krate, |krate| front::config::strip_unconfigured_items(krate)); - krate = time(time_passes, "expansion", krate, |krate| { - // Windows dlls do not have rpaths, so they don't know how to find their - // dependencies. It's up to us to tell the system where to find all the - // dependent dlls. Note that this uses cfg!(windows) as opposed to - // targ_cfg because syntax extensions are always loaded for the host - // compiler, not for the target. - if cfg!(windows) { - sess.host_filesearch().add_dylib_search_paths(); + let Plugins { macros, registrars } + = time(time_passes, "plugin loading", (), |_| + plugin::load::load_plugins(sess, &krate)); + + let mut registry = Registry::new(&krate); + + time(time_passes, "plugin registration", (), |_| { + for ®istrar in registrars.iter() { + registrar(&mut registry); } - let cfg = syntax::ext::expand::ExpansionConfig { - loader: loader, - deriving_hash_type_parameter: sess.features.default_type_params.get(), - crate_id: crate_id.clone(), - }; - syntax::ext::expand::expand_crate(&sess.parse_sess, - cfg, - krate) }); + let Registry { syntax_exts, .. } = registry; + + krate = time(time_passes, "expansion", (krate, macros, syntax_exts), + |(krate, macros, syntax_exts)| { + // Windows dlls do not have rpaths, so they don't know how to find their + // dependencies. It's up to us to tell the system where to find all the + // dependent dlls. Note that this uses cfg!(windows) as opposed to + // targ_cfg because syntax extensions are always loaded for the host + // compiler, not for the target. + if cfg!(windows) { + sess.host_filesearch().add_dylib_search_paths(); + } + let cfg = syntax::ext::expand::ExpansionConfig { + deriving_hash_type_parameter: sess.features.default_type_params.get(), + crate_id: crate_id.clone(), + }; + syntax::ext::expand::expand_crate(&sess.parse_sess, + cfg, + macros, + syntax_exts, + krate) + } + ); + // strip again, in case expansion added anything with a #[cfg]. krate = time(time_passes, "configuration 2", krate, |krate| front::config::strip_unconfigured_items(krate)); @@ -281,9 +297,9 @@ pub fn phase_3_run_analysis_passes(sess: Session, time(time_passes, "looking for entry point", (), |_| middle::entry::find_entry_point(&sess, krate, &ast_map)); - sess.macro_registrar_fn.set( - time(time_passes, "looking for macro registrar", (), |_| - syntax::ext::registrar::find_macro_registrar( + sess.plugin_registrar_fn.set( + time(time_passes, "looking for plugin registrar", (), |_| + plugin::build::find_plugin_registrar( sess.diagnostic(), krate))); let freevars = time(time_passes, "freevar finding", (), |_| @@ -596,9 +612,7 @@ pub fn pretty_print_input(sess: Session, let (krate, ast_map, is_expanded) = match ppm { PpmExpanded | PpmExpandedIdentified | PpmTyped | PpmFlowGraph(_) => { - let loader = &mut Loader::new(&sess); let (krate, ast_map) = phase_2_configure_and_expand(&sess, - loader, krate, &id); (krate, Some(ast_map), true) diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs index 109622b6627..773b9e6e0aa 100644 --- a/src/librustc/driver/session.rs +++ b/src/librustc/driver/session.rs @@ -36,7 +36,7 @@ pub struct Session { // For a library crate, this is always none pub entry_fn: RefCell>, pub entry_type: Cell>, - pub macro_registrar_fn: Cell>, + pub plugin_registrar_fn: Cell>, pub default_sysroot: Option, // The name of the root source file of the crate, in the local file system. The path is always // expected to be absolute. `None` means that there is no source file. @@ -232,7 +232,7 @@ pub fn build_session_(sopts: config::Options, // For a library crate, this is always none entry_fn: RefCell::new(None), entry_type: Cell::new(None), - macro_registrar_fn: Cell::new(None), + plugin_registrar_fn: Cell::new(None), default_sysroot: default_sysroot, local_crate_source_file: local_crate_source_file, working_dir: os::getcwd(), diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs index 25f0dc808c8..11dd6a86cd8 100644 --- a/src/librustc/front/feature_gate.rs +++ b/src/librustc/front/feature_gate.rs @@ -46,7 +46,7 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[ ("thread_local", Active), ("link_args", Active), ("phase", Active), - ("macro_registrar", Active), + ("plugin_registrar", Active), ("log_syntax", Active), ("trace_macros", Active), ("concat_idents", Active), @@ -192,10 +192,9 @@ impl<'a> Visitor<()> for Context<'a> { } ast::ItemFn(..) => { - if attr::contains_name(i.attrs.as_slice(), "macro_registrar") { - self.gate_feature("macro_registrar", i.span, - "cross-crate macro exports are \ - experimental and possibly buggy"); + if attr::contains_name(i.attrs.as_slice(), "plugin_registrar") { + self.gate_feature("plugin_registrar", i.span, + "compiler plugins are experimental and possibly buggy"); } } diff --git a/src/librustc/front/std_inject.rs b/src/librustc/front/std_inject.rs index fe636f7b686..0514f7de505 100644 --- a/src/librustc/front/std_inject.rs +++ b/src/librustc/front/std_inject.rs @@ -81,7 +81,7 @@ impl<'a> fold::Folder for StandardLibraryInjector<'a> { attr::mk_attr_outer(attr::mk_attr_id(), attr::mk_list_item( InternedString::new("phase"), vec!( - attr::mk_word_item(InternedString::new("syntax")), + attr::mk_word_item(InternedString::new("plugin")), attr::mk_word_item(InternedString::new("link") ))))), vis: ast::Inherited, diff --git a/src/librustc/front/test.rs b/src/librustc/front/test.rs index b21f3c2a019..174bcc86d26 100644 --- a/src/librustc/front/test.rs +++ b/src/librustc/front/test.rs @@ -16,7 +16,6 @@ use driver::session::Session; use front::config; use front::std_inject::with_version; -use metadata::creader::Loader; use std::cell::RefCell; use std::slice; @@ -150,12 +149,10 @@ impl<'a> fold::Folder for TestHarnessGenerator<'a> { fn generate_test_harness(sess: &Session, krate: ast::Crate) -> ast::Crate { - let loader = &mut Loader::new(sess); let mut cx: TestCtxt = TestCtxt { sess: sess, ext_cx: ExtCtxt::new(&sess.parse_sess, sess.opts.cfg.clone(), ExpansionConfig { - loader: loader, deriving_hash_type_parameter: false, crate_id: from_str("test").unwrap(), }), diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 1b17cfb1bae..b82dace62ef 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -109,6 +109,14 @@ pub mod metadata; pub mod driver; +pub mod plugin { + pub use self::registry::Registry; + + pub mod registry; + pub mod load; + pub mod build; +} + pub mod util { pub mod common; pub mod ppaux; diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index 6287683c1a1..2ff656853c3 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -198,7 +198,7 @@ pub static tag_native_libraries_lib: uint = 0x88; pub static tag_native_libraries_name: uint = 0x89; pub static tag_native_libraries_kind: uint = 0x8a; -pub static tag_macro_registrar_fn: uint = 0x8b; +pub static tag_plugin_registrar_fn: uint = 0x8b; pub static tag_exported_macros: uint = 0x8c; pub static tag_macro_def: uint = 0x8d; diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs index 38d2b7a67a0..4df21fbc974 100644 --- a/src/librustc/metadata/creader.rs +++ b/src/librustc/metadata/creader.rs @@ -21,6 +21,7 @@ use metadata::cstore::{CStore, CrateSource}; use metadata::decoder; use metadata::loader; use metadata::loader::CratePaths; +use plugin::load::PluginMetadata; use std::rc::Rc; use std::collections::HashMap; @@ -30,7 +31,6 @@ use syntax::attr; use syntax::attr::AttrMetaMethods; use syntax::codemap::{Span}; use syntax::diagnostic::SpanHandler; -use syntax::ext::base::{CrateLoader, MacroCrate}; use syntax::parse::token::InternedString; use syntax::parse::token; use syntax::crateid::CrateId; @@ -379,23 +379,21 @@ fn resolve_crate_deps(e: &mut Env, }).collect() } -pub struct Loader<'a> { +pub struct PluginMetadataReader<'a> { env: Env<'a>, } -impl<'a> Loader<'a> { - pub fn new(sess: &'a Session) -> Loader<'a> { - Loader { +impl<'a> PluginMetadataReader<'a> { + pub fn new(sess: &'a Session) -> PluginMetadataReader<'a> { + PluginMetadataReader { env: Env { sess: sess, next_crate_num: sess.cstore.next_crate_num(), } } } -} -impl<'a> CrateLoader for Loader<'a> { - fn load_crate(&mut self, krate: &ast::ViewItem) -> MacroCrate { + pub fn read_plugin_metadata(&mut self, krate: &ast::ViewItem) -> PluginMetadata { let info = extract_crate_info(&self.env, krate).unwrap(); let target_triple = self.env.sess.targ_cfg.target_strs.target_triple.as_slice(); let is_cross = target_triple != driver::host_triple(); @@ -425,8 +423,8 @@ impl<'a> CrateLoader for Loader<'a> { load_ctxt.os = config::cfg_os_to_meta_os(self.env.sess.targ_cfg.os); load_ctxt.filesearch = self.env.sess.target_filesearch(); let lib = load_ctxt.load_library_crate(); - if decoder::get_macro_registrar_fn(lib.metadata.as_slice()).is_some() { - let message = format!("crate `{}` contains a macro_registrar fn but \ + 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, driver::host_triple()); self.env.sess.span_err(krate.span, message.as_slice()); @@ -441,10 +439,10 @@ impl<'a> CrateLoader for Loader<'a> { None => { load_ctxt.report_load_errs(); unreachable!() }, }; let macros = decoder::get_exported_macros(library.metadata.as_slice()); - let registrar = decoder::get_macro_registrar_fn(library.metadata.as_slice()).map(|id| { + let registrar = decoder::get_plugin_registrar_fn(library.metadata.as_slice()).map(|id| { decoder::get_symbol(library.metadata.as_slice(), id).to_string() }); - let mc = MacroCrate { + let pc = PluginMetadata { lib: library.dylib.clone(), macros: macros.move_iter().map(|x| x.to_string()).collect(), registrar_symbol: registrar, @@ -454,6 +452,6 @@ impl<'a> CrateLoader for Loader<'a> { register_crate(&mut self.env, &None, info.ident.as_slice(), &info.crate_id, krate.span, library); } - mc + pc } } diff --git a/src/librustc/metadata/cstore.rs b/src/librustc/metadata/cstore.rs index aa8d695465a..846f879104f 100644 --- a/src/librustc/metadata/cstore.rs +++ b/src/librustc/metadata/cstore.rs @@ -139,9 +139,6 @@ impl CStore { .map(|source| source.clone()) } - pub fn dump_phase_syntax_crates(&self) { - } - pub fn reset(&self) { self.metas.borrow_mut().clear(); self.extern_mod_crate_map.borrow_mut().clear(); diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index c67b5bf1a60..8a2c3c08d41 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -1255,8 +1255,8 @@ pub fn get_native_libraries(cdata: Cmd) return result; } -pub fn get_macro_registrar_fn(data: &[u8]) -> Option { - reader::maybe_get_doc(ebml::Doc::new(data), tag_macro_registrar_fn) +pub fn get_plugin_registrar_fn(data: &[u8]) -> Option { + reader::maybe_get_doc(ebml::Doc::new(data), tag_plugin_registrar_fn) .map(|doc| FromPrimitive::from_u32(reader::doc_as_u32(doc)).unwrap()) } diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 37cb75e4697..1846c9c881b 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -1582,9 +1582,9 @@ fn encode_native_libraries(ecx: &EncodeContext, ebml_w: &mut Encoder) { ebml_w.end_tag(); } -fn encode_macro_registrar_fn(ecx: &EncodeContext, ebml_w: &mut Encoder) { - match ecx.tcx.sess.macro_registrar_fn.get() { - Some(id) => { ebml_w.wr_tagged_u32(tag_macro_registrar_fn, id); } +fn encode_plugin_registrar_fn(ecx: &EncodeContext, ebml_w: &mut Encoder) { + match ecx.tcx.sess.plugin_registrar_fn.get() { + Some(id) => { ebml_w.wr_tagged_u32(tag_plugin_registrar_fn, id); } None => {} } } @@ -1791,7 +1791,7 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, krate: &Crate) dep_bytes: u64, lang_item_bytes: u64, native_lib_bytes: u64, - macro_registrar_fn_bytes: u64, + plugin_registrar_fn_bytes: u64, macro_defs_bytes: u64, impl_bytes: u64, misc_bytes: u64, @@ -1805,7 +1805,7 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, krate: &Crate) dep_bytes: 0, lang_item_bytes: 0, native_lib_bytes: 0, - macro_registrar_fn_bytes: 0, + plugin_registrar_fn_bytes: 0, macro_defs_bytes: 0, impl_bytes: 0, misc_bytes: 0, @@ -1870,10 +1870,10 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, krate: &Crate) encode_native_libraries(&ecx, &mut ebml_w); stats.native_lib_bytes = ebml_w.writer.tell().unwrap() - i; - // Encode the macro registrar function + // Encode the plugin registrar function i = ebml_w.writer.tell().unwrap(); - encode_macro_registrar_fn(&ecx, &mut ebml_w); - stats.macro_registrar_fn_bytes = ebml_w.writer.tell().unwrap() - i; + encode_plugin_registrar_fn(&ecx, &mut ebml_w); + stats.plugin_registrar_fn_bytes = ebml_w.writer.tell().unwrap() - i; // Encode macro definitions i = ebml_w.writer.tell().unwrap(); @@ -1912,18 +1912,18 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, krate: &Crate) } println!("metadata stats:"); - println!(" attribute bytes: {}", stats.attr_bytes); - println!(" dep bytes: {}", stats.dep_bytes); - println!(" lang item bytes: {}", stats.lang_item_bytes); - println!(" native bytes: {}", stats.native_lib_bytes); - println!("macro registrar bytes: {}", stats.macro_registrar_fn_bytes); - println!(" macro def bytes: {}", stats.macro_defs_bytes); - println!(" impl bytes: {}", stats.impl_bytes); - println!(" misc bytes: {}", stats.misc_bytes); - println!(" item bytes: {}", stats.item_bytes); - println!(" index bytes: {}", stats.index_bytes); - println!(" zero bytes: {}", stats.zero_bytes); - println!(" total bytes: {}", stats.total_bytes); + println!(" attribute bytes: {}", stats.attr_bytes); + println!(" dep bytes: {}", stats.dep_bytes); + println!(" lang item bytes: {}", stats.lang_item_bytes); + println!(" native bytes: {}", stats.native_lib_bytes); + println!("plugin registrar bytes: {}", stats.plugin_registrar_fn_bytes); + println!(" macro def bytes: {}", stats.macro_defs_bytes); + println!(" impl bytes: {}", stats.impl_bytes); + println!(" misc bytes: {}", stats.misc_bytes); + println!(" item bytes: {}", stats.item_bytes); + println!(" index bytes: {}", stats.index_bytes); + println!(" zero bytes: {}", stats.zero_bytes); + println!(" total bytes: {}", stats.total_bytes); } } diff --git a/src/libsyntax/ext/registrar.rs b/src/librustc/plugin/build.rs similarity index 56% rename from src/libsyntax/ext/registrar.rs rename to src/librustc/plugin/build.rs index b76708147e1..a27632882fc 100644 --- a/src/libsyntax/ext/registrar.rs +++ b/src/librustc/plugin/build.rs @@ -8,23 +8,23 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use ast; -use attr; -use codemap::Span; -use diagnostic; -use visit; -use visit::Visitor; +use syntax::ast; +use syntax::attr; +use syntax::codemap::Span; +use syntax::diagnostic; +use syntax::visit; +use syntax::visit::Visitor; -struct MacroRegistrarContext { +struct RegistrarFinder { registrars: Vec<(ast::NodeId, Span)> , } -impl Visitor<()> for MacroRegistrarContext { +impl Visitor<()> for RegistrarFinder { fn visit_item(&mut self, item: &ast::Item, _: ()) { match item.node { ast::ItemFn(..) => { if attr::contains_name(item.attrs.as_slice(), - "macro_registrar") { + "plugin_registrar") { self.registrars.push((item.id, item.span)); } } @@ -35,20 +35,22 @@ impl Visitor<()> for MacroRegistrarContext { } } -pub fn find_macro_registrar(diagnostic: &diagnostic::SpanHandler, - krate: &ast::Crate) -> Option { - let mut ctx = MacroRegistrarContext { registrars: Vec::new() }; - visit::walk_crate(&mut ctx, krate, ()); +/// Find the function marked with `#[plugin_registrar]`, if any. +/// Used while compiling a crate which defines a registrar. +pub fn find_plugin_registrar(diagnostic: &diagnostic::SpanHandler, + krate: &ast::Crate) -> Option { + let mut finder = RegistrarFinder { registrars: Vec::new() }; + visit::walk_crate(&mut finder, krate, ()); - match ctx.registrars.len() { + match finder.registrars.len() { 0 => None, 1 => { - let (node_id, _) = ctx.registrars.pop().unwrap(); + let (node_id, _) = finder.registrars.pop().unwrap(); Some(node_id) }, _ => { - diagnostic.handler().err("multiple macro registration functions found"); - for &(_, span) in ctx.registrars.iter() { + diagnostic.handler().err("multiple plugin registration functions found"); + for &(_, span) in finder.registrars.iter() { diagnostic.span_note(span, "one is here"); } diagnostic.handler().abort_if_errors(); diff --git a/src/librustc/plugin/load.rs b/src/librustc/plugin/load.rs new file mode 100644 index 00000000000..c80f8f96c4b --- /dev/null +++ b/src/librustc/plugin/load.rs @@ -0,0 +1,131 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use plugin::registry::PluginRegistrarFun; +use driver::session::Session; +use metadata::creader::PluginMetadataReader; + +use std::mem; +use std::os; +use std::unstable::dynamic_lib::DynamicLibrary; +use syntax::ast; +use syntax::attr; +use syntax::visit; +use syntax::visit::Visitor; +use syntax::ext::expand::ExportedMacros; +use syntax::attr::AttrMetaMethods; + +pub struct PluginMetadata { + pub macros: Vec, + pub lib: Option, + pub registrar_symbol: Option, +} + +pub struct Plugins { + pub macros: Vec, + pub registrars: Vec, +} + +struct PluginLoader<'a> { + sess: &'a Session, + reader: PluginMetadataReader<'a>, + plugins: Plugins, +} + +impl<'a> PluginLoader<'a> { + fn new(sess: &'a Session) -> PluginLoader<'a> { + PluginLoader { + sess: sess, + reader: PluginMetadataReader::new(sess), + plugins: Plugins { + macros: vec!(), + registrars: vec!(), + }, + } + } +} + +pub fn load_plugins(sess: &Session, krate: &ast::Crate) -> Plugins { + let mut loader = PluginLoader::new(sess); + visit::walk_crate(&mut loader, krate, ()); + loader.plugins +} + +impl<'a> Visitor<()> for PluginLoader<'a> { + fn visit_view_item(&mut self, vi: &ast::ViewItem, _: ()) { + match vi.node { + ast::ViewItemExternCrate(name, _, _) => { + 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)"); + } + } + + if !plugin_phase { return; } + + let PluginMetadata { macros, lib, registrar_symbol } = + self.reader.read_plugin_metadata(vi); + + self.plugins.macros.push(ExportedMacros { + crate_name: name, + macros: macros, + }); + + match (lib, registrar_symbol) { + (Some(lib), Some(symbol)) + => self.dylink_registrar(vi, lib, symbol), + _ => (), + } + } + _ => (), + } + } +} + +impl<'a> PluginLoader<'a> { + // Dynamically link a registrar function into the compiler process. + fn dylink_registrar(&mut self, vi: &ast::ViewItem, path: Path, symbol: String) { + // Make sure the path contains a / or the linker will search for it. + let path = os::make_absolute(&path); + + let lib = match DynamicLibrary::open(Some(&path)) { + Ok(lib) => lib, + // this is fatal: there are almost certainly macros we need + // inside this crate, so continue would spew "macro undefined" + // errors + Err(err) => self.sess.span_fatal(vi.span, err.as_slice()) + }; + + unsafe { + let registrar: PluginRegistrarFun = + match lib.symbol(symbol.as_slice()) { + Ok(registrar) => registrar, + // again fatal if we can't register macros + Err(err) => self.sess.span_fatal(vi.span, err.as_slice()) + }; + + self.plugins.registrars.push(registrar); + + // Intentionally leak the dynamic library. We can't ever unload it + // since the library can make things that will live arbitrarily long + // (e.g. an @-box cycle or a task). + mem::forget(lib); + + } + } +} diff --git a/src/librustc/plugin/registry.rs b/src/librustc/plugin/registry.rs new file mode 100644 index 00000000000..6402b116536 --- /dev/null +++ b/src/librustc/plugin/registry.rs @@ -0,0 +1,55 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use syntax::ext::base::{SyntaxExtension, NamedSyntaxExtension, NormalTT}; +use syntax::ext::base::{IdentTT, ItemDecorator, ItemModifier, BasicMacroExpander}; +use syntax::ext::base::{MacroExpanderFn}; +use syntax::codemap::Span; +use syntax::parse::token; +use syntax::ast; + +pub struct Registry { + #[doc(hidden)] + pub krate_span: Span, + + #[doc(hidden)] + pub syntax_exts: Vec, +} + +pub type PluginRegistrarFun = + fn(&mut Registry); + +impl Registry { + #[doc(hidden)] + pub fn new(krate: &ast::Crate) -> Registry { + Registry { + krate_span: krate.span, + syntax_exts: vec!(), + } + } + + pub fn register_syntax_extension(&mut self, name: ast::Name, extension: SyntaxExtension) { + self.syntax_exts.push((name, match extension { + NormalTT(ext, _) => NormalTT(ext, Some(self.krate_span)), + IdentTT(ext, _) => IdentTT(ext, Some(self.krate_span)), + ItemDecorator(ext) => ItemDecorator(ext), + ItemModifier(ext) => ItemModifier(ext), + })); + } + + pub fn register_macro(&mut self, name: &str, expander: MacroExpanderFn) { + self.register_syntax_extension( + token::intern(name), + NormalTT(box BasicMacroExpander { + expander: expander, + span: None, + }, None)); + } +} diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 1786e5b3fd2..f848c5224b7 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -10,7 +10,6 @@ use rustc; use rustc::{driver, middle}; -use rustc::metadata::creader::Loader; use rustc::middle::privacy; use rustc::middle::lint; @@ -100,8 +99,8 @@ fn get_ast_and_resolve(cpath: &Path, libs: HashSet, cfgs: Vec) } let krate = phase_1_parse_input(&sess, cfg, &input); - let (krate, ast_map) = phase_2_configure_and_expand(&sess, &mut Loader::new(&sess), - krate, &from_str("rustdoc").unwrap()); + let (krate, ast_map) = phase_2_configure_and_expand(&sess, krate, + &from_str("rustdoc").unwrap()); let driver::driver::CrateAnalysis { exported_items, public_items, ty_cx, .. } = phase_3_run_analysis_passes(sess, &krate, ast_map); diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 745e29508d2..34363058297 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -23,7 +23,6 @@ use rustc::back::link; use rustc::driver::config; use rustc::driver::driver; use rustc::driver::session; -use rustc::metadata::creader::Loader; use syntax::ast; use syntax::codemap::{CodeMap, dummy_spanned}; use syntax::diagnostic; @@ -68,7 +67,7 @@ pub fn run(input: &str, @dummy_spanned(ast::MetaWord(cfg_)) })); let krate = driver::phase_1_parse_input(&sess, cfg, &input); - let (krate, _) = driver::phase_2_configure_and_expand(&sess, &mut Loader::new(&sess), krate, + let (krate, _) = driver::phase_2_configure_and_expand(&sess, krate, &from_str("rustdoc-test").unwrap()); let ctx = @core::DocContext { diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 521b7ee0063..e81421cff04 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -95,9 +95,6 @@ impl IdentMacroExpander for BasicIdentMacroExpander { pub type IdentMacroExpanderFn = fn(&mut ExtCtxt, Span, ast::Ident, Vec) -> Box; -pub type MacroCrateRegistrationFun = - fn(|ast::Name, SyntaxExtension|); - /// The result of a macro expansion. The return values of the various /// methods are spliced into the AST at the callsite of the macro (or /// just into the compiler's internal macro table, for `make_def`). @@ -268,6 +265,8 @@ pub enum SyntaxExtension { IdentTT(Box, Option), } +pub type NamedSyntaxExtension = (Name, SyntaxExtension); + pub struct BlockInfo { // should macros escape from this scope? pub macros_escape: bool, @@ -392,16 +391,6 @@ pub fn syntax_expander_table() -> SyntaxEnv { syntax_expanders } -pub struct MacroCrate { - pub lib: Option, - pub macros: Vec, - pub registrar_symbol: Option, -} - -pub trait CrateLoader { - fn load_crate(&mut self, krate: &ast::ViewItem) -> MacroCrate; -} - // One of these is made during expansion and incrementally updated as we go; // when a macro expansion occurs, the resulting nodes have the backtrace() // -> expn_info of their expansion context stored into their span. @@ -409,7 +398,7 @@ pub struct ExtCtxt<'a> { pub parse_sess: &'a parse::ParseSess, pub cfg: ast::CrateConfig, pub backtrace: Option<@ExpnInfo>, - pub ecfg: expand::ExpansionConfig<'a>, + pub ecfg: expand::ExpansionConfig, pub mod_path: Vec , pub trace_mac: bool, @@ -417,7 +406,7 @@ pub struct ExtCtxt<'a> { impl<'a> ExtCtxt<'a> { pub fn new<'a>(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig, - ecfg: expand::ExpansionConfig<'a>) -> ExtCtxt<'a> { + ecfg: expand::ExpansionConfig) -> ExtCtxt<'a> { ExtCtxt { parse_sess: parse_sess, cfg: cfg, diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 03001acc5d0..bb335e7bed0 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -29,10 +29,6 @@ use visit; use visit::Visitor; use util::small_vector::SmallVector; -use std::mem; -use std::os; -use std::unstable::dynamic_lib::DynamicLibrary; - pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr { match e.node { // expr_mac should really be expr_ext or something; it's the @@ -497,96 +493,6 @@ pub fn expand_item_mac(it: @ast::Item, fld: &mut MacroExpander) return items; } -// load macros from syntax-phase crates -pub fn expand_view_item(vi: &ast::ViewItem, - fld: &mut MacroExpander) - -> ast::ViewItem { - match vi.node { - ast::ViewItemExternCrate(..) => { - let should_load = vi.attrs.iter().any(|attr| { - attr.check_name("phase") && - attr.meta_item_list().map_or(false, |phases| { - attr::contains_name(phases, "syntax") - }) - }); - - if should_load { - load_extern_macros(vi, fld); - } - } - ast::ViewItemUse(_) => {} - } - - noop_fold_view_item(vi, fld) -} - -fn load_extern_macros(krate: &ast::ViewItem, fld: &mut MacroExpander) { - let MacroCrate { lib, macros, registrar_symbol } = - fld.cx.ecfg.loader.load_crate(krate); - - let crate_name = match krate.node { - ast::ViewItemExternCrate(name, _, _) => name, - _ => unreachable!() - }; - let name = format!("<{} macros>", token::get_ident(crate_name)); - let name = name.to_string(); - - for source in macros.iter() { - let item = parse::parse_item_from_source_str(name.clone(), - (*source).clone(), - fld.cx.cfg(), - fld.cx.parse_sess()) - .expect("expected a serialized item"); - expand_item_mac(item, fld); - } - - let path = match lib { - Some(path) => path, - None => return - }; - // Make sure the path contains a / or the linker will search for it. - let path = os::make_absolute(&path); - - let registrar = match registrar_symbol { - Some(registrar) => registrar, - None => return - }; - - debug!("load_extern_macros: mapped crate {} to path {} and registrar {:s}", - crate_name, path.display(), registrar); - - let lib = match DynamicLibrary::open(Some(&path)) { - Ok(lib) => lib, - // this is fatal: there are almost certainly macros we need - // inside this crate, so continue would spew "macro undefined" - // errors - Err(err) => fld.cx.span_fatal(krate.span, err.as_slice()) - }; - - unsafe { - let registrar: MacroCrateRegistrationFun = - match lib.symbol(registrar.as_slice()) { - Ok(registrar) => registrar, - // again fatal if we can't register macros - Err(err) => fld.cx.span_fatal(krate.span, err.as_slice()) - }; - registrar(|name, extension| { - let extension = match extension { - NormalTT(ext, _) => NormalTT(ext, Some(krate.span)), - IdentTT(ext, _) => IdentTT(ext, Some(krate.span)), - ItemDecorator(ext) => ItemDecorator(ext), - ItemModifier(ext) => ItemModifier(ext), - }; - fld.extsbox.insert(name, extension); - }); - - // Intentionally leak the dynamic library. We can't ever unload it - // since the library can do things that will outlive the expansion - // phase (e.g. make an @-box cycle or launch a task). - mem::forget(lib); - } -} - // expand a stmt pub fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector<@Stmt> { // why the copying here and not in expand_expr? @@ -969,10 +875,6 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> { expand_item(item, self) } - fn fold_view_item(&mut self, vi: &ast::ViewItem) -> ast::ViewItem { - expand_view_item(vi, self) - } - fn fold_stmt(&mut self, stmt: &ast::Stmt) -> SmallVector<@ast::Stmt> { expand_stmt(stmt, self) } @@ -986,14 +888,20 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> { } } -pub struct ExpansionConfig<'a> { - pub loader: &'a mut CrateLoader, +pub struct ExpansionConfig { pub deriving_hash_type_parameter: bool, pub crate_id: CrateId, } +pub struct ExportedMacros { + pub crate_name: Ident, + pub macros: Vec, +} + pub fn expand_crate(parse_sess: &parse::ParseSess, cfg: ExpansionConfig, + macros: Vec, + user_exts: Vec, c: Crate) -> Crate { let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg); let mut expander = MacroExpander { @@ -1001,6 +909,24 @@ pub fn expand_crate(parse_sess: &parse::ParseSess, cx: &mut cx, }; + for ExportedMacros { crate_name, macros } in macros.move_iter() { + let name = format!("<{} macros>", token::get_ident(crate_name)) + .into_string(); + + for source in macros.move_iter() { + let item = parse::parse_item_from_source_str(name.clone(), + source, + expander.cx.cfg(), + expander.cx.parse_sess()) + .expect("expected a serialized item"); + expand_item_mac(item, &mut expander); + } + } + + for (name, extension) in user_exts.move_iter() { + expander.extsbox.insert(name, extension); + } + let ret = expander.fold_crate(c); parse_sess.span_diagnostic.handler().abort_if_errors(); return ret; @@ -1093,7 +1019,6 @@ mod test { use attr; use codemap; use codemap::Spanned; - use ext::base::{CrateLoader, MacroCrate}; use ext::mtwt; use parse; use parse::token; @@ -1137,14 +1062,6 @@ mod test { } } - struct ErrLoader; - - impl CrateLoader for ErrLoader { - fn load_crate(&mut self, _: &ast::ViewItem) -> MacroCrate { - fail!("lolwut") - } - } - // these following tests are quite fragile, in that they don't test what // *kind* of failure occurs. @@ -1159,13 +1076,11 @@ mod test { src, Vec::new(), &sess); // should fail: - let mut loader = ErrLoader; let cfg = ::syntax::ext::expand::ExpansionConfig { - loader: &mut loader, deriving_hash_type_parameter: false, crate_id: from_str("test").unwrap(), }; - expand_crate(&sess,cfg,crate_ast); + expand_crate(&sess,cfg,vec!(),vec!(),crate_ast); } // make sure that macros can leave scope for modules @@ -1178,14 +1093,11 @@ mod test { "".to_string(), src, Vec::new(), &sess); - // should fail: - let mut loader = ErrLoader; let cfg = ::syntax::ext::expand::ExpansionConfig { - loader: &mut loader, deriving_hash_type_parameter: false, crate_id: from_str("test").unwrap(), }; - expand_crate(&sess,cfg,crate_ast); + expand_crate(&sess,cfg,vec!(),vec!(),crate_ast); } // macro_escape modules shouldn't cause macros to leave scope @@ -1198,13 +1110,11 @@ mod test { src, Vec::new(), &sess); // should fail: - let mut loader = ErrLoader; let cfg = ::syntax::ext::expand::ExpansionConfig { - loader: &mut loader, deriving_hash_type_parameter: false, crate_id: from_str("test").unwrap(), }; - expand_crate(&sess, cfg, crate_ast); + expand_crate(&sess, cfg, vec!(), vec!(), crate_ast); } #[test] fn test_contains_flatten (){ @@ -1237,13 +1147,11 @@ mod test { let ps = parse::new_parse_sess(); let crate_ast = string_to_parser(&ps, crate_str).parse_crate_mod(); // the cfg argument actually does matter, here... - let mut loader = ErrLoader; let cfg = ::syntax::ext::expand::ExpansionConfig { - loader: &mut loader, deriving_hash_type_parameter: false, crate_id: from_str("test").unwrap(), }; - expand_crate(&ps,cfg,crate_ast) + expand_crate(&ps,cfg,vec!(),vec!(),crate_ast) } //fn expand_and_resolve(crate_str: @str) -> ast::crate { diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 1ab420eb69b..7fe67ad6f87 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -74,7 +74,6 @@ pub mod ext { pub mod asm; pub mod base; pub mod expand; - pub mod registrar; pub mod quote;