diff --git a/components/plugins/jstraceable.rs b/components/plugins/jstraceable.rs new file mode 100644 index 00000000000..5a113ab1691 --- /dev/null +++ b/components/plugins/jstraceable.rs @@ -0,0 +1,76 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use syntax::ext::base::ExtCtxt; +use syntax::codemap::Span; +use syntax::ptr::P; +use syntax::ast::{Item, MetaItem, Expr}; +use syntax::ast; +use syntax::ext::build::AstBuilder; +use syntax::ext::deriving::generic::{combine_substructure, EnumMatching, FieldInfo, MethodDef, Struct, Substructure, TraitDef, ty}; + + pub fn expand_jstraceable(cx: &mut ExtCtxt, span: Span, mitem: &MetaItem, item: &Item, push: |P|) { + let trait_def = TraitDef { + span: span, + attributes: Vec::new(), + path: ty::Path::new(vec!("dom","bindings","trace","JSTraceable")), + additional_bounds: Vec::new(), + generics: ty::LifetimeBounds::empty(), + methods: vec!( + MethodDef { + name: "trace", + generics: ty::LifetimeBounds::empty(), + explicit_self: ty::borrowed_explicit_self(), + args: vec!(ty::Ptr(box ty::Literal(ty::Path::new(vec!("js","jsapi","JSTracer"))), ty::Raw(ast::MutMutable))), + ret_ty: ty::nil_ty(), + attributes: vec!(), + combine_substructure: combine_substructure(|a, b, c| { + jstraceable_substructure(a, b, c) + }) + } + ) + }; + trait_def.expand(cx, mitem, item, push) +} + +// Mostly copied from syntax::ext::deriving::hash +fn jstraceable_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P { + let state_expr = match substr.nonself_args { + [ref state_expr] => state_expr, + _ => cx.span_bug(trait_span, "incorrect number of arguments in `jstraceable`") + }; + let hash_ident = substr.method_ident; + let call_hash = |span, thing_expr| { + let expr = cx.expr_method_call(span, thing_expr, hash_ident, vec!(state_expr.clone())); + cx.stmt_expr(expr) + }; + let mut stmts = Vec::new(); + + let fields = match *substr.fields { + Struct(ref fs) => fs, + EnumMatching(index, variant, ref fs) => { + // Determine the discriminant. We will feed this value to the byte + // iteration function. + let discriminant = match variant.node.disr_expr { + Some(ref d) => d.clone(), + None => cx.expr_uint(trait_span, index) + }; + + stmts.push(call_hash(trait_span, discriminant)); + + fs + } + _ => cx.span_bug(trait_span, "impossible substructure in `jstraceable`") + }; + + for &FieldInfo { ref self_, span, .. } in fields.iter() { + stmts.push(call_hash(span, self_.clone())); + } + + if stmts.len() == 0 { + cx.span_bug(trait_span, "#[jstraceable] needs at least one field"); + } + + cx.expr_block(cx.block(trait_span, stmts, None)) +} diff --git a/components/plugins/lib.rs b/components/plugins/lib.rs index 3247ad383ed..741a82ee045 100644 --- a/components/plugins/lib.rs +++ b/components/plugins/lib.rs @@ -6,8 +6,6 @@ #![deny(unused_imports, unused_variable)] - - #[phase(plugin,link)] extern crate syntax; #[phase(plugin, link)] @@ -17,14 +15,18 @@ extern crate sync; use rustc::lint::LintPassObject; use rustc::plugin::Registry; +use syntax::ext::base::ItemDecorator; + +use syntax::parse::token::intern; mod lints; mod macros; - +mod jstraceable; #[plugin_registrar] pub fn plugin_registrar(reg: &mut Registry) { reg.register_lint_pass(box lints::TransmutePass as LintPassObject); reg.register_lint_pass(box lints::UnrootedPass as LintPassObject); + reg.register_syntax_extension(intern("jstraceable"), ItemDecorator(box jstraceable::expand_jstraceable)) }