Serializable::inspect
to show more useful information
nitc :: modelbuilder
more_collections :: more_collections
Highly specific, but useful, collections-related classes.serialization :: serialization_core
Abstract services to serialize Nit objects to different formatsnitc :: toolcontext
Common command-line tool infrastructure than handle options and error messagescore :: union_find
union–find algorithm using an efficient disjoint-set data structurenitc :: serialization_model_phase
Phase generating methods (model-only) to serialize Nit objectsnitc :: nitmetrics
A program that collects various metrics on nit programs and librariesclone
method of the astbuilder tool
# Phase generating methods (code) to serialize Nit objects
module serialization_code_gen_phase
intrude import serialization_model_phase
redef class ToolContext
# The second phase of the serialization
var serialization_phase_post_model: Phase = new SerializationPhasePostModel(self,
[modelize_property_phase, serialization_phase_pre_model])
end
private class SerializationPhasePostModel
super Phase
# Fill the deserialization init `from_deserializer` and `Deserializer.deserialize_class_intern`
redef fun process_nmodule(nmodule)
do
for npropdef in nmodule.inits_to_retype do
var nclassdef = npropdef.parent
assert nclassdef isa AStdClassdef
var serialize_by_default = nclassdef.how_serialize
assert serialize_by_default != null
var per_attribute = not serialize_by_default
fill_deserialization_init(nclassdef, npropdef, per_attribute)
end
# collect all classes
var auto_serializable_nclassdefs = nmodule.auto_serializable_nclassdefs
if not auto_serializable_nclassdefs.is_empty then
fill_deserialization_method(nmodule, auto_serializable_nclassdefs)
end
end
# Fill the constructor to the generated `init_npropdef` of `nclassdef`
fun fill_deserialization_init(nclassdef: AClassdef, init_npropdef: AMethPropdef, per_attribute: Bool)
do
var code = new Array[String]
code.add """
redef init from_deserializer(v)
do
super
v.notify_of_creation self
"""
for attribute in nclassdef.n_propdefs do
if not attribute isa AAttrPropdef then continue
# Is `attribute` to be skipped?
if (per_attribute and not attribute.is_serialize) or
attribute.is_noserialize then continue
var mtype = attribute.mtype
if mtype == null then continue
var type_name = mtype.to_s
var name = attribute.name
if mtype.need_anchor then
# Check dynamic type of virtual params
code.add """
var {{{name}}} = v.deserialize_attribute("{{{attribute.serialize_name}}}", (new GetName[{{{mtype}}}]).to_s)
"""
else
# No param to check
code.add """
var {{{name}}} = v.deserialize_attribute("{{{attribute.serialize_name}}}", "{{{type_name}}}")
"""
end
if type_name == "nullable Object" then
# Don't type check
code.add """
self.{{{name}}} = {{{name}}}
"""
else
# Needs type check
code.add """
if v.deserialize_attribute_missing then
"""
# What to do when an attribute is missing?
if attribute.has_value then
# Leave it to the default value
else if mtype isa MNullableType then
# This is always a nullable type
code.add """
self.{{{name}}} = null"""
else if mtype isa MFormalType then
# This type nullability may change in subclasses
code.add """
var n___{{{name}}} = null
if n___{{{name}}} isa {{{mtype}}} then
self.{{{name}}} = n___{{{name}}}
else
v.errors.add new AttributeMissingError(self, "{{{name}}}")
end"""
else code.add """
v.errors.add new AttributeMissingError(self, "{{{name}}}")"""
code.add """
else if not {{{name}}} isa {{{type_name}}} then
v.errors.add new AttributeTypeError(self, "{{{attribute.serialize_name}}}", {{{name}}}, "{{{type_name}}}")
if v.keep_going == false then return
else
self.{{{name}}} = {{{name}}}
end
"""
end
end
code.add "end"
# Replace the body of the constructor
var npropdef = toolcontext.parse_propdef(code.join("\n")).as(AMethPropdef)
init_npropdef.n_block = npropdef.n_block
# Run the literal phase on the generated code
var v = new LiteralVisitor(toolcontext)
v.enter_visit(npropdef.n_block)
end
# Fill the abstract serialization service
fun fill_deserialization_method(nmodule: AModule, nclassdefs: Array[AStdClassdef])
do
var deserializer_nclassdef = nmodule.deserializer_nclassdef
if deserializer_nclassdef == null then return
var deserializer_npropdef = deserializer_nclassdef.deserializer_npropdef
if deserializer_npropdef == null then return
# Collect local types expected to be deserialized
#
# Map types' `name` to their `full_name`.
#
# FIXME use only the full name when there's a `class_full_name`
var types_to_deserialize = new Map[String, String]
## Local serializable standard class without parameters
for nclassdef in nclassdefs do
var mclass = nclassdef.mclass
if mclass == null then continue
if mclass.arity == 0 and mclass.kind == concrete_kind then
types_to_deserialize[mclass.name] = mclass.full_name
end
end
## Static parametized types on serializable attributes
for nclassdef in nmodule.n_classdefs do
if not nclassdef isa AStdClassdef then continue
for attribute in nclassdef.n_propdefs do
if not attribute isa AAttrPropdef then continue
var serialize_by_default = nclassdef.how_serialize
if serialize_by_default == null then continue
var per_attribute = not serialize_by_default
# Is `attribute` to be skipped?
if (per_attribute and not attribute.is_serialize) or
attribute.is_noserialize then continue
var mtype = attribute.mtype
if mtype == null then continue
if mtype isa MNullableType then mtype = mtype.mtype
if mtype isa MClassType and mtype.mclass.arity > 0 and
mtype.mclass.kind == concrete_kind and not mtype.need_anchor then
# Check is a `Serializable`
var mmodule = nmodule.mmodule
if mmodule == null then continue
var greaters = mtype.mclass.in_hierarchy(mmodule).greaters
var is_serializable = false
for sup in greaters do if sup.name == "Serializable" then
is_serializable = true
break
end
if is_serializable then types_to_deserialize[mtype.name] = mtype.full_name
end
end
end
# Build implementation code
var code = new Array[String]
code.add "redef fun deserialize_class_intern(name)"
code.add "do"
for name, full_name in types_to_deserialize do
if full_name.has('-') then
# Invalid module name, it is either artificial or a script
# without module declaration (like those generated by nitunit)
full_name = name
end
code.add " if name == \"{name}\" then return new {full_name}.from_deserializer(self)"
end
code.add " return super"
code.add "end"
# Replace the body of the constructor
var npropdef = toolcontext.parse_propdef(code.join("\n")).as(AMethPropdef)
deserializer_npropdef.n_block = npropdef.n_block
# Run the literal phase on the generated code
var v = new LiteralVisitor(toolcontext)
v.enter_visit(npropdef.n_block)
end
end
src/frontend/serialization_code_gen_phase.nit:15,1--227,3