Merge: doc: fixed some typos and other misc. corrections
[nit.git] / src / frontend / serialization_code_gen_phase.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
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
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14
15 # Phase generating methods (code) to serialize Nit objects
16 module serialization_code_gen_phase
17
18 intrude import serialization_model_phase
19
20 redef class ToolContext
21
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])
25 end
26
27 private class SerializationPhasePostModel
28 super Phase
29
30 # Fill the deserialization init `from_deserializer` and `Deserializer.deserialize_class_intern`
31 redef fun process_nmodule(nmodule)
32 do
33 for npropdef in nmodule.inits_to_retype do
34 var nclassdef = npropdef.parent
35 assert nclassdef isa AStdClassdef
36
37 var serialize_by_default = nclassdef.how_serialize
38 assert serialize_by_default != null
39
40 var per_attribute = not serialize_by_default
41 fill_deserialization_init(nclassdef, npropdef, per_attribute)
42 end
43
44 # collect all classes
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)
48 end
49 end
50
51 # Fill the constructor to the generated `init_npropdef` of `nclassdef`
52 fun fill_deserialization_init(nclassdef: AClassdef, init_npropdef: AMethPropdef, per_attribute: Bool)
53 do
54 var code = new Array[String]
55 code.add """
56 redef init from_deserializer(v)
57 do
58 super
59 v.notify_of_creation self
60 """
61
62 for attribute in nclassdef.n_propdefs do
63 if not attribute isa AAttrPropdef then continue
64
65 # Is `attribute` to be skipped?
66 if (per_attribute and not attribute.is_serialize) or
67 attribute.is_noserialize then continue
68
69 var mtype = attribute.mtype
70 if mtype == null then continue
71 var type_name = mtype.to_s
72 var name = attribute.name
73
74 if mtype.need_anchor then
75 # Check dynamic type of virtual params
76 code.add """
77 var {{{name}}} = v.deserialize_attribute("{{{attribute.serialize_name}}}", (new GetName[{{{mtype}}}]).to_s)
78 """
79 else
80 # No param to check
81 code.add """
82 var {{{name}}} = v.deserialize_attribute("{{{attribute.serialize_name}}}", "{{{type_name}}}")
83 """
84 end
85
86 if type_name == "nullable Object" then
87 # Don't type check
88 code.add """
89 self.{{{name}}} = {{{name}}}
90 """
91 else
92 # Needs type check
93 code.add """
94 if v.deserialize_attribute_missing then
95 """
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
101 code.add """
102 self.{{{name}}} = null"""
103 else if mtype isa MFormalType then
104 # This type nullability may change in subclasses
105 code.add """
106 var n___{{{name}}} = null
107 if n___{{{name}}} isa {{{mtype}}} then
108 self.{{{name}}} = n___{{{name}}}
109 else
110 v.errors.add new AttributeMissingError(self, "{{{name}}}")
111 end"""
112 else code.add """
113 v.errors.add new AttributeMissingError(self, "{{{name}}}")"""
114
115 code.add """
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
119 else
120 self.{{{name}}} = {{{name}}}
121 end
122 """
123 end
124 end
125
126 code.add "end"
127
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
131
132 # Run the literal phase on the generated code
133 var v = new LiteralVisitor(toolcontext)
134 v.enter_visit(npropdef.n_block)
135 end
136
137 # Fill the abstract serialization service
138 fun fill_deserialization_method(nmodule: AModule, nclassdefs: Array[AStdClassdef])
139 do
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
144
145 # Collect local types expected to be deserialized
146 #
147 # Map types' `name` to their `full_name`.
148 #
149 # FIXME use only the full name when there's a `class_full_name`
150 var types_to_deserialize = new Map[String, String]
151
152 ## Local serializable standard class without parameters
153 for nclassdef in nclassdefs do
154 var mclass = nclassdef.mclass
155 if mclass == null then continue
156
157 if mclass.arity == 0 and mclass.kind == concrete_kind then
158 types_to_deserialize[mclass.name] = mclass.full_name
159 end
160 end
161
162 ## Static parametized types on serializable attributes
163 for nclassdef in nmodule.n_classdefs do
164 if not nclassdef isa AStdClassdef then continue
165
166 for attribute in nclassdef.n_propdefs do
167 if not attribute isa AAttrPropdef then continue
168
169 var serialize_by_default = nclassdef.how_serialize
170 if serialize_by_default == null then continue
171 var per_attribute = not serialize_by_default
172
173 # Is `attribute` to be skipped?
174 if (per_attribute and not attribute.is_serialize) or
175 attribute.is_noserialize then continue
176
177 var mtype = attribute.mtype
178 if mtype == null then continue
179 if mtype isa MNullableType then mtype = mtype.mtype
180
181 if mtype isa MClassType and mtype.mclass.arity > 0 and
182 mtype.mclass.kind == concrete_kind and not mtype.need_anchor then
183
184 # Check is a `Serializable`
185 var mmodule = nmodule.mmodule
186 if mmodule == null then continue
187
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
192 break
193 end
194
195 if is_serializable then types_to_deserialize[mtype.name] = mtype.full_name
196 end
197 end
198 end
199
200 # Build implementation code
201 var code = new Array[String]
202 code.add "redef fun deserialize_class_intern(name)"
203 code.add "do"
204
205 for name, full_name in types_to_deserialize do
206
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)
210 full_name = name
211 end
212
213 code.add " if name == \"{name}\" then return new {full_name}.from_deserializer(self)"
214 end
215
216 code.add " return super"
217 code.add "end"
218
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
222
223 # Run the literal phase on the generated code
224 var v = new LiteralVisitor(toolcontext)
225 v.enter_visit(npropdef.n_block)
226 end
227 end