77ff746508eed8bbb44cb40e12ff05fe025ee426
[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: Deserializer)
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 var types_to_deserialize = new Set[String]
147
148 ## Local serializable standard class without parameters
149 for nclassdef in nclassdefs do
150 var mclass = nclassdef.mclass
151 if mclass == null then continue
152
153 if mclass.arity == 0 and mclass.kind == concrete_kind then
154 types_to_deserialize.add mclass.name
155 end
156 end
157
158 ## Static parametized types on serializable attributes
159 for nclassdef in nmodule.n_classdefs do
160 if not nclassdef isa AStdClassdef then continue
161
162 for attribute in nclassdef.n_propdefs do
163 if not attribute isa AAttrPropdef then continue
164
165 var serialize_by_default = nclassdef.how_serialize
166 if serialize_by_default == null then continue
167 var per_attribute = not serialize_by_default
168
169 # Is `attribute` to be skipped?
170 if (per_attribute and not attribute.is_serialize) or
171 attribute.is_noserialize then continue
172
173 var mtype = attribute.mtype
174 if mtype == null then continue
175 if mtype isa MNullableType then mtype = mtype.mtype
176
177 if mtype isa MClassType and mtype.mclass.arity > 0 and
178 mtype.mclass.kind == concrete_kind and not mtype.need_anchor then
179
180 # Check is a `Serializable`
181 var mmodule = nmodule.mmodule
182 if mmodule == null then continue
183
184 var greaters = mtype.mclass.in_hierarchy(mmodule).greaters
185 var is_serializable = false
186 for sup in greaters do if sup.name == "Serializable" then
187 is_serializable = true
188 break
189 end
190
191 if is_serializable then types_to_deserialize.add mtype.to_s
192 end
193 end
194 end
195
196 # Build implementation code
197 var code = new Array[String]
198 code.add "redef fun deserialize_class_intern(name)"
199 code.add "do"
200
201 for name in types_to_deserialize do
202 code.add " if name == \"{name}\" then return new {name}.from_deserializer(self)"
203 end
204
205 code.add " return super"
206 code.add "end"
207
208 # Replace the body of the constructor
209 var npropdef = toolcontext.parse_propdef(code.join("\n")).as(AMethPropdef)
210 deserializer_npropdef.n_block = npropdef.n_block
211
212 # Run the literal phase on the generated code
213 var v = new LiteralVisitor(toolcontext)
214 v.enter_visit(npropdef.n_block)
215 end
216 end