1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2013 Jean-Philippe Caissy <jpcaissy@piji.ca>
4 # Copyright 2013 Guillaume Auger <jeho@resist.ca>
5 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
11 # http://www.apache.org/licenses/LICENSE-2.0
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
19 # Phase generating methods to serialize Nit objects to different formats
20 module serialization_phase
22 private import parser_util
24 private import annotation
26 redef class ToolContext
27 # Generate serialization and deserialization methods on `auto_serializable` annotated classes.
28 var serialization_phase_pre_model
: Phase = new SerializationPhasePreModel(self, null)
30 # The second phase of the serialization
31 var serialization_phase_post_model
: Phase = new SerializationPhasePostModel(self,
32 [modelize_class_phase
, serialization_phase_pre_model
])
34 private fun place_holder_type_name
: String do return "PlaceHolderTypeWhichShouldNotExist"
37 # TODO add annotations on attributes (volatile, sensitive or do_not_serialize?)
38 private class SerializationPhasePreModel
41 redef fun process_annotated_node
(nclassdef
, nat
)
43 # Skip if we are not interested
44 if nat
.n_atid
.n_id
.text
!= "auto_serializable" then return
45 if not nclassdef
isa AStdClassdef then
46 toolcontext
.error
(nclassdef
.location
, "Syntax Error: only a concrete class can be automatically serialized.")
50 # Add `super Serializable`
51 var sc
= toolcontext
.parse_superclass
("Serializable")
52 sc
.location
= nat
.location
53 nclassdef
.n_propdefs
.add sc
55 generate_serialization_method
(nclassdef
)
57 generate_deserialization_init
(nclassdef
)
60 redef fun process_nmodule
(nmodule
)
62 # Clear the cache of constructors to review before adding to it
63 nmodule
.inits_to_retype
.clear
66 var auto_serializable_nclassdefs
= new Array[AStdClassdef]
67 for nclassdef
in nmodule
.n_classdefs
do
68 if nclassdef
isa AStdClassdef and
69 not nclassdef
.get_annotations
("auto_serializable").is_empty
then
70 auto_serializable_nclassdefs
.add nclassdef
74 if not auto_serializable_nclassdefs
.is_empty
then
75 generate_deserialization_method
(nmodule
, auto_serializable_nclassdefs
)
79 fun generate_serialization_method
(nclassdef
: AClassdef)
81 var npropdefs
= nclassdef
.n_propdefs
83 var code
= new Array[String]
84 code
.add
"redef fun core_serialize_to(v)"
88 for attribute
in npropdefs
do if attribute
isa AAttrPropdef then
89 var name
= attribute
.name
90 code
.add
" v.serialize_attribute(\"{name}\
", {name})"
95 # Create method Node and add it to the AST
96 npropdefs
.push
(toolcontext
.parse_propdef
(code
.join
("\n")))
99 # Add a constructor to the automated nclassdef
100 fun generate_deserialization_init
(nclassdef
: AStdClassdef)
102 # Do not generate constructors for abstract classes
103 if nclassdef
.n_classkind
isa AAbstractClasskind then return
105 var npropdefs
= nclassdef
.n_propdefs
107 var code
= new Array[String]
108 code
.add
"redef init from_deserializer(v: Deserializer)"
111 code
.add
" v.notify_of_creation self"
113 for attribute
in npropdefs
do if attribute
isa AAttrPropdef then
114 var n_type
= attribute
.n_type
116 if n_type
== null then
117 # Use a place holder, we will replace it with the infered type after the model phases
118 type_name
= toolcontext
.place_holder_type_name
120 type_name
= n_type
.type_name
122 var name
= attribute
.name
125 code
.add
"\tvar {name} = v.deserialize_attribute(\"{name}\
")"
126 code
.add
"\tassert {name} isa {type_name} else print \"Unsupported type for attribute
'{name}', got
'\{{name}.class_name\}' (ex
{type_name})\
""
127 code
.add
"\tself.{name} = {name}"
132 var npropdef
= toolcontext
.parse_propdef
(code
.join
("\n")).as(AMethPropdef)
133 npropdefs
.add npropdef
134 nclassdef
.parent
.as(AModule).inits_to_retype
.add npropdef
137 # Added to the abstract serialization service
138 fun generate_deserialization_method
(nmodule
: AModule, nclassdefs
: Array[AStdClassdef])
140 var code
= new Array[String]
142 var deserializer_nclassdef
= nmodule
.deserializer_nclassdef
143 var deserializer_npropdef
144 if deserializer_nclassdef
== null then
146 code
.add
"redef class Deserializer"
147 deserializer_npropdef
= null
149 deserializer_npropdef
= deserializer_nclassdef
.deserializer_npropdef
152 if deserializer_npropdef
== null then
153 # create the property
154 code
.add
" redef fun deserialize_class(name)"
157 toolcontext
.error
(deserializer_npropdef
.location
, "Error: you cannot define `Deserializer::deserialize_class` in a module where you use `auto_serializable`.")
161 for nclassdef
in nclassdefs
do
162 var name
= nclassdef
.n_id
.text
163 if nclassdef
.n_formaldefs
.is_empty
and
164 not nclassdef
.n_classkind
isa AAbstractClasskind then
166 code
.add
" if name == \"{name}\
" then return new {name}.from_deserializer(self)"
170 code
.add
" return super"
173 if deserializer_nclassdef
== null then
175 nmodule
.n_classdefs
.add toolcontext
.parse_classdef
(code
.join
("\n"))
177 deserializer_nclassdef
.n_propdefs
.add
(toolcontext
.parse_propdef
(code
.join
("\n")))
182 private class SerializationPhasePostModel
185 redef fun process_nmodule
(nmodule
)
187 for npropdef
in nmodule
.inits_to_retype
do
188 var mpropdef
= npropdef
.mpropdef
189 if mpropdef
== null then continue # skip error
190 var v
= new PreciseTypeVisitor(npropdef
, mpropdef
.mclassdef
, toolcontext
)
191 npropdef
.accept_precise_type_visitor v
196 # Visitor on generated constructors to replace the expected type of deserialized attributes
197 private class PreciseTypeVisitor
200 var npropdef
: AMethPropdef
201 var mclassdef
: MClassDef
202 var toolcontext
: ToolContext
204 redef fun visit
(n
) do n
.accept_precise_type_visitor
(self)
208 private fun accept_precise_type_visitor
(v
: PreciseTypeVisitor) do visit_all
(v
)
212 redef fun accept_precise_type_visitor
(v
)
214 if n_type
.collect_text
!= v
.toolcontext
.place_holder_type_name
then return
216 var attr_name
= "_" + n_expr
.collect_text
217 for mattrdef
in v
.mclassdef
.mpropdefs
do
218 if mattrdef
isa MAttributeDef and mattrdef
.name
== attr_name
then
219 var new_ntype
= v
.toolcontext
.parse_something
(mattrdef
.static_mtype
.to_s
)
220 n_type
.replace_with new_ntype
227 redef class AAttrPropdef
228 private fun name
: String
235 private fun type_name
: String
239 if n_kwnullable
!= null then name
= "nullable {name}"
242 if not types
.is_empty
then
243 var params
= new Array[String]
244 for t
in types
do params
.add
(t
.type_name
)
245 return "{name}[{params.join(", ")}]"
251 private fun deserializer_nclassdef
: nullable AStdClassdef
253 for nclassdef
in n_classdefs
do
254 if nclassdef
isa AStdClassdef and nclassdef
.n_id
.text
== "Deserialization" then
262 private var inits_to_retype
= new Array[AMethPropdef]
265 redef class AStdClassdef
266 private fun deserializer_npropdef
: nullable AMethPropdef
268 for npropdef
in n_propdefs
do if npropdef
isa AMethPropdef then
269 var id
= npropdef
.n_methid
270 if id
isa AIdMethid and id
.n_id
.text
== "deserialize_class" then