Rewrite deriving(Decodable, Encodable)

Now it uses the generic deriving code and should in theory work in all cases.
This commit is contained in:
Alex Crichton 2013-05-30 16:58:16 -05:00
parent d01c7d0d42
commit a25c7045c1
3 changed files with 297 additions and 947 deletions

View File

@ -14,410 +14,150 @@
*/
use core::prelude::*;
use ast::*;
use ast;
use ast_util;
use codemap::{span, spanned};
use ext::base::ExtCtxt;
use ext::build::AstBuilder;
use ext::deriving::*;
use opt_vec;
use core::vec;
use core::uint;
pub fn expand_deriving_decodable(
cx: @ExtCtxt,
span: span,
_mitem: @meta_item,
in_items: ~[@item]
) -> ~[@item] {
expand_deriving(
cx,
span,
in_items,
expand_deriving_decodable_struct_def,
expand_deriving_decodable_enum_def
)
}
use ast::{meta_item, item, expr, m_mutbl};
use codemap::span;
use ext::base::ExtCtxt;
use ext::build::AstBuilder;
use ext::deriving::generic::*;
fn create_derived_decodable_impl(
cx: @ExtCtxt,
span: span,
type_ident: ident,
generics: &Generics,
method: @method
) -> @item {
let decoder_ty_param = cx.typaram(
cx.ident_of("__D"),
@opt_vec::with(
cx.typarambound(
cx.path_global(
span,
~[
cx.ident_of("extra"),
cx.ident_of("serialize"),
cx.ident_of("Decoder"),
]))));
// All the type parameters need to bound to the trait.
let generic_ty_params = opt_vec::with(decoder_ty_param);
let methods = [method];
let trait_path = cx.path_all(
span,
true,
~[
cx.ident_of("extra"),
cx.ident_of("serialize"),
cx.ident_of("Decodable")
],
None,
~[
cx.ty_ident(span, cx.ident_of("__D"))
pub fn expand_deriving_decodable(cx: @ExtCtxt,
span: span,
mitem: @meta_item,
in_items: ~[@item]) -> ~[@item] {
let trait_def = TraitDef {
path: Path::new_(~["extra", "serialize", "Decodable"], None,
~[~Literal(Path::new_local("__D"))], true),
additional_bounds: ~[],
generics: LifetimeBounds {
lifetimes: ~[],
bounds: ~[("__D", ~[Path::new(~["extra", "serialize", "Decoder"])])],
},
methods: ~[
MethodDef {
name: "decode",
generics: LifetimeBounds::empty(),
explicit_self: None,
args: ~[Ptr(~Literal(Path::new_local("__D")),
Borrowed(None, m_mutbl))],
ret_ty: Self,
const_nonmatching: true,
combine_substructure: decodable_substructure,
},
]
);
create_derived_impl(
cx,
span,
type_ident,
generics,
methods,
trait_path,
Generics { ty_params: generic_ty_params, lifetimes: opt_vec::Empty },
opt_vec::Empty
)
};
expand_deriving_generic(cx, span, mitem, in_items,
&trait_def)
}
// Creates a method from the given set of statements conforming to the
// signature of the `decodable` method.
fn create_decode_method(
cx: @ExtCtxt,
span: span,
type_ident: ast::ident,
generics: &Generics,
expr: @ast::expr
) -> @method {
// Create the `e` parameter.
let d_arg_type = cx.ty_rptr(
span,
cx.ty_ident(span, cx.ident_of("__D")),
None,
ast::m_mutbl
);
let d_ident = cx.ident_of("__d");
let d_arg = cx.arg(span, d_ident, d_arg_type);
// Create the type of the return value.
let output_type = create_self_type_with_params(
cx,
span,
type_ident,
generics
);
// Create the function declaration.
let inputs = ~[d_arg];
let fn_decl = cx.fn_decl(inputs, output_type);
// Create the body block.
let body_block = cx.blk_expr(expr);
// Create the method.
let explicit_self = spanned { node: sty_static, span: span };
let method_ident = cx.ident_of("decode");
@ast::method {
ident: method_ident,
attrs: ~[],
generics: ast_util::empty_generics(),
explicit_self: explicit_self,
purity: impure_fn,
decl: fn_decl,
body: body_block,
id: cx.next_id(),
span: span,
self_id: cx.next_id(),
vis: public
}
}
fn call_substructure_decode_method(
cx: @ExtCtxt,
span: span
) -> @ast::expr {
// Call the substructure method.
cx.expr_call(
span,
cx.expr_path(
cx.path_global(
span,
~[
cx.ident_of("extra"),
fn decodable_substructure(cx: @ExtCtxt, span: span,
substr: &Substructure) -> @expr {
let decoder = substr.nonself_args[0];
let recurse = ~[cx.ident_of("extra"),
cx.ident_of("serialize"),
cx.ident_of("Decodable"),
cx.ident_of("decode"),
]
)
),
~[
cx.expr_ident(span, cx.ident_of("__d"))
]
)
}
cx.ident_of("decode")];
// throw an underscore in front to suppress unused variable warnings
let blkarg = cx.ident_of("_d");
let blkdecoder = cx.expr_ident(span, blkarg);
let calldecode = cx.expr_call_global(span, recurse, ~[blkdecoder]);
let lambdadecode = cx.lambda_expr_1(span, calldecode, blkarg);
fn expand_deriving_decodable_struct_def(
cx: @ExtCtxt,
span: span,
struct_def: &struct_def,
type_ident: ident,
generics: &Generics
) -> @item {
// Create the method.
let method = expand_deriving_decodable_struct_method(
cx,
span,
struct_def,
type_ident,
generics
);
return match *substr.fields {
StaticStruct(_, ref summary) => {
let nfields = match *summary {
Left(n) => n, Right(ref fields) => fields.len()
};
let read_struct_field = cx.ident_of("read_struct_field");
// Create the implementation.
create_derived_decodable_impl(
cx,
span,
type_ident,
generics,
method
)
}
let getarg = |name: ~str, field: uint| {
cx.expr_method_call(span, blkdecoder, read_struct_field,
~[cx.expr_str(span, name),
cx.expr_uint(span, field),
lambdadecode])
};
fn expand_deriving_decodable_enum_def(
cx: @ExtCtxt,
span: span,
enum_definition: &enum_def,
type_ident: ident,
generics: &Generics
) -> @item {
// Create the method.
let method = expand_deriving_decodable_enum_method(
cx,
span,
enum_definition,
type_ident,
generics
);
let result = match *summary {
Left(n) => {
if n == 0 {
cx.expr_ident(span, substr.type_ident)
} else {
let mut fields = vec::with_capacity(n);
for uint::range(0, n) |i| {
fields.push(getarg(fmt!("_field%u", i), i));
}
cx.expr_call_ident(span, substr.type_ident, fields)
}
}
Right(ref fields) => {
let fields = do fields.mapi |i, f| {
cx.field_imm(span, *f, getarg(cx.str_of(*f), i))
};
cx.expr_struct_ident(span, substr.type_ident, fields)
}
};
// Create the implementation.
create_derived_decodable_impl(
cx,
span,
type_ident,
generics,
method
)
}
cx.expr_method_call(span, decoder, cx.ident_of("read_struct"),
~[cx.expr_str(span, cx.str_of(substr.type_ident)),
cx.expr_uint(span, nfields),
cx.lambda_expr_1(span, result, blkarg)])
}
StaticEnum(_, ref fields) => {
let variant = cx.ident_of("i");
fn create_read_struct_field(
cx: @ExtCtxt,
span: span,
idx: uint,
ident: ident
) -> ast::field {
// Call the substructure method.
let decode_expr = call_substructure_decode_method(cx, span);
let mut arms = ~[];
let mut variants = ~[];
let rvariant_arg = cx.ident_of("read_enum_variant_arg");
let d_id = cx.ident_of("__d");
for fields.eachi |i, f| {
let (name, parts) = match *f { (i, ref p) => (i, p) };
variants.push(cx.expr_str(span, cx.str_of(name)));
let call_expr = cx.expr_method_call(
span,
cx.expr_ident(span, d_id),
cx.ident_of("read_struct_field"),
~[
cx.expr_str(span, cx.str_of(ident)),
cx.expr_uint(span, idx),
cx.lambda_expr_1(span, decode_expr, d_id)
]
);
let getarg = |field: uint| {
cx.expr_method_call(span, blkdecoder, rvariant_arg,
~[cx.expr_uint(span, field),
lambdadecode])
};
cx.field_imm(span, ident, call_expr)
}
fn create_read_struct_arg(
cx: @ExtCtxt,
span: span,
idx: uint,
ident: ident
) -> ast::field {
// Call the substructure method.
let decode_expr = call_substructure_decode_method(cx, span);
let call_expr = cx.expr_method_call(
span,
cx.expr_ident(span, cx.ident_of("__d")),
cx.ident_of("read_struct_arg"),
~[
cx.expr_uint(span, idx),
cx.lambda_expr_0(span, decode_expr),
]
);
cx.field_imm(span, ident, call_expr)
}
fn expand_deriving_decodable_struct_method(
cx: @ExtCtxt,
span: span,
struct_def: &struct_def,
type_ident: ident,
generics: &Generics
) -> @method {
// Create the body of the method.
let mut i = 0;
let mut fields = ~[];
for struct_def.fields.each |struct_field| {
match struct_field.node.kind {
named_field(ident, _) => {
fields.push(create_read_struct_field(cx, span, i, ident));
}
unnamed_field => {
cx.span_unimpl(
span,
"unnamed fields with `deriving(Decodable)`"
);
let decoded = match *parts {
Left(n) => {
if n == 0 {
cx.expr_ident(span, name)
} else {
let mut fields = vec::with_capacity(n);
for uint::range(0, n) |i| {
fields.push(getarg(i));
}
cx.expr_call_ident(span, name, fields)
}
}
Right(ref fields) => {
let fields = do fields.mapi |i, f| {
cx.field_imm(span, *f, getarg(i))
};
cx.expr_struct_ident(span, name, fields)
}
};
arms.push(cx.arm(span,
~[cx.pat_lit(span, cx.expr_uint(span, i))],
decoded));
}
arms.push(cx.arm_unreachable(span));
let result = cx.expr_match(span, cx.expr_ident(span, variant), arms);
let lambda = cx.lambda_expr(span, ~[blkarg, variant], result);
let variant_vec = cx.expr_vec(span, variants);
let result = cx.expr_method_call(span, blkdecoder,
cx.ident_of("read_enum_variant"),
~[variant_vec, lambda]);
cx.expr_method_call(span, decoder, cx.ident_of("read_enum"),
~[cx.expr_str(span, cx.str_of(substr.type_ident)),
cx.lambda_expr_1(span, result, blkarg)])
}
i += 1;
}
let d_id = cx.ident_of("__d");
let read_struct_expr = cx.expr_method_call(
span,
cx.expr_ident(span, d_id),
cx.ident_of("read_struct"),
~[
cx.expr_str(span, cx.str_of(type_ident)),
cx.expr_uint(span, fields.len()),
cx.lambda_expr_1(
span,
cx.expr_struct_ident(span, type_ident, fields),
d_id)
]
);
// Create the method itself.
create_decode_method(cx, span, type_ident, generics, read_struct_expr)
}
fn create_read_variant_arg(
cx: @ExtCtxt,
span: span,
idx: uint,
variant: &ast::variant
) -> ast::arm {
// Create the matching pattern.
let pat = cx.pat_lit(span, cx.expr_uint(span, idx));
// Feed each argument in this variant to the decode function
// as well.
let variant_arg_len = variant_arg_count(cx, span, variant);
let expr = if variant_arg_len == 0 {
cx.expr_ident(span, variant.node.name)
} else {
// Feed the discriminant to the decode function.
let mut args = ~[];
for uint::range(0, variant_arg_len) |j| {
// Call the substructure method.
let expr = call_substructure_decode_method(cx, span);
let d_id = cx.ident_of("__d");
let call_expr = cx.expr_method_call(
span,
cx.expr_ident(span, d_id),
cx.ident_of("read_enum_variant_arg"),
~[
cx.expr_uint(span, j),
cx.lambda_expr_1(span, expr, d_id),
]
);
args.push(call_expr);
}
cx.expr_call_ident(span, variant.node.name, args)
_ => cx.bug("expected StaticEnum or StaticStruct in deriving(Decodable)")
};
// Create the arm.
cx.arm(span, ~[pat], expr)
}
fn create_read_enum_variant(
cx: @ExtCtxt,
span: span,
enum_definition: &enum_def
) -> @expr {
// Create a vector that contains all the variant names.
let expr_arm_names = cx.expr_vec(
span,
do enum_definition.variants.map |variant| {
cx.expr_str(
span,
cx.str_of(variant.node.name)
)
}
);
// Create the arms of the match in the method body.
let mut arms = do enum_definition.variants.mapi |i, variant| {
create_read_variant_arg(cx, span, i, variant)
};
// Add the impossible case arm.
arms.push(cx.arm_unreachable(span));
// Create the read_enum_variant expression.
cx.expr_method_call(
span,
cx.expr_ident(span, cx.ident_of("__d")),
cx.ident_of("read_enum_variant"),
~[
expr_arm_names,
cx.lambda_expr(span,
~[cx.ident_of("__d"), cx.ident_of("__i")],
cx.expr_match(span, cx.expr_ident(span, cx.ident_of("__i")), arms))
]
)
}
fn expand_deriving_decodable_enum_method(
cx: @ExtCtxt,
span: span,
enum_definition: &enum_def,
type_ident: ast::ident,
generics: &Generics
) -> @method {
let read_enum_variant_expr = create_read_enum_variant(
cx,
span,
enum_definition
);
let d_id = cx.ident_of("__d");
// Create the read_enum expression
let read_enum_expr = cx.expr_method_call(
span,
cx.expr_ident(span, d_id),
cx.ident_of("read_enum"),
~[
cx.expr_str(span, cx.str_of(type_ident)),
cx.lambda_expr_1(span, read_enum_variant_expr, d_id)
]
);
// Create the method.
create_decode_method(cx, span, type_ident, generics, read_enum_expr)
}

View File

@ -77,578 +77,113 @@ fn decode(d: &D) -> spanned<T> {
use core::prelude::*;
use ast;
use ast::*;
use ast::{meta_item, item, expr, m_imm, m_mutbl};
use codemap::span;
use ext::base::ExtCtxt;
use ext::build::AstBuilder;
use ext::deriving::*;
use codemap::{span, spanned};
use ast_util;
use opt_vec;
use ext::deriving::generic::*;
pub fn expand_deriving_encodable(
cx: @ExtCtxt,
span: span,
_mitem: @meta_item,
in_items: ~[@item]
) -> ~[@item] {
expand_deriving(
cx,
span,
in_items,
expand_deriving_encodable_struct_def,
expand_deriving_encodable_enum_def
)
}
fn create_derived_encodable_impl(
cx: @ExtCtxt,
span: span,
type_ident: ident,
generics: &Generics,
method: @method
) -> @item {
let encoder_ty_param = cx.typaram(
cx.ident_of("__E"),
@opt_vec::with(
cx.typarambound(
cx.path_global(
span,
~[
cx.ident_of("extra"),
cx.ident_of("serialize"),
cx.ident_of("Encoder"),
]))));
// All the type parameters need to bound to the trait.
let generic_ty_params = opt_vec::with(encoder_ty_param);
let methods = [method];
let trait_path = cx.path_all(
span,
true,
~[
cx.ident_of("extra"),
cx.ident_of("serialize"),
cx.ident_of("Encodable")
],
None,
~[
cx.ty_ident(span, cx.ident_of("__E"))
pub fn expand_deriving_encodable(cx: @ExtCtxt,
span: span,
mitem: @meta_item,
in_items: ~[@item]) -> ~[@item] {
let trait_def = TraitDef {
path: Path::new_(~["extra", "serialize", "Encodable"], None,
~[~Literal(Path::new_local("__E"))], true),
additional_bounds: ~[],
generics: LifetimeBounds {
lifetimes: ~[],
bounds: ~[("__E", ~[Path::new(~["extra", "serialize", "Encoder"])])],
},
methods: ~[
MethodDef {
name: "encode",
generics: LifetimeBounds::empty(),
explicit_self: Some(Some(Borrowed(None, m_imm))),
args: ~[Ptr(~Literal(Path::new_local("__E")),
Borrowed(None, m_mutbl))],
ret_ty: nil_ty(),
const_nonmatching: true,
combine_substructure: encodable_substructure,
},
]
);
create_derived_impl(
cx,
span,
type_ident,
generics,
methods,
trait_path,
Generics { ty_params: generic_ty_params, lifetimes: opt_vec::Empty },
opt_vec::Empty
)
}
// Creates a method from the given set of statements conforming to the
// signature of the `encodable` method.
fn create_encode_method(
cx: @ExtCtxt,
span: span,
statements: ~[@stmt]
) -> @method {
// Create the `e` parameter.
let e_arg_type = cx.ty_rptr(
span,
cx.ty_ident(span, cx.ident_of("__E")),
None,
ast::m_mutbl
);
let e_arg = cx.arg(span, cx.ident_of("__e"), e_arg_type);
// Create the type of the return value.
let output_type = cx.ty_nil();
// Create the function declaration.
let inputs = ~[e_arg];
let fn_decl = cx.fn_decl(inputs, output_type);
// Create the body block.
let body_block = cx.blk(span, statements, None);
// Create the method.
let explicit_self = spanned { node: sty_region(None, m_imm), span: span };
let method_ident = cx.ident_of("encode");
@ast::method {
ident: method_ident,
attrs: ~[],
generics: ast_util::empty_generics(),
explicit_self: explicit_self,
purity: impure_fn,
decl: fn_decl,
body: body_block,
id: cx.next_id(),
span: span,
self_id: cx.next_id(),
vis: public
}
}
fn call_substructure_encode_method(
cx: @ExtCtxt,
span: span,
self_field: @expr
) -> @ast::expr {
// Gather up the parameters we want to chain along.
let e_ident = cx.ident_of("__e");
let e_expr = cx.expr_ident(span, e_ident);
// Call the substructure method.
let encode_ident = cx.ident_of("encode");
cx.expr_method_call(
span,
self_field,
encode_ident,
~[e_expr]
)
}
fn expand_deriving_encodable_struct_def(
cx: @ExtCtxt,
span: span,
struct_def: &struct_def,
type_ident: ident,
generics: &Generics
) -> @item {
// Create the method.
let method = expand_deriving_encodable_struct_method(
cx,
span,
type_ident,
struct_def
);
// Create the implementation.
create_derived_encodable_impl(
cx,
span,
type_ident,
generics,
method
)
}
fn expand_deriving_encodable_enum_def(
cx: @ExtCtxt,
span: span,
enum_definition: &enum_def,
type_ident: ident,
generics: &Generics
) -> @item {
// Create the method.
let method = expand_deriving_encodable_enum_method(
cx,
span,
type_ident,
enum_definition
);
// Create the implementation.
create_derived_encodable_impl(
cx,
span,
type_ident,
generics,
method
)
}
fn expand_deriving_encodable_struct_method(
cx: @ExtCtxt,
span: span,
type_ident: ident,
struct_def: &struct_def
) -> @method {
// Create the body of the method.
let mut idx = 0;
let mut statements = ~[];
for struct_def.fields.each |struct_field| {
match struct_field.node.kind {
named_field(ident, _) => {
// Create the accessor for this field.
let self_field = cx.expr_field_access(span,
cx.expr_self(span),
ident);
// Call the substructure method.
let encode_expr = call_substructure_encode_method(
cx,
span,
self_field
);
let e_ident = cx.ident_of("__e");
let call_expr = cx.expr_method_call(
span,
cx.expr_ident(span, e_ident),
cx.ident_of("emit_struct_field"),
~[
cx.expr_str(span, cx.str_of(ident)),
cx.expr_uint(span, idx),
cx.lambda_expr_1(span, encode_expr, e_ident)
]
);
statements.push(cx.stmt_expr(call_expr));
}
unnamed_field => {
cx.span_unimpl(
span,
"unnamed fields with `deriving(Encodable)`"
);
}
}
idx += 1;
}
let e_id = cx.ident_of("__e");
let emit_struct_stmt = cx.expr_method_call(
span,
cx.expr_ident(span, e_id),
cx.ident_of("emit_struct"),
~[
cx.expr_str(span, cx.str_of(type_ident)),
cx.expr_uint(span, statements.len()),
cx.lambda_stmts_1(span, statements, e_id),
]
);
let statements = ~[cx.stmt_expr(emit_struct_stmt)];
// Create the method itself.
return create_encode_method(cx, span, statements);
}
fn expand_deriving_encodable_enum_method(
cx: @ExtCtxt,
span: span,
type_ident: ast::ident,
enum_definition: &enum_def
) -> @method {
// Create the arms of the match in the method body.
let arms = do enum_definition.variants.mapi |i, variant| {
// Create the matching pattern.
let (pat, fields) = create_enum_variant_pattern(cx, span, variant, "__self", ast::m_imm);
// Feed the discriminant to the encode function.
let mut stmts = ~[];
// Feed each argument in this variant to the encode function
// as well.
let variant_arg_len = variant_arg_count(cx, span, variant);
for fields.eachi |j, &(_, field)| {
// Call the substructure method.
let expr = call_substructure_encode_method(cx, span, field);
let e_ident = cx.ident_of("__e");
let call_expr = cx.expr_method_call(
span,
cx.expr_ident(span, e_ident),
cx.ident_of("emit_enum_variant_arg"),
~[
cx.expr_uint(span, j),
cx.lambda_expr_1(span, expr, e_ident),
]
);
stmts.push(cx.stmt_expr(call_expr));
}
// Create the pattern body.
let e_id = cx.ident_of("__e");
let call_expr = cx.expr_method_call(
span,
cx.expr_ident(span, e_id),
cx.ident_of("emit_enum_variant"),
~[
cx.expr_str(span, cx.str_of(variant.node.name)),
cx.expr_uint(span, i),
cx.expr_uint(span, variant_arg_len),
cx.lambda_stmts_1(span, stmts, e_id)
]
);
//let match_body_block = cx.blk_expr(call_expr);
// Create the arm.
cx.arm(span, ~[pat], call_expr) //match_body_block)
};
let e_ident = cx.ident_of("__e");
// Create the method body.
let lambda_expr = cx.lambda_expr_1(
span,
expand_enum_or_struct_match(cx, span, arms),
e_ident);
let call_expr = cx.expr_method_call(
span,
cx.expr_ident(span, e_ident),
cx.ident_of("emit_enum"),
~[
cx.expr_str(span, cx.str_of(type_ident)),
lambda_expr,
]
);
let stmt = cx.stmt_expr(call_expr);
// Create the method.
create_encode_method(cx, span, ~[stmt])
expand_deriving_generic(cx, span, mitem, in_items,
&trait_def)
}
#[cfg(test)]
mod test {
extern mod extra;
use core::option::{None, Some};
use extra::serialize::Encodable;
use extra::serialize::Encoder;
fn encodable_substructure(cx: @ExtCtxt, span: span,
substr: &Substructure) -> @expr {
let encoder = substr.nonself_args[0];
// throw an underscore in front to suppress unused variable warnings
let blkarg = cx.ident_of("_e");
let blkencoder = cx.expr_ident(span, blkarg);
let encode = cx.ident_of("encode");
// just adding the ones I want to test, for now:
#[deriving(Eq)]
pub enum call {
CallToEmitEnum(~str),
CallToEmitEnumVariant(~str, uint, uint),
CallToEmitEnumVariantArg(uint),
CallToEmitUint(uint),
CallToEmitNil,
CallToEmitStruct(~str,uint),
CallToEmitField(~str,uint),
CallToEmitOption,
CallToEmitOptionNone,
CallToEmitOptionSome,
// all of the ones I was too lazy to handle:
CallToOther
}
// using `@mut` rather than changing the
// type of self in every method of every encoder everywhere.
pub struct TestEncoder {call_log : @mut ~[call]}
return match *substr.fields {
Struct(ref fields) => {
let emit_struct_field = cx.ident_of("emit_struct_field");
let mut stmts = ~[];
for fields.eachi |i, f| {
let (name, val) = match *f {
(Some(id), e, _) => (cx.str_of(id), e),
(None, e, _) => (fmt!("_field%u", i), e)
};
let enc = cx.expr_method_call(span, val, encode, ~[blkencoder]);
let lambda = cx.lambda_expr_1(span, enc, blkarg);
let call = cx.expr_method_call(span, blkencoder,
emit_struct_field,
~[cx.expr_str(span, name),
cx.expr_uint(span, i),
lambda]);
stmts.push(cx.stmt_expr(call));
}
pub impl TestEncoder {
// these self's should be &mut self's, as well....
fn add_to_log (&self, c : call) {
self.call_log.push(copy c);
}
fn add_unknown_to_log (&self) {
self.add_to_log (CallToOther)
}
}
impl Encoder for TestEncoder {
fn emit_nil(&mut self) { self.add_to_log(CallToEmitNil) }
fn emit_uint(&mut self, v: uint) {
self.add_to_log(CallToEmitUint(v));
}
fn emit_u64(&mut self, _v: u64) { self.add_unknown_to_log(); }
fn emit_u32(&mut self, _v: u32) { self.add_unknown_to_log(); }
fn emit_u16(&mut self, _v: u16) { self.add_unknown_to_log(); }
fn emit_u8(&mut self, _v: u8) { self.add_unknown_to_log(); }
fn emit_int(&mut self, _v: int) { self.add_unknown_to_log(); }
fn emit_i64(&mut self, _v: i64) { self.add_unknown_to_log(); }
fn emit_i32(&mut self, _v: i32) { self.add_unknown_to_log(); }
fn emit_i16(&mut self, _v: i16) { self.add_unknown_to_log(); }
fn emit_i8(&mut self, _v: i8) { self.add_unknown_to_log(); }
fn emit_bool(&mut self, _v: bool) { self.add_unknown_to_log(); }
fn emit_f64(&mut self, _v: f64) { self.add_unknown_to_log(); }
fn emit_f32(&mut self, _v: f32) { self.add_unknown_to_log(); }
fn emit_float(&mut self, _v: float) { self.add_unknown_to_log(); }
fn emit_char(&mut self, _v: char) { self.add_unknown_to_log(); }
fn emit_str(&mut self, _v: &str) { self.add_unknown_to_log(); }
fn emit_enum(&mut self, name: &str, f: &fn(&mut TestEncoder)) {
self.add_to_log(CallToEmitEnum(name.to_str()));
f(self);
let blk = cx.lambda_stmts_1(span, stmts, blkarg);
cx.expr_method_call(span, encoder, cx.ident_of("emit_struct"),
~[cx.expr_str(span, cx.str_of(substr.type_ident)),
cx.expr_uint(span, fields.len()),
blk])
}
fn emit_enum_variant(&mut self,
name: &str,
id: uint,
cnt: uint,
f: &fn(&mut TestEncoder)) {
self.add_to_log(CallToEmitEnumVariant(name.to_str(), id, cnt));
f(self);
EnumMatching(idx, variant, ref fields) => {
// We're not generating an AST that the borrow checker is expecting,
// so we need to generate a unique local variable to take the
// mutable loan out on, otherwise we get conflicts which don't
// actually exist.
let me = cx.stmt_let(span, false, blkarg, encoder);
let encoder = cx.expr_ident(span, blkarg);
let emit_variant_arg = cx.ident_of("emit_enum_variant_arg");
let mut stmts = ~[];
for fields.eachi |i, f| {
let val = match *f { (_, e, _) => e };
let enc = cx.expr_method_call(span, val, encode, ~[blkencoder]);
let lambda = cx.lambda_expr_1(span, enc, blkarg);
let call = cx.expr_method_call(span, blkencoder,
emit_variant_arg,
~[cx.expr_uint(span, i),
lambda]);
stmts.push(cx.stmt_expr(call));
}
let blk = cx.lambda_stmts_1(span, stmts, blkarg);
let name = cx.expr_str(span, cx.str_of(variant.node.name));
let call = cx.expr_method_call(span, blkencoder,
cx.ident_of("emit_enum_variant"),
~[name,
cx.expr_uint(span, idx),
cx.expr_uint(span, fields.len()),
blk]);
let blk = cx.lambda_expr_1(span, call, blkarg);
let ret = cx.expr_method_call(span, encoder,
cx.ident_of("emit_enum"),
~[cx.expr_str(span,
cx.str_of(substr.type_ident)),
blk]);
cx.expr_blk(cx.blk(span, ~[me], Some(ret)))
}
fn emit_enum_variant_arg(&mut self,
idx: uint,
f: &fn(&mut TestEncoder)) {
self.add_to_log(CallToEmitEnumVariantArg(idx));
f(self);
}
fn emit_enum_struct_variant(&mut self,
name: &str,
id: uint,
cnt: uint,
f: &fn(&mut TestEncoder)) {
self.emit_enum_variant(name, id, cnt, f)
}
fn emit_enum_struct_variant_field(&mut self,
_name: &str,
idx: uint,
f: &fn(&mut TestEncoder)) {
self.emit_enum_variant_arg(idx, f)
}
fn emit_struct(&mut self,
name: &str,
len: uint,
f: &fn(&mut TestEncoder)) {
self.add_to_log(CallToEmitStruct (name.to_str(),len));
f(self);
}
fn emit_struct_field(&mut self,
name: &str,
idx: uint,
f: &fn(&mut TestEncoder)) {
self.add_to_log(CallToEmitField (name.to_str(),idx));
f(self);
}
fn emit_tuple(&mut self, _len: uint, f: &fn(&mut TestEncoder)) {
self.add_unknown_to_log();
f(self);
}
fn emit_tuple_arg(&mut self, _idx: uint, f: &fn(&mut TestEncoder)) {
self.add_unknown_to_log();
f(self);
}
fn emit_tuple_struct(&mut self,
_name: &str,
_len: uint,
f: &fn(&mut TestEncoder)) {
self.add_unknown_to_log();
f(self);
}
fn emit_tuple_struct_arg(&mut self,
_idx: uint,
f: &fn(&mut TestEncoder)) {
self.add_unknown_to_log();
f(self);
}
fn emit_option(&mut self, f: &fn(&mut TestEncoder)) {
self.add_to_log(CallToEmitOption);
f(self);
}
fn emit_option_none(&mut self) {
self.add_to_log(CallToEmitOptionNone);
}
fn emit_option_some(&mut self, f: &fn(&mut TestEncoder)) {
self.add_to_log(CallToEmitOptionSome);
f(self);
}
fn emit_seq(&mut self, _len: uint, f: &fn(&mut TestEncoder)) {
self.add_unknown_to_log();
f(self);
}
fn emit_seq_elt(&mut self, _idx: uint, f: &fn(&mut TestEncoder)) {
self.add_unknown_to_log();
f(self);
}
fn emit_map(&mut self, _len: uint, f: &fn(&mut TestEncoder)) {
self.add_unknown_to_log();
f(self);
}
fn emit_map_elt_key(&mut self, _idx: uint, f: &fn(&mut TestEncoder)) {
self.add_unknown_to_log();
f(self);
}
fn emit_map_elt_val(&mut self, _idx: uint, f: &fn(&mut TestEncoder)) {
self.add_unknown_to_log();
f(self);
}
}
fn to_call_log<E:Encodable<TestEncoder>>(val: E) -> ~[call] {
let mut te = TestEncoder {
call_log: @mut ~[]
};
val.encode(&mut te);
copy *te.call_log
}
#[deriving(Encodable)]
enum Written {
Book(uint,uint),
Magazine(~str)
}
#[test]
fn test_encode_enum() {
assert_eq!(
to_call_log(Book(34,44)),
~[
CallToEmitEnum(~"Written"),
CallToEmitEnumVariant(~"Book",0,2),
CallToEmitEnumVariantArg(0),
CallToEmitUint(34),
CallToEmitEnumVariantArg(1),
CallToEmitUint(44),
]
);
}
pub struct BPos(uint);
#[deriving(Encodable)]
pub struct HasPos { pos : BPos }
#[test]
fn test_encode_newtype() {
assert_eq!(
to_call_log(HasPos { pos:BPos(48) }),
~[
CallToEmitStruct(~"HasPos",1),
CallToEmitField(~"pos",0),
CallToEmitUint(48),
]
);
}
#[test]
fn test_encode_option() {
let mut v = None;
assert_eq!(
to_call_log(v),
~[
CallToEmitOption,
CallToEmitOptionNone,
]
);
v = Some(54u);
assert_eq!(
to_call_log(v),
~[
CallToEmitOption,
CallToEmitOptionSome,
CallToEmitUint(54)
]
);
}
_ => cx.bug("expected Struct or EnumMatching in deriving(Encodable)")
};
}

View File

@ -0,0 +1,75 @@
// Copyright 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// This actually tests a lot more than just encodable/decodable, but it gets the
// job done at least
extern mod extra;
use std::io;
use std::rand::{random, Rand};
use extra::serialize::*;
use extra::ebml;
use extra::ebml::writer::Encoder;
use extra::ebml::reader::Decoder;
#[deriving(Encodable, Decodable, Eq, Rand)]
struct A;
#[deriving(Encodable, Decodable, Eq, Rand)]
struct B(int);
#[deriving(Encodable, Decodable, Eq, Rand)]
struct C(int, int, uint);
#[deriving(Encodable, Decodable, Eq, Rand)]
struct D {
a: int,
b: uint,
}
#[deriving(Encodable, Decodable, Eq, Rand)]
enum E {
E1,
E2(uint),
E3(D),
E4{ x: uint },
}
#[deriving(Encodable, Decodable, Eq, Rand)]
enum F { F1 }
#[deriving(Encodable, Decodable, Eq, Rand)]
struct G<T> {
t: T
}
fn roundtrip<T: Rand + Eq + Encodable<Encoder> + Decodable<Decoder>>() {
let obj: T = random();
let bytes = do io::with_bytes_writer |w| {
let mut e = Encoder(w);
obj.encode(&mut e);
};
let doc = ebml::reader::Doc(@bytes);
let mut dec = Decoder(doc);
let obj2 = Decodable::decode(&mut dec);
assert!(obj == obj2);
}
pub fn main() {
roundtrip::<A>();
roundtrip::<B>();
roundtrip::<C>();
roundtrip::<D>();
for 20.times {
roundtrip::<E>();
roundtrip::<F>();
roundtrip::<G<int>>();
}
}