1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Phase generating methods (code) to serialize Nit objects
16 module serialization_code_gen_phase
18 intrude import serialization_model_phase
20 redef class ToolContext
22 # The second phase of the serialization
23 var serialization_phase_post_model
: Phase = new SerializationPhasePostModel(self,
24 [modelize_property_phase
, serialization_phase_pre_model
])
27 private class SerializationPhasePostModel
30 # Fill the deserialization init `from_deserializer` and `Deserializer.deserialize_class_intern`
31 redef fun process_nmodule
(nmodule
)
33 for npropdef
in nmodule
.inits_to_retype
do
34 var nclassdef
= npropdef
.parent
35 assert nclassdef
isa AStdClassdef
37 var serialize_by_default
= nclassdef
.how_serialize
38 assert serialize_by_default
!= null
40 var per_attribute
= not serialize_by_default
41 fill_deserialization_init
(nclassdef
, npropdef
, per_attribute
)
45 var auto_serializable_nclassdefs
= nmodule
.auto_serializable_nclassdefs
46 if not auto_serializable_nclassdefs
.is_empty
then
47 fill_deserialization_method
(nmodule
, auto_serializable_nclassdefs
)
51 # Fill the constructor to the generated `init_npropdef` of `nclassdef`
52 fun fill_deserialization_init
(nclassdef
: AClassdef, init_npropdef
: AMethPropdef, per_attribute
: Bool)
54 var code
= new Array[String]
56 redef init from_deserializer(v)
59 v.notify_of_creation self
62 for attribute
in nclassdef
.n_propdefs
do
63 if not attribute
isa AAttrPropdef then continue
65 # Is `attribute` to be skipped?
66 if (per_attribute
and not attribute
.is_serialize
) or
67 attribute
.is_noserialize
then continue
69 var mtype
= attribute
.mtype
70 if mtype
== null then continue
71 var type_name
= mtype
.to_s
72 var name
= attribute
.name
74 if mtype
.need_anchor
then
75 # Check dynamic type of virtual params
77 var {{{name}}} = v.deserialize_attribute("{{{attribute.serialize_name}}}", (new GetName[{{{mtype}}}]).to_s)
82 var {{{name}}} = v.deserialize_attribute("{{{attribute.serialize_name}}}", "{{{type_name}}}")
86 if type_name
== "nullable Object" then
89 self.{{{name}}} = {{{name}}}
94 if v.deserialize_attribute_missing then
96 # What to do when an attribute is missing?
97 if attribute
.has_value
then
98 # Leave it to the default value
99 else if mtype
isa MNullableType then
100 # This is always a nullable type
102 self.{{{name}}} = null"""
103 else if mtype
isa MFormalType then
104 # This type nullability may change in subclasses
106 var n___{{{name}}} = null
107 if n___{{{name}}} isa {{{mtype}}} then
108 self.{{{name}}} = n___{{{name}}}
110 v.errors.add new AttributeMissingError(self, "{{{name}}}")
113 v.errors.add new AttributeMissingError(self, "{{{name}}}")"""
116 else if not {{{name}}} isa {{{type_name}}} then
117 v.errors.add new AttributeTypeError(self, "{{{attribute.serialize_name}}}", {{{name}}}, "{{{type_name}}}")
118 if v.keep_going == false then return
120 self.{{{name}}} = {{{name}}}
128 # Replace the body of the constructor
129 var npropdef
= toolcontext
.parse_propdef
(code
.join
("\n")).as(AMethPropdef)
130 init_npropdef
.n_block
= npropdef
.n_block
132 # Run the literal phase on the generated code
133 var v
= new LiteralVisitor(toolcontext
)
134 v
.enter_visit
(npropdef
.n_block
)
137 # Fill the abstract serialization service
138 fun fill_deserialization_method
(nmodule
: AModule, nclassdefs
: Array[AStdClassdef])
140 var deserializer_nclassdef
= nmodule
.deserializer_nclassdef
141 if deserializer_nclassdef
== null then return
142 var deserializer_npropdef
= deserializer_nclassdef
.deserializer_npropdef
143 if deserializer_npropdef
== null then return
145 # Collect local types expected to be deserialized
147 # Map types' `name` to their `full_name`.
149 # FIXME use only the full name when there's a `class_full_name`
150 var types_to_deserialize
= new Map[String, String]
152 ## Local serializable standard class without parameters
153 for nclassdef
in nclassdefs
do
154 var mclass
= nclassdef
.mclass
155 if mclass
== null then continue
157 if mclass
.arity
== 0 and mclass
.kind
== concrete_kind
then
158 types_to_deserialize
[mclass
.name
] = mclass
.full_name
162 ## Static parametized types on serializable attributes
163 for nclassdef
in nmodule
.n_classdefs
do
164 if not nclassdef
isa AStdClassdef then continue
166 for attribute
in nclassdef
.n_propdefs
do
167 if not attribute
isa AAttrPropdef then continue
169 var serialize_by_default
= nclassdef
.how_serialize
170 if serialize_by_default
== null then continue
171 var per_attribute
= not serialize_by_default
173 # Is `attribute` to be skipped?
174 if (per_attribute
and not attribute
.is_serialize
) or
175 attribute
.is_noserialize
then continue
177 var mtype
= attribute
.mtype
178 if mtype
== null then continue
179 if mtype
isa MNullableType then mtype
= mtype
.mtype
181 if mtype
isa MClassType and mtype
.mclass
.arity
> 0 and
182 mtype
.mclass
.kind
== concrete_kind
and not mtype
.need_anchor
then
184 # Check is a `Serializable`
185 var mmodule
= nmodule
.mmodule
186 if mmodule
== null then continue
188 var greaters
= mtype
.mclass
.in_hierarchy
(mmodule
).greaters
189 var is_serializable
= false
190 for sup
in greaters
do if sup
.name
== "Serializable" then
191 is_serializable
= true
195 if is_serializable
then types_to_deserialize
[mtype
.name
] = mtype
.full_name
200 # Build implementation code
201 var code
= new Array[String]
202 code
.add
"redef fun deserialize_class_intern(name)"
205 for name
, full_name
in types_to_deserialize
do
207 if full_name
.has
('-') then
208 # Invalid module name, it is either artificial or a script
209 # without module declaration (like those generated by nitunit)
213 code
.add
" if name == \"{name}\
" then return new {full_name}.from_deserializer(self)"
216 code
.add
" return super"
219 # Replace the body of the constructor
220 var npropdef
= toolcontext
.parse_propdef
(code
.join
("\n")).as(AMethPropdef)
221 deserializer_npropdef
.n_block
= npropdef
.n_block
223 # Run the literal phase on the generated code
224 var v
= new LiteralVisitor(toolcontext
)
225 v
.enter_visit
(npropdef
.n_block
)