1
+ /*
2
+
3
+ The compiler code necessary to implement the #[auto_serialize]
4
+ extension. The idea here is that type-defining items may be tagged
5
+ with #[auto_serialize], which will cause us to generate a little
6
+ companion module with the same name as the item.
7
+
8
+ For example, a type like:
9
+
10
+ type node_id = uint;
11
+
12
+ would generate a companion module like:
13
+
14
+ mod node_id {
15
+ use std;
16
+ import std::serialization::serializer;
17
+ import std::serialization::deserializer;
18
+ fn serialize<S: serializer>(s: S, v: node_id) {
19
+ s.emit_uint(v);
20
+ }
21
+ fn deserializer<D: deserializer>(d: D) -> node_id {
22
+ d.read_uint()
23
+ }
24
+ }
25
+
26
+ Other interesting scenarios are whe the item has type parameters or
27
+ references other non-built-in types. A type definition like:
28
+
29
+ type spanned<T> = {node: T, span: span};
30
+
31
+ would yield a helper module like:
32
+
33
+ mod spanned {
34
+ use std;
35
+ import std::serialization::serializer;
36
+ import std::serialization::deserializer;
37
+ fn serialize<S: serializer,T>(s: S, t: fn(T), v: spanned<T>) {
38
+ s.emit_rec(2u) {||
39
+ s.emit_rec_field("node", 0u) {||
40
+ t(s.node);
41
+ };
42
+ s.emit_rec_field("span", 1u) {||
43
+ span::serialize(s, s.span);
44
+ };
45
+ }
46
+ }
47
+ fn deserializer<D: deserializer>(d: D, t: fn() -> T) -> node_id {
48
+ d.read_rec(2u) {||
49
+ {node: d.read_rec_field("node", 0u, t),
50
+ span: d.read_rec_field("span", 1u) {||span::deserialize(d)}}
51
+ }
52
+ }
53
+ }
54
+
55
+ In general, the code to serialize an instance `v` of a non-built-in
56
+ type a::b::c<T0,...,Tn> looks like:
57
+
58
+ a::b::c::serialize(s, {|v| c_T0}, ..., {|v| c_Tn}, v)
59
+
60
+ where `c_Ti` is the code to serialize an instance `v` of the type
61
+ `Ti`.
62
+
63
+ Similarly, the code to deserialize an instance of a non-built-in type
64
+ `a::b::c<T0,...,Tn>` using the deserializer `d` looks like:
65
+
66
+ a::b::c::deserialize(d, {|| c_T0}, ..., {|| c_Tn})
67
+
68
+ where `c_Ti` is the code to deserialize an instance of `Ti` using the
69
+ deserializer `d`.
70
+
71
+ TODO--Hygiene. Search for "__" strings.
72
+
73
+ */
74
+ import base:: * ;
75
+ import driver:: session:: session;
76
+ import codemap:: span;
77
+ import std:: map;
78
+
79
+ export expand_auto_serialize;
80
+
81
+ enum ser_cx = {
82
+ ext_cx: ext_ctxt,
83
+ tps: map:: map<str, fn @( @ast:: expr) -> [ @ast:: stmt] >
84
+ } ;
85
+
86
+ fn expand_auto_serialize ( cx : ext_ctxt ,
87
+ span : span ,
88
+ mitem : ast:: meta_item ,
89
+ in_items : [ @ast:: item ] ) -> [ @ast:: item ] {
90
+ vec:: flat_map ( in_items) { |in_item|
91
+ alt in_item. node {
92
+ ast:: item_ty ( ty, tps) {
93
+ [ in_item, ty_module ( cx, in_item. ident , ty, tps) ]
94
+ }
95
+
96
+ ast:: item_enum ( variants, tps) {
97
+ [ in_item, enum_module ( cx, in_item. ident , variants, tps) ]
98
+ }
99
+
100
+ _ {
101
+ cx. session ( ) . span_err ( span, "#[auto_serialize] can only be \
102
+ applied to type and enum \
103
+ definitions") ;
104
+ [ in_item]
105
+ }
106
+ }
107
+ }
108
+ }
109
+
110
+ impl helpers for ser_cx {
111
+ fn session ( ) -> session { self . ext_cx . session ( ) }
112
+
113
+ fn next_id ( ) -> ast:: node_id { self . session ( ) . next_node_id ( ) }
114
+
115
+ fn path ( span : span , strs : [ str ] ) -> @ast:: path {
116
+ @{ node : { global : false ,
117
+ idents : strs + [ "serialize" ] ,
118
+ types : [ ] } ,
119
+ span: span}
120
+ }
121
+
122
+ fn expr ( span : span , node : ast:: expr_ ) -> @ast:: expr {
123
+ @{ id: self . next_id ( ) , node: node, span: span}
124
+ }
125
+
126
+ fn ty_path ( span : span , strs : [ str ] ) -> @ast:: ty {
127
+ @{ node: ast:: ty_path ( self . path ( span, strs) , self . next_id ( ) ) ,
128
+ span: span}
129
+ }
130
+
131
+ fn var_ref ( span : span , name : str ) -> @ast:: expr {
132
+ self . expr ( span, ast:: expr_path ( self . path ( span, [ name] ) ) )
133
+ }
134
+
135
+ fn blk ( span : span , stmts : [ @ast:: stmt ] ) -> ast:: blk {
136
+ { node : { view_items : [ ] ,
137
+ stmts : stmts,
138
+ expr : none,
139
+ id : self . next_id ( ) ,
140
+ rules : ast:: default_blk} ,
141
+ span: span}
142
+ }
143
+
144
+ fn binder_pat ( span : span , nm : str ) -> @ast:: pat {
145
+ let path = @{ node : { global : false ,
146
+ idents : [ nm] ,
147
+ types : [ ] } ,
148
+ span: span} ;
149
+ @{ id: self . next_id ( ) ,
150
+ node: ast:: pat_ident ( path, none) ,
151
+ span: span}
152
+ }
153
+
154
+ fn stmt ( expr : @ast:: expr ) -> @ast:: stmt {
155
+ @{ node: ast:: stmt_semi ( expr, self . next_id ( ) ) ,
156
+ span: expr. span }
157
+ }
158
+
159
+ fn alt_stmt ( arms : [ ast:: arm ] , span : span , -v : @ast:: expr ) -> @ast:: stmt {
160
+ self . stmt (
161
+ self . expr (
162
+ span,
163
+ ast:: expr_alt ( v, arms, ast:: alt_exhaustive) ) )
164
+ }
165
+
166
+ fn clone ( v : @ast:: expr ) -> @ast:: expr {
167
+ let fld = fold:: make_fold ( {
168
+ new_id: { |_id| self . next_id ( ) }
169
+ with * fold:: default_ast_fold ( )
170
+ } ) ;
171
+ fld. fold_expr ( v)
172
+ }
173
+
174
+ fn clone_ty_param ( v : ast:: ty_param ) -> ast:: ty_param {
175
+ let fld = fold:: make_fold ( {
176
+ new_id: { |_id| self . next_id ( ) }
177
+ with * fold:: default_ast_fold ( )
178
+ } ) ;
179
+ fold:: fold_ty_param ( v, fld)
180
+ }
181
+
182
+ fn at ( span : span , expr : @ast:: expr ) -> @ast:: expr {
183
+ fn repl_sp ( old_span : span , repl_span : span , with_span : span ) -> span {
184
+ if old_span == repl_span {
185
+ with_span
186
+ } else {
187
+ old_span
188
+ }
189
+ }
190
+
191
+ let fld = fold:: make_fold ( {
192
+ new_span: repl_sp ( _, ast_util:: dummy_sp ( ) , span)
193
+ with * fold:: default_ast_fold ( )
194
+ } ) ;
195
+
196
+ fld. fold_expr ( expr)
197
+ }
198
+ }
199
+
200
+ fn serialize_path ( cx : ser_cx , path : @ast:: path , -s : @ast:: expr , -v : @ast:: expr )
201
+ -> [ @ast:: stmt ] {
202
+ let ext_cx = cx. ext_cx ;
203
+
204
+ // We want to take a path like a::b::c<...> and generate a call
205
+ // like a::b::c::serialize(s, ...), as described above.
206
+
207
+ let callee =
208
+ cx. expr (
209
+ path. span ,
210
+ ast:: expr_path (
211
+ cx. path ( path. span , path. node . idents + [ "serialize" ] ) ) ) ;
212
+
213
+ let ty_args = vec:: map ( path. node . types ) { |ty|
214
+ let sv = serialize_ty ( cx, ty, s, #ast ( expr) { "__v" } ) ;
215
+ cx. at ( ty. span , #ast ( expr) { "{|__v| $(sv)}" } )
216
+ } ;
217
+
218
+ [ cx. stmt (
219
+ cx. expr (
220
+ path. span ,
221
+ ast:: expr_call ( callee, [ s] + ty_args + [ v] , false ) ) ) ]
222
+ }
223
+
224
+ fn serialize_variant ( cx : ser_cx ,
225
+ tys : [ @ast:: ty ] ,
226
+ span : span ,
227
+ -s : @ast:: expr ,
228
+ pfn : fn ( [ @ast:: pat ] ) -> ast:: pat_ ) -> ast:: arm {
229
+ let vnames = vec:: init_fn ( vec:: len ( tys) ) { |i| #fmt[ "__v%u" , i] } ;
230
+ let pats = vec:: init_fn ( vec:: len ( tys) ) { |i|
231
+ cx. binder_pat ( tys[ i] . span , vnames[ i] )
232
+ } ;
233
+ let pat: @ast:: pat = @{ id: cx. next_id ( ) , node: pfn ( pats) , span: span} ;
234
+ let stmts = vec:: init_fn ( vec:: len ( tys) ) { |i|
235
+ let v = cx. var_ref ( span, vnames[ i] ) ;
236
+ serialize_ty ( cx, tys[ i] , cx. clone ( s) , v)
237
+ } ;
238
+ { pats: [ pat] , guard: none, body: cx. blk ( span, vec:: concat ( stmts) ) }
239
+ }
240
+
241
+ fn serialize_ty ( cx : ser_cx , ty : @ast:: ty , -s : @ast:: expr , -v : @ast:: expr )
242
+ -> [ @ast:: stmt ] {
243
+ let ext_cx = cx. ext_cx ;
244
+
245
+ alt ty. node {
246
+ ast:: ty_nil | ast:: ty_bot {
247
+ [ ]
248
+ }
249
+
250
+ ast:: ty_box ( mt) |
251
+ ast:: ty_uniq ( mt) |
252
+ ast:: ty_ptr ( mt) {
253
+ serialize_ty ( cx, mt. ty , s, #ast ( expr) { "*$(v)" } )
254
+ }
255
+
256
+ ast:: ty_rec ( flds) {
257
+ vec:: flat_map ( flds) { |fld|
258
+ let vf = cx. expr (
259
+ fld. span ,
260
+ ast:: expr_field ( cx. clone ( v) , fld. node . ident , [ ] ) ) ;
261
+ serialize_ty ( cx, fld. node . mt . ty , cx. clone ( s) , vf)
262
+ }
263
+ }
264
+
265
+ ast:: ty_fn ( _, _) {
266
+ cx. session ( ) . span_err (
267
+ ty. span , #fmt[ "Cannot serialize function types" ] ) ;
268
+ [ ]
269
+ }
270
+
271
+ ast:: ty_tup ( tys) {
272
+ // Generate code like
273
+ //
274
+ // alt v {
275
+ // (v1, v2, v3) {
276
+ // .. serialize v1, v2, v3 ..
277
+ // }
278
+ // };
279
+
280
+ let arms = [
281
+ serialize_variant ( cx, tys, ty. span , s,
282
+ { |pats| ast:: pat_tup ( pats) } )
283
+ ] ;
284
+ [ cx. alt_stmt ( arms, ty. span , v) ]
285
+ }
286
+
287
+ ast:: ty_path ( path, _) {
288
+ if vec:: len ( path. node . idents ) == 1 u &&
289
+ vec:: is_empty ( path. node . types ) {
290
+ let ident = path. node . idents [ 0 ] ;
291
+
292
+ alt cx. tps . find ( ident) {
293
+ some ( f) { f ( v) }
294
+ none { serialize_path( cx, path, s, v) }
295
+ }
296
+ } else {
297
+ serialize_path( cx, path, s, v)
298
+ }
299
+ }
300
+
301
+ ast:: ty_constr ( ty, _) {
302
+ serialize_ty ( cx, ty, s, v)
303
+ }
304
+
305
+ ast:: ty_mac ( _) {
306
+ cx. session ( ) . span_err (
307
+ ty. span , #fmt[ "Cannot serialize macro types" ] ) ;
308
+ [ ]
309
+ }
310
+
311
+ ast:: ty_infer {
312
+ cx. session ( ) . span_err (
313
+ ty. span , #fmt[ "Cannot serialize inferred types" ] ) ;
314
+ [ ]
315
+ }
316
+
317
+ ast:: ty_vec ( mt) {
318
+ let ser_e =
319
+ cx. expr (
320
+ ty. span ,
321
+ expr_block (
322
+ cx. blk (
323
+ ty. span ,
324
+ serialize_ty (
325
+ cx, mt. ty ,
326
+ cx. clone ( s) ,
327
+ cx. at (
328
+ ty. span ,
329
+ #ast ( expr) { __e} ) ) ) ) ) ;
330
+ [ #ast ( stmt) { $( s) . emit_from_vec ( $( v) , { |__e| $( ser_e) } ) } ]
331
+ }
332
+ }
333
+ }
334
+
335
+ fn ty_module ( ext_cx : ext_ctxt , name : str , -ty : @ast:: ty , tps : [ ast:: ty_param ] )
336
+ -> @ast:: item {
337
+
338
+ let cx = ser_cx ( { ext_cx: ext_cx, tps: map:: new_str_hash ( ) } ) ;
339
+
340
+ let ser_inputs: [ ast:: arg ] =
341
+ [ { mode: ast:: expl ( ast:: by_ref) ,
342
+ ty: cx. ty_path ( ty. span , [ "__S" ] ) ,
343
+ ident: "__s" ,
344
+ id: cx. next_id ( ) } ,
345
+ { mode: ast:: expl ( ast:: by_ref) ,
346
+ ty: ty,
347
+ ident: "__v" ,
348
+ id: cx. next_id ( ) } ] +
349
+ vec:: map ( tps, { |tp|
350
+ { mode: ast:: expl ( ast:: by_ref) ,
351
+ ty: cx. ty_path ( ty. span , [ tp. ident ] ) ,
352
+ ident: "__v" ,
353
+ id: cx. next_id ( ) } } ) ;
354
+
355
+ let ser_bnds = @[ ast:: bound_iface ( cx. ty_path ( ty. span ,
356
+ [ "__std" , "serialization" ,
357
+ "serializer" ] ) ) ] ;
358
+ let ser_tps: [ ast:: ty_param ] =
359
+ [ { ident: "__S" ,
360
+ id: cx. next_id ( ) ,
361
+ bounds: ser_bnds} ] +
362
+ vec:: map ( tps) { |tp| cx. clone_ty_param ( tp) } ;
363
+
364
+ let ser_output: @ast:: ty = @{ node: ast:: ty_nil,
365
+ span: ty. span } ;
366
+
367
+ let ser_blk = cx. blk ( ty. span ,
368
+ serialize_ty ( cx, ty,
369
+ #ast ( expr) { "__s" } , #ast ( expr) { "__v" } ) ) ;
370
+
371
+ @{ ident: "serialize" ,
372
+ attrs: [ ] ,
373
+ id: cx. next_id ( ) ,
374
+ node: ast:: item_fn ( { inputs: ser_inputs,
375
+ output: ser_output,
376
+ purity: ast:: impure_fn,
377
+ cf: ast:: return_val,
378
+ constraints: [ ] } ,
379
+ ser_tps,
380
+ ser_blk) ,
381
+ span: ty. span }
382
+ }
383
+
384
+ fn enum_module ( cx : ext_ctxt , name : str ,
385
+ variants : [ ast:: variant ] , tps : [ ast:: ty_param ] )
386
+ -> @ast:: item {
387
+
388
+ }
0 commit comments