private class SerializationPhasePreModel
super Phase
redef fun process_annotated_node(node, nat)
do
# Skip if we are not interested
var text = nat.n_atid.n_id.text
var serialize = text == "auto_serializable" or text == "serialize"
var noserialize = text == "noserialize"
if not (serialize or noserialize) then return
# Check legality of annotation
if node isa AModuledecl then
if noserialize then toolcontext.error(node.location, "Syntax Error: superfluous use of `{text}`, by default a module is `{text}`")
return
else if not (node isa AStdClassdef or node isa AAttrPropdef) then
toolcontext.error(node.location,
"Syntax Error: only a class, a module or an attribute can be annotated with `{text}`.")
return
else if serialize and node.is_noserialize then
toolcontext.error(node.location,
"Syntax Error: an entity cannot be both `{text}` and `noserialize`.")
return
else if node.as(Prod).get_annotations(text).length > 1 then
toolcontext.warning(node.location, "useless-{text}",
"Warning: duplicated annotation `{text}`.")
end
# Check the `serialize` state of the parent
if not node isa AModuledecl then
var up_serialize = false
var up: nullable ANode = node
while up != null do
up = up.parent
if up == null then
break
else if up.is_serialize then
up_serialize = true
break
else if up.is_noserialize then
break
end
end
# Check for useless double declarations
if serialize and up_serialize then
toolcontext.warning(node.location, "useless-serialize",
"Warning: superfluous use of `{text}`.")
else if noserialize and not up_serialize then
toolcontext.warning(node.location, "useless-noserialize",
"Warning: superfluous use of `{text}`.")
end
end
end
redef fun process_nclassdef(nclassdef)
do
if not nclassdef isa AStdClassdef then return
var serialize_by_default = nclassdef.how_serialize
if serialize_by_default != null then
# Add `super Serializable`
var sc = toolcontext.parse_superclass("Serializable")
sc.location = nclassdef.location
nclassdef.n_propdefs.add sc
# Add services
var per_attribute = not serialize_by_default
generate_serialization_method(nclassdef, per_attribute)
generate_deserialization_init(nclassdef)
end
end
redef fun process_nmodule(nmodule)
do
# Clear the cache of constructors to review before adding to it
nmodule.inits_to_retype.clear
# collect all classes
var auto_serializable_nclassdefs = nmodule.auto_serializable_nclassdefs
if not auto_serializable_nclassdefs.is_empty then
generate_deserialization_method(nmodule, auto_serializable_nclassdefs)
end
end
# Implement `core_serialize_to` on `nclassdef`
#
# Are attributes serialized on demand `per_attribute` with `serialize`?
# Otherwise they are serialized by default, and we check instead for `noserialize`.
fun generate_serialization_method(nclassdef: AClassdef, per_attribute: Bool)
do
var npropdefs = nclassdef.n_propdefs
# Do not insert a `core_serialize_to` if it already exists
for npropdef in npropdefs do
if npropdef isa AMethPropdef then
var methid = npropdef.n_methid
if methid != null and methid.collect_text == "core_serialize_to" then
return
end
end
end
var code = new Array[String]
code.add "redef fun core_serialize_to(v)"
code.add "do"
code.add " super"
for attribute in npropdefs do if attribute isa AAttrPropdef then
# Is `attribute` to be skipped?
if (per_attribute and not attribute.is_serialize) or
attribute.is_noserialize then continue
code.add " v.serialize_attribute(\"{attribute.serialize_name}\", {attribute.name})"
end
code.add "end"
# Create method Node and add it to the AST
npropdefs.push(toolcontext.parse_propdef(code.join("\n")))
end
# Add an empty constructor to the automated nclassdef
#
# Will be filled by `SerializationPhasePostModel`.
fun generate_deserialization_init(nclassdef: AClassdef)
do
var npropdefs = nclassdef.n_propdefs
# Do not insert a `from_deserializer` if it already exists
for npropdef in npropdefs do
if npropdef isa AMethPropdef then
var methid = npropdef.n_methid
if methid != null and methid.collect_text == "from_deserializer" then
return
end
end
end
var code = """
redef init from_deserializer(v) do abort"""
var npropdef = toolcontext.parse_propdef(code).as(AMethPropdef)
npropdefs.add npropdef
nclassdef.parent.as(AModule).inits_to_retype.add npropdef
end
# Add an empty `Deserializer::deserialize_class_intern`
#
# Will be filled by `SerializationPhasePostModel`.
fun generate_deserialization_method(nmodule: AModule, nclassdefs: Array[AStdClassdef])
do
var code = new Array[String]
var deserializer_nclassdef = nmodule.deserializer_nclassdef
var deserializer_npropdef
if deserializer_nclassdef == null then
# create the class
code.add "redef class Deserializer"
deserializer_npropdef = null
else
deserializer_npropdef = deserializer_nclassdef.deserializer_npropdef
end
if deserializer_npropdef == null then
# create the property
code.add " redef fun deserialize_class_intern(name) do abort"
else
toolcontext.error(deserializer_npropdef.location, "Error: `Deserializer::deserialize_class_intern` is generated and must not be defined, use `deserialize_class` instead.")
return
end
if deserializer_nclassdef == null then
code.add "end"
nmodule.n_classdefs.add toolcontext.parse_classdef(code.join("\n"))
else
deserializer_nclassdef.n_propdefs.add(toolcontext.parse_propdef(code.join("\n")))
end
end
end
src/frontend/serialization_model_phase.nit:85,1--267,3