Merge: modelize_property: Split the build_property method of AAttrPropdef
authorJean Privat <jean@pryen.org>
Tue, 17 Mar 2020 21:35:57 +0000 (17:35 -0400)
committerJean Privat <jean@pryen.org>
Tue, 17 Mar 2020 21:35:57 +0000 (17:35 -0400)
This pr split the `build property` method into several methods. The objective is to be able to build the entities of the model (read, write...) more independently according to the current state. The construction of the signature was also split for the same purpose.

Pull-Request: #2813
Reviewed-by: Jean Privat <jean@pryen.org>

.gitlab-ci.yml
src/astbuilder.nit
src/compiler/separate_compiler.nit
src/compiler/separate_erasure_compiler.nit
src/neo.nit

index d4e5928..398ed14 100644 (file)
@@ -7,6 +7,7 @@ services:
 
 variables:
   NEO4J_AUTH: none
+  POSTGRES_HOST_AUTH_METHOD: trust
 
 cache:
   paths:
index 681c4bf..f3b35ea 100644 (file)
@@ -20,6 +20,7 @@ intrude import literal
 intrude import parser
 intrude import semantize::scope
 intrude import modelbuilder_base
+intrude import modelize_property
 
 # General factory to build semantic nodes in the AST of expressions
 class ASTBuilder
@@ -165,6 +166,41 @@ class ASTBuilder
                return new ANotExpr.make(expr)
        end
 
+       # Make a new attribute
+       fun make_attribute(name: String,
+                                       n_type: nullable AType,
+                                       n_visibility: nullable AVisibility,
+                                       initial_value: nullable AExpr,
+                                       n_block: nullable AExpr,
+                                       m_attributedef: nullable MAttributeDef,
+                                       m_setterdef: nullable MMethodDef,
+                                       m_getterdef: nullable MMethodDef): AAttrPropdef
+       do
+               return new AAttrPropdef.make(name, n_type, n_visibility, initial_value, n_block, m_attributedef, m_setterdef, m_getterdef)
+       end
+
+       # Make a new class (AStdClassdef)
+       fun make_class(mclassdef: nullable MClassDef,
+                                       n_visibility: nullable AVisibility,
+                                       n_formaldefs : Collection[AFormaldef],
+                                       n_extern_code_block : nullable AExternCodeBlock,
+                                       n_propdefs : Collection[APropdef],
+                                       n_classkind: nullable AClasskind): AStdClassdef
+       do
+               return new AStdClassdef.make(mclassdef, n_visibility, n_formaldefs, n_extern_code_block, n_propdefs, n_classkind)
+       end
+
+       fun make_var(variable: Variable, mtype: MType): AVarExpr
+       do
+               return new AVarExpr.make(variable, mtype)
+       end
+
+       # Make a call assignment i.e `a = 10`
+       fun make_call_assign(recv: AExpr, callsite: CallSite, n_args: nullable Collection[AExpr], n_value: AExpr): ACallAssignExpr
+       do
+               return new ACallAssignExpr.make(recv, callsite, n_args, n_value)
+       end
+
        # Build a callsite to call the `mproperty` in the current method `caller_method`.
        # `is_self_call` indicate if the method caller is a property of `self`
        fun create_callsite(modelbuilder: ModelBuilder, caller_method : AMethPropdef, mproperty: MMethod, is_self_call: Bool): CallSite
@@ -262,6 +298,68 @@ class APlaceholderExpr
        end
 end
 
+redef class ACallAssignExpr
+       private init make(recv: AExpr, callsite: CallSite, args: nullable Collection[AExpr], n_value: AExpr)
+       do
+               _callsite = callsite
+               _mtype = callsite.recv
+               _is_typed = true
+               var n_args = new AListExprs
+               if args != null then
+                       n_args.n_exprs.add_all(args)
+               end
+               var n_qid = new AQid
+               n_qid.n_id = new TId
+               n_qid.n_id.text = callsite.mproperty.name
+               init_acallassignexpr(recv, n_qid, n_args, new TAssign, n_value)
+       end
+end
+
+redef class AStdClassdef
+       private init make(mclassdef: nullable MClassDef,
+                                       n_visibility: nullable AVisibility,
+                                       n_formaldefs : Collection[Object],
+                                       n_extern_code_block : nullable AExternCodeBlock,
+                                       n_propdefs : Collection[Object],
+                                       n_classkind: nullable AClasskind)
+       do
+               if n_visibility == null then n_visibility = new APublicVisibility
+               if n_classkind == null then n_classkind = new AConcreteClasskind.init_aconcreteclasskind(new TKwclass)
+               var n_qid = new AQclassid.init_aqclassid(null, new TClassid)
+               init_astdclassdef(null, null, n_visibility, n_classkind, n_qid, null, n_formaldefs, null, n_extern_code_block, n_propdefs, new TKwend)
+               _mclassdef = mclassdef
+               _mclass = mclassdef.mclass
+       end
+end
+
+redef class AAttrPropdef
+
+       # Create a new `AAttrPropdef`
+       # Note: By default if the `AVisibility` is not given the visibility is set to public
+       private init make(name: String,
+                                       n_type: nullable AType,
+                                       n_visibility: nullable AVisibility,
+                                       initial_value: nullable AExpr,
+                                       n_block: nullable AExpr,
+                                       m_attributedef: nullable MAttributeDef,
+                                       m_setterdef: nullable MMethodDef,
+                                       m_getterdef: nullable MMethodDef)
+       do
+               # Set the model type
+               if n_type != null then mtype = n_type.mtype
+               # Define the visibility default is public
+               if n_visibility == null then n_visibility = new APublicVisibility
+               init_aattrpropdef(null, null, n_visibility, new TKwvar, new TId, n_type, null, initial_value, null, null , n_block, null)
+               # Set the name of the attribute
+               _n_id2.text = name
+               _mpropdef = m_attributedef
+               _mreadpropdef = m_getterdef
+               _mwritepropdef = m_setterdef
+               if initial_value != null or n_block != null then has_value = true
+               if m_attributedef != null then self.location = m_attributedef.location
+       end
+end
+
 redef class ANotExpr
        private init make(expr: AExpr)
        do
@@ -318,6 +416,7 @@ redef class AMethPropdef
                if n_visibility == null then n_visibility = new APublicVisibility
                self.init_amethpropdef(null,tk_redef,n_visibility,new TKwmeth,null,null,null,n_methid,n_signature,n_annotations,n_extern_calls,n_extern_code_block,new TKwdo,n_block,new TKwend)
                self.mpropdef = mmethoddef
+               if mpropdef != null then self.location = mmethoddef.location
        end
 end
 
@@ -538,13 +637,6 @@ redef class ACallExpr
        end
 end
 
-redef class AAsCastExpr
-       private init make(n_expr: AExpr, n_type: AType)
-       do
-               init_aascastexpr(n_expr, new TKwas , null , n_type, null)
-       end
-end
-
 redef class AAsNotnullExpr
        private init make(n_expr: AExpr, t: nullable MType)
        do
@@ -642,6 +734,20 @@ redef class AVarAssignExpr
 end
 
 redef class ASignature
+
+       init make_from_msignature(msignature: MSignature)
+       do
+               var nparams = new Array[AParam]
+               for mparam in msignature.mparameters do
+                       var variable = new Variable(mparam.name)
+                       variable.declared_type = mparam.mtype
+                       n_params.add(new AParam.make(variable, new AType.make(mparam.mtype)))
+               end
+               var return_type = null
+               if msignature.return_mtype != null then return_type = new AType.make(msignature.return_mtype)
+               init_asignature(null, nparams, null, return_type)
+       end
+
        redef fun clone: SELF
        do
                var ntype = n_type
@@ -655,6 +761,7 @@ redef class AParam
        private init make(v: nullable Variable, t: nullable AType)
        do
                _n_id = new TId
+               if v != null then _n_id.text = v.name
                _variable = v
                _n_type = t
        end
index 4716dae..4fa8fd8 100644 (file)
@@ -825,23 +825,14 @@ class SeparateCompiler
                v.add_decl("\};")
        end
 
-       # Globally compile the table of the class mclass
-       # In a link-time optimisation compiler, tables are globally computed
-       # In a true separate compiler (a with dynamic loading) you cannot do this unfortnally
-       fun compile_class_to_c(mclass: MClass)
+       protected fun compile_class_vft(ccinfo: ClassCompilationInfo, v: SeparateCompilerVisitor)
        do
-               if mclass.is_broken then return
-
-               var mtype = mclass.intro.bound_mtype
-               var c_name = mclass.c_name
-
-               var v = new_visitor
-
+               var mclass = ccinfo.mclass
+               var mtype = ccinfo.mtype
                var rta = runtime_type_analysis
-               var is_dead = rta != null and not rta.live_classes.has(mclass)
-               # While the class may be dead, some part of separately compiled code may use symbols associated to the class, so
-               # in order to compile and link correctly the C code, these symbols should be declared and defined.
-               var need_corpse = is_dead and mtype.is_c_primitive or mclass.kind == extern_kind or mclass.kind == enum_kind
+               var c_name = ccinfo.mclass.c_name
+               var is_dead = ccinfo.is_dead
+               var need_corpse = ccinfo.need_corpse
 
                v.add_decl("/* runtime class {c_name}: {mclass.full_name} (dead={is_dead}; need_corpse={need_corpse})*/")
 
@@ -873,11 +864,24 @@ class SeparateCompiler
                        v.add_decl("\}")
                        v.add_decl("\};")
                end
+       end
+
+       # Given a `MClass`, if it's a universal class and if it needs to be handle
+       # specifically by the compiler, this function will compile it and return
+       # true. Otherwise, no C code will be written in the visitor and the value
+       # false will be returned.
+       fun compile_class_if_universal(ccinfo: ClassCompilationInfo, v: SeparateCompilerVisitor): Bool
+       do
+               var mclass = ccinfo.mclass
+               var mtype = ccinfo.mtype
+               var c_name = ccinfo.mclass.c_name
+               var is_dead = ccinfo.is_dead
+               var need_corpse = ccinfo.need_corpse
 
                if mtype.is_c_primitive or mtype.mclass.name == "Pointer" then
                        # Is a primitive type or the Pointer class, not any other extern class
 
-                       if mtype.is_tagged then return
+                       if mtype.is_tagged then return true
 
                        #Build instance struct
                        self.header.add_decl("struct instance_{c_name} \{")
@@ -887,7 +891,7 @@ class SeparateCompiler
                        self.header.add_decl("\};")
 
                        # Pointer is needed by extern types, live or not
-                       if is_dead and mtype.mclass.name != "Pointer" then return
+                       if is_dead and mtype.mclass.name != "Pointer" then return true
 
                        #Build BOX
                        self.provide_declaration("BOX_{c_name}", "val* BOX_{c_name}({mtype.ctype_extern});")
@@ -905,7 +909,7 @@ class SeparateCompiler
                        v.add("\}")
 
                        # A Pointer class also need its constructor
-                       if mtype.mclass.name != "Pointer" then return
+                       if mtype.mclass.name != "Pointer" then return true
 
                        v = new_visitor
                        self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
@@ -926,7 +930,7 @@ class SeparateCompiler
                                v.add("return {res};")
                        end
                        v.add("\}")
-                       return
+                       return true
                else if mclass.name == "NativeArray" then
                        #Build instance struct
                        self.header.add_decl("struct instance_{c_name} \{")
@@ -953,7 +957,7 @@ class SeparateCompiler
                        v.add("{res}->length = length;")
                        v.add("return (val*){res};")
                        v.add("\}")
-                       return
+                       return true
                else if mclass.name == "RoutineRef" then
                        self.header.add_decl("struct instance_{c_name} \{")
                        self.header.add_decl("const struct type *type;")
@@ -976,7 +980,7 @@ class SeparateCompiler
                        v.add("{res}->method = method;")
                        v.add("return (val*){res};")
                        v.add("\}")
-                       return
+                       return true
                else if mtype.mclass.kind == extern_kind and mtype.mclass.name != "CString" then
                        # Is an extern class (other than Pointer and CString)
                        # Pointer is caught in a previous `if`, and CString is internal
@@ -1001,8 +1005,17 @@ class SeparateCompiler
                                v.add("return {res};")
                        end
                        v.add("\}")
-                       return
+                       return true
                end
+               return false
+       end
+
+       protected fun compile_default_new(ccinfo: ClassCompilationInfo, v: SeparateCompilerVisitor)
+       do
+               var mclass = ccinfo.mclass
+               var mtype = ccinfo.mtype
+               var c_name = ccinfo.mclass.c_name
+               var is_dead = ccinfo.is_dead
 
                #Build NEW
                self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
@@ -1036,6 +1049,35 @@ class SeparateCompiler
                        v.add("return {res};")
                end
                v.add("\}")
+
+       end
+
+       protected fun build_class_compilation_info(mclass: MClass): ClassCompilationInfo
+       do
+               var mtype = mclass.intro.bound_mtype
+               var rta = runtime_type_analysis
+               var is_dead = rta != null and not rta.live_classes.has(mclass)
+
+               # While the class may be dead, some part of separately compiled code may use symbols associated to the class, so
+               # in order to compile and link correctly the C code, these symbols should be declared and defined.
+               var need_corpse = is_dead and mtype.is_c_primitive or mclass.kind == extern_kind or mclass.kind == enum_kind
+
+               var compilation_info = new ClassCompilationInfo(mclass, is_dead, need_corpse)
+               return compilation_info
+       end
+
+       # Globally compile the table of the class mclass
+       # In a link-time optimisation compiler, tables are globally computed
+       # In a true separate compiler (a with dynamic loading) you cannot do this unfortnally
+       fun compile_class_to_c(mclass: MClass)
+       do
+               var v = new_visitor
+               var class_info = build_class_compilation_info(mclass)
+               compile_class_vft(class_info, v)
+               var is_already_managed = compile_class_if_universal(class_info, v)
+               if not is_already_managed then
+                       compile_default_new(class_info, v)
+               end
        end
 
        # Compile structures used to map tagged primitive values to their classes and types.
@@ -2551,6 +2593,34 @@ class SeparateRuntimeFunction
        end
 end
 
+# Encapsulates every information needed to compile a class.
+#
+# The compilation of a class is done by several methods, two of those are
+# mandatory :
+# - compile_class_to_c : starts the compilation process
+# - compile_class_vft : generate the virtual function table
+# And one of them is optional :
+# - compile_class_if_universal : compiles the rest of the class if its a universal
+# type. Universal type are handle in a case-basis, this is why they need special treatment.
+# Generally, universal class will have special structure and a custom allocator.
+#
+# Throughout each step of the class compilation process, some information must be share.
+# This class encapsulates the compilation process state.
+# (except vft), eg
+class ClassCompilationInfo
+       var mclass: MClass # class to compile
+       var is_dead: Bool
+       var need_corpse: Bool
+
+       # Shortcut to access the class's bound type.
+       var mtype: MClassType is noinit
+
+       init
+       do
+               mtype = mclass.intro.bound_mtype
+       end
+end
+
 class SeparateThunkFunction
        super ThunkFunction
        super SeparateRuntimeFunction
index 8e0a0b2..635e601 100644 (file)
@@ -194,74 +194,12 @@ class SeparateErasureCompiler
                self.header.add_decl("typedef struct instance \{ const struct class *class; nitattribute_t attrs[1]; \} val; /* general C type representing a Nit instance. */")
        end
 
-       redef fun compile_class_to_c(mclass: MClass)
+       redef fun compile_class_if_universal(ccinfo, v)
        do
-               var mtype = mclass.intro.bound_mtype
+               var mclass = ccinfo.mclass
+               var mtype = ccinfo.mtype
                var c_name = mclass.c_name
-
-               var class_table = self.class_tables[mclass]
-               var v = self.new_visitor
-
-               var rta = runtime_type_analysis
-               var is_dead = false # mclass.kind == abstract_kind or mclass.kind == interface_kind
-               if not is_dead and rta != null and not rta.live_classes.has(mclass) and not mtype.is_c_primitive and mclass.name != "NativeArray" then
-                       is_dead = true
-               end
-
-               v.add_decl("/* runtime class {c_name} */")
-
-               self.provide_declaration("class_{c_name}", "extern const struct class class_{c_name};")
-               v.add_decl("extern const struct type_table type_table_{c_name};")
-
-               # Build class vft
-               v.add_decl("const struct class class_{c_name} = \{")
-               v.add_decl("{class_ids[mclass]},")
-               v.add_decl("\"{mclass.name}\", /* class_name_string */")
-               v.add_decl("{self.box_kind_of(mclass)}, /* box_kind */")
-               v.add_decl("{class_colors[mclass]},")
-               if not is_dead then
-                       if build_class_vts_table(mclass) then
-                               v.require_declaration("vts_table_{c_name}")
-                               v.add_decl("&vts_table_{c_name},")
-                       else
-                               v.add_decl("NULL,")
-                       end
-                       v.add_decl("&type_table_{c_name},")
-                       v.add_decl("\{")
-                       var vft = self.method_tables.get_or_null(mclass)
-                       if vft != null then for i in [0 .. vft.length[ do
-                               var mpropdef = vft[i]
-                               if mpropdef == null then
-                                       v.add_decl("NULL, /* empty */")
-                               else
-                                       assert mpropdef isa MMethodDef
-                                       if rta != null and not rta.live_methoddefs.has(mpropdef) then
-                                               v.add_decl("NULL, /* DEAD {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
-                                               continue
-                                       end
-                                       var rf = mpropdef.virtual_runtime_function
-                                       v.require_declaration(rf.c_name)
-                                       v.add_decl("(nitmethod_t){rf.c_name}, /* pointer to {mpropdef.full_name} */")
-                               end
-                       end
-                       v.add_decl("\}")
-               end
-               v.add_decl("\};")
-
-               # Build class type table
-
-               v.add_decl("const struct type_table type_table_{c_name} = \{")
-               v.add_decl("{class_table.length},")
-               v.add_decl("\{")
-               for msuper in class_table do
-                       if msuper == null then
-                               v.add_decl("-1, /* empty */")
-                       else
-                               v.add_decl("{self.class_ids[msuper]}, /* {msuper} */")
-                       end
-               end
-               v.add_decl("\}")
-               v.add_decl("\};")
+               var is_dead = ccinfo.is_dead
 
                if mtype.is_c_primitive or mtype.mclass.name == "Pointer" then
                        #Build instance struct
@@ -281,7 +219,7 @@ class SeparateErasureCompiler
                        v.add("return (val*)res;")
                        v.add("\}")
 
-                       if mtype.mclass.name != "Pointer" then return
+                       if mtype.mclass.name != "Pointer" then return true
 
                        v = new_visitor
                        self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}();")
@@ -299,7 +237,7 @@ class SeparateErasureCompiler
                                v.add("return {res};")
                        end
                        v.add("\}")
-                       return
+                       return true
                else if mclass.name == "NativeArray" then
                        #Build instance struct
                        self.header.add_decl("struct instance_{c_name} \{")
@@ -321,7 +259,7 @@ class SeparateErasureCompiler
                        v.add("{res}->length = length;")
                        v.add("return (val*){res};")
                        v.add("\}")
-                       return
+                       return true
                 else if mclass.name == "RoutineRef" then
                         self.header.add_decl("struct instance_{c_name} \{")
                         self.header.add_decl("const struct class *class;")
@@ -341,7 +279,7 @@ class SeparateErasureCompiler
                         v.add("{res}->method = method;")
                         v.add("return (val*){res};")
                         v.add("\}")
-                        return
+                        return true
                else if mtype.mclass.kind == extern_kind and mtype.mclass.name != "CString" then
                        var pointer_type = mainmodule.pointer_type
 
@@ -361,8 +299,84 @@ class SeparateErasureCompiler
                                v.add("return {res};")
                        end
                        v.add("\}")
-                       return
+                       return true
                end
+               return false
+       end
+
+       redef fun compile_class_vft(ccinfo, v)
+       do
+               var mclass = ccinfo.mclass
+               var mtype = ccinfo.mtype
+               var c_name = mclass.c_name
+               var is_dead = ccinfo.is_dead
+               var rta = runtime_type_analysis
+
+               # Build class vft
+               self.provide_declaration("class_{c_name}", "extern const struct class class_{c_name};")
+
+               v.add_decl("const struct class class_{c_name} = \{")
+               v.add_decl("{class_ids[mclass]},")
+               v.add_decl("\"{mclass.name}\", /* class_name_string */")
+               v.add_decl("{self.box_kind_of(mclass)}, /* box_kind */")
+               v.add_decl("{class_colors[mclass]},")
+               if not is_dead then
+                       if build_class_vts_table(mclass) then
+                               v.require_declaration("vts_table_{c_name}")
+                               v.add_decl("&vts_table_{c_name},")
+                       else
+                               v.add_decl("NULL,")
+                       end
+                       v.add_decl("&type_table_{c_name},")
+                       v.add_decl("\{")
+                       var vft = self.method_tables.get_or_null(mclass)
+                       if vft != null then for i in [0 .. vft.length[ do
+                               var mpropdef = vft[i]
+                               if mpropdef == null then
+                                       v.add_decl("NULL, /* empty */")
+                               else
+                                       assert mpropdef isa MMethodDef
+                                       if rta != null and not rta.live_methoddefs.has(mpropdef) then
+                                               v.add_decl("NULL, /* DEAD {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
+                                               continue
+                                       end
+                                       var rf = mpropdef.virtual_runtime_function
+                                       v.require_declaration(rf.c_name)
+                                       v.add_decl("(nitmethod_t){rf.c_name}, /* pointer to {mpropdef.full_name} */")
+                               end
+                       end
+                       v.add_decl("\}")
+               end
+               v.add_decl("\};")
+       end
+
+       protected fun compile_class_type_table(ccinfo: ClassCompilationInfo, v: SeparateCompilerVisitor)
+       do
+               var mclass = ccinfo.mclass
+               var c_name = mclass.c_name
+               var class_table = self.class_tables[mclass]
+
+               # Build class type table
+               v.add_decl("const struct type_table type_table_{c_name} = \{")
+               v.add_decl("{class_table.length},")
+               v.add_decl("\{")
+               for msuper in class_table do
+                       if msuper == null then
+                               v.add_decl("-1, /* empty */")
+                       else
+                               v.add_decl("{self.class_ids[msuper]}, /* {msuper} */")
+                       end
+               end
+               v.add_decl("\}")
+               v.add_decl("\};")
+       end
+
+       redef fun compile_default_new(ccinfo, v)
+       do
+               var mclass = ccinfo.mclass
+               var mtype = ccinfo.mtype
+               var c_name = mclass.c_name
+               var is_dead = ccinfo.is_dead
 
                #Build NEW
                self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(void);")
@@ -391,6 +405,33 @@ class SeparateErasureCompiler
                v.add("\}")
        end
 
+       redef fun build_class_compilation_info(mclass)
+       do
+               var ccinfo = super
+               var mtype = ccinfo.mtype
+               var rta = runtime_type_analysis
+               var is_dead = false # mclass.kind == abstract_kind or mclass.kind == interface_kind
+               if not is_dead and rta != null and not rta.live_classes.has(mclass) and not mtype.is_c_primitive and mclass.name != "NativeArray" then
+                       is_dead = true
+               end
+               ccinfo.is_dead = is_dead
+               return ccinfo
+       end
+
+       redef fun compile_class_to_c(mclass: MClass)
+       do
+               var ccinfo = build_class_compilation_info(mclass)
+               var v = new_visitor
+               v.add_decl("/* runtime class {mclass.c_name} */")
+               self.provide_declaration("class_{mclass.c_name}", "extern const struct class class_{mclass.c_name};")
+               v.add_decl("extern const struct type_table type_table_{mclass.c_name};")
+               self.compile_class_vft(ccinfo, v)
+               self.compile_class_type_table(ccinfo, v)
+               if not self.compile_class_if_universal(ccinfo, v) then
+                       self.compile_default_new(ccinfo, v)
+               end
+       end
+
        private fun build_class_vts_table(mclass: MClass): Bool do
                if self.vt_tables[mclass].is_empty then return false
 
index 15b1de6..be92ab6 100644 (file)
@@ -305,8 +305,8 @@ class NeoModel
        # Collect all nodes from the current `model`.
        private fun collect_model_nodes(model: Model): Collection[NeoNode] do
                for mpackage in model.mpackages do
-                       to_node(mpackage)
-                       for mgroup in mpackage.mgroups do to_node(mgroup)
+                       mpackage.to_node(nodes, model_name)
+                       for mgroup in mpackage.mgroups do mgroup.to_node(nodes, model_name)
                end
                return nodes.values
        end
@@ -328,22 +328,6 @@ class NeoModel
        # Nodes associated with MEntities.
        private var nodes = new HashMap[MEntity, NeoNode]
 
-       # Get the `NeoNode` associated with `mentity`.
-       # `mentities` are stored locally to avoid duplication.
-       fun to_node(mentity: MEntity): NeoNode do
-               if nodes.has_key(mentity) then return nodes[mentity]
-               if mentity isa MPackage then return mpackage_node(mentity)
-               if mentity isa MGroup then return mgroup_node(mentity)
-               if mentity isa MModule then return mmodule_node(mentity)
-               if mentity isa MClass then return mclass_node(mentity)
-               if mentity isa MClassDef then return mclassdef_node(mentity)
-               if mentity isa MProperty then return mproperty_node(mentity)
-               if mentity isa MPropDef then return mpropdef_node(mentity)
-               if mentity isa MType then return mtype_node(mentity)
-               if mentity isa MParameter then return mparameter_node(mentity)
-               abort
-       end
-
        # Get the `MEntity` associated with `node`.
        fun to_mentity(model: Model, node: NeoNode): MEntity do
                if node.labels.has("MPackage") then return to_mpackage(model, node)
@@ -358,36 +342,6 @@ class NeoModel
                abort
        end
 
-       # Make a new `NeoNode` based on `mentity`.
-       private fun make_node(mentity: MEntity): NeoNode do
-               var node = new NeoNode
-               nodes[mentity] = node
-               node.labels.add "MEntity"
-               node.labels.add model_name
-               node["name"] = mentity.name
-               if not mentity isa MSignature then
-                       #FIXME: MSignature is a MEntity, but has no model :/
-                       node["location"] = mentity.location.to_s
-               end
-               var mdoc = mentity.mdoc
-               if mdoc != null then
-                       node["mdoc"] = new JsonArray.from(mdoc.content)
-                       node["mdoc_location"] = mdoc.location.to_s
-               end
-               return node
-       end
-
-       # Build a `NeoNode` representing `mpackage`.
-       private fun mpackage_node(mpackage: MPackage): NeoNode do
-               var node = make_node(mpackage)
-               node.labels.add "MPackage"
-               var root = mpackage.root
-               if root != null then
-                       node.out_edges.add(new NeoEdge(node, "ROOT", to_node(root)))
-               end
-               return node
-       end
-
        # Build a new `MPackage` from a `node`.
        #
        # REQUIRE `node.labels.has("MPackage")`
@@ -404,24 +358,6 @@ class NeoModel
                return mpackage
        end
 
-       # Build a `NeoNode` representing `mgroup`.
-       private fun mgroup_node(mgroup: MGroup): NeoNode do
-               var node = make_node(mgroup)
-               node.labels.add "MGroup"
-               var parent = mgroup.parent
-               node.out_edges.add(new NeoEdge(node, "PROJECT", to_node(mgroup.mpackage)))
-               if parent != null then
-                       node.out_edges.add(new NeoEdge(node, "PARENT", to_node(parent)))
-               end
-               for mmodule in mgroup.mmodules do
-                       node.out_edges.add(new NeoEdge(node, "DECLARES", to_node(mmodule)))
-               end
-               for subgroup in mgroup.in_nesting.direct_smallers do
-                       node.in_edges.add(new NeoEdge(node, "NESTS", to_node(subgroup)))
-               end
-               return node
-       end
-
        # Build a new `MGroup` from a `node`.
        #
        # REQUIRE `node.labels.has("MGroup")`
@@ -443,22 +379,6 @@ class NeoModel
                return mgroup
        end
 
-       # Build a `NeoNode` representing `mmodule`.
-       private fun mmodule_node(mmodule: MModule): NeoNode do
-               var node = make_node(mmodule)
-               node.labels.add "MModule"
-               for parent in mmodule.in_importation.direct_greaters do
-                       node.out_edges.add(new NeoEdge(node, "IMPORTS", to_node(parent)))
-               end
-               for mclass in mmodule.intro_mclasses do
-                       node.out_edges.add(new NeoEdge(node, "INTRODUCES", to_node(mclass)))
-               end
-               for mclassdef in mmodule.mclassdefs do
-                       node.out_edges.add(new NeoEdge(node, "DEFINES", to_node(mclassdef)))
-               end
-               return node
-       end
-
        # Build a new `MModule` from a `node`.
        #
        # REQUIRE `node.labels.has("MModule")`
@@ -485,21 +405,6 @@ class NeoModel
                return mmodule
        end
 
-       # Build a `NeoNode` representing `mclass`.
-       private fun mclass_node(mclass: MClass): NeoNode do
-               var node = make_node(mclass)
-               node.labels.add "MClass"
-               node["kind"] = mclass.kind.to_s
-               node["visibility"] = mclass.visibility.to_s
-               if not mclass.mparameters.is_empty then
-                       var parameter_names = new Array[String]
-                       for p in mclass.mparameters do parameter_names.add(p.name)
-                       node["parameter_names"] = new JsonArray.from(parameter_names)
-               end
-               node.out_edges.add(new NeoEdge(node, "CLASSTYPE", to_node(mclass.mclass_type)))
-               return node
-       end
-
        # Build a new `MClass` from a `node`.
        #
        # REQUIRE `node.labels.has("MClass")`
@@ -525,24 +430,6 @@ class NeoModel
                return mclass
        end
 
-       # Build a `NeoNode` representing `mclassdef`.
-       private fun mclassdef_node(mclassdef: MClassDef): NeoNode do
-               var node = make_node(mclassdef)
-               node.labels.add "MClassDef"
-               node.out_edges.add(new NeoEdge(node, "BOUNDTYPE", to_node(mclassdef.bound_mtype)))
-               node.out_edges.add(new NeoEdge(node, "MCLASS", to_node(mclassdef.mclass)))
-               for mproperty in mclassdef.intro_mproperties do
-                       node.out_edges.add(new NeoEdge(node, "INTRODUCES", to_node(mproperty)))
-               end
-               for mpropdef in mclassdef.mpropdefs do
-                       node.out_edges.add(new NeoEdge(node, "DECLARES", to_node(mpropdef)))
-               end
-               for sup in mclassdef.supertypes do
-                       node.out_edges.add(new NeoEdge(node, "INHERITS", to_node(sup)))
-               end
-               return node
-       end
-
        # Build a new `MClassDef` from a `node`.
        #
        # REQUIRE `node.labels.has("MClassDef")`
@@ -566,26 +453,6 @@ class NeoModel
                return mclassdef
        end
 
-       # Build a `NeoNode` representing `mproperty`.
-       private fun mproperty_node(mproperty: MProperty): NeoNode do
-               var node = make_node(mproperty)
-               node.labels.add "MProperty"
-               node["visibility"] = mproperty.visibility.to_s
-               if mproperty isa MMethod then
-                       node.labels.add "MMethod"
-                       node["is_init"] = mproperty.is_init
-               else if mproperty isa MAttribute then
-                       node.labels.add "MAttribute"
-               else if mproperty isa MVirtualTypeProp then
-                       node.labels.add "MVirtualTypeProp"
-               else if mproperty isa MInnerClass then
-                       node.labels.add "MInnerClass"
-                       node.out_edges.add(new NeoEdge(node, "NESTS", to_node(mproperty.inner)))
-               end
-               node.out_edges.add(new NeoEdge(node, "INTRO_CLASSDEF", to_node(mproperty.intro_mclassdef)))
-               return node
-       end
-
        # Build a new `MProperty` from a `node`.
        #
        # REQUIRE `node.labels.has("MProperty")`
@@ -619,39 +486,6 @@ class NeoModel
                return mprop
        end
 
-       # Build a `NeoNode` representing `mpropdef`.
-       private fun mpropdef_node(mpropdef: MPropDef): NeoNode do
-               var node = make_node(mpropdef)
-               node.labels.add "MPropDef"
-               node.out_edges.add(new NeoEdge(node, "DEFINES", to_node(mpropdef.mproperty)))
-               if mpropdef isa MMethodDef then
-                       node.labels.add "MMethodDef"
-                       node["is_abstract"] = mpropdef.is_abstract
-                       node["is_intern"] = mpropdef.is_intern
-                       node["is_extern"] = mpropdef.is_extern
-                       var msignature = mpropdef.msignature
-                       if msignature != null then
-                               node.out_edges.add(new NeoEdge(node, "SIGNATURE", to_node(msignature)))
-                       end
-               else if mpropdef isa MAttributeDef then
-                       node.labels.add "MAttributeDef"
-                       var static_mtype = mpropdef.static_mtype
-                       if static_mtype != null then
-                               node.out_edges.add(new NeoEdge(node, "TYPE", to_node(static_mtype)))
-                       end
-               else if mpropdef isa MVirtualTypeDef then
-                       node.labels.add "MVirtualTypeDef"
-                       var bound = mpropdef.bound
-                       if bound != null then
-                               node.out_edges.add(new NeoEdge(node, "BOUND", to_node(bound)))
-                       end
-               else if mpropdef isa MInnerClassDef then
-                       node.labels.add "MInnerClassDef"
-                       node.out_edges.add(new NeoEdge(node, "NESTS", to_node(mpropdef.inner)))
-               end
-               return node
-       end
-
        # Build a new `MPropDef` from a `node`.
        #
        # REQUIRE `node.labels.has("MPropDef")`
@@ -695,74 +529,6 @@ class NeoModel
                return mpropdef
        end
 
-       # Build a `NeoNode` representing `mtype`.
-       private fun mtype_node(mtype: MType): NeoNode do
-               var node = make_node(mtype)
-               node.labels.add "MType"
-               if mtype isa MClassType then
-                       node.labels.add "MClassType"
-                       node.out_edges.add(new NeoEdge(node, "CLASS", to_node(mtype.mclass)))
-                       for arg in mtype.arguments do
-                               node.out_edges.add(new NeoEdge(node, "ARGUMENT", to_node(arg)))
-                       end
-                       if mtype isa MGenericType then
-                               node.labels.add "MGenericType"
-                       end
-               else if mtype isa MVirtualType then
-                       node.labels.add "MVirtualType"
-                       node.out_edges.add(new NeoEdge(node, "PROPERTY", to_node(mtype.mproperty)))
-               else if mtype isa MParameterType then
-                       node.labels.add "MParameterType"
-                       node["rank"] = mtype.rank
-                       node.out_edges.add(new NeoEdge(node, "CLASS", to_node(mtype.mclass)))
-               else if mtype isa MNullableType then
-                       node.labels.add "MNullableType"
-                       node.out_edges.add(new NeoEdge(node, "TYPE", to_node(mtype.mtype)))
-               else if mtype isa MSignature then
-                       node.labels.add "MSignature"
-                       var names = new JsonArray
-                       var rank = 0
-                       for mparameter in mtype.mparameters do
-                               names.add mparameter.name
-                               var pnode = to_node(mparameter)
-                               pnode["rank"] = rank
-                               node.out_edges.add(new NeoEdge(node, "PARAMETER", pnode))
-                               rank += 1
-                       end
-                       if not names.is_empty then node["parameter_names"] = names
-                       var return_mtype = mtype.return_mtype
-                       if return_mtype != null then
-                               node.out_edges.add(new NeoEdge(node, "RETURNTYPE", to_node(return_mtype)))
-                       end
-               else if mtype isa MRawType then
-                       node.labels.add "MRawType"
-                       var text = new JsonArray
-                       var rank = 0
-                       for part in mtype.parts do
-                               text.add part.text
-                               if part.target != null then
-                                       var pnode = mtypepart_node(part)
-                                       pnode["rank"] = rank
-                                       node.out_edges.add(new NeoEdge(node, "LINK", pnode))
-                               end
-                               rank += 1
-                       end
-                       if not text.is_empty then node["text"] = text
-               end
-               return node
-       end
-
-       # Build a `NeoNode` representing `mtypepart`.
-       private fun mtypepart_node(mtypepart: MTypePart): NeoNode do
-               var node = make_node(mtypepart)
-               node.labels.add "MTypePart"
-               if mtypepart.target != null then
-                       var target_node = to_node(mtypepart.target.as(not null))
-                       node.out_edges.add(new NeoEdge(node, "TARGET", target_node))
-               end
-               return node
-       end
-
        # Build a new `MType` from a `node`.
        #
        # REQUIRE `node.labels.has("MType")`
@@ -845,16 +611,6 @@ class NeoModel
                abort
        end
 
-       # Build a `NeoNode` representing `mparameter`.
-       private fun mparameter_node(mparameter: MParameter): NeoNode do
-               var node = make_node(mparameter)
-               node.labels.add "MParameter"
-               node["name"] = mparameter.name
-               node["is_vararg"] = mparameter.is_vararg
-               node.out_edges.add(new NeoEdge(node, "TYPE", to_node(mparameter.mtype)))
-               return node
-       end
-
        # Build a new `MParameter` from `node`.
        #
        # REQUIRE `node.labels.has("MParameter")`
@@ -925,3 +681,334 @@ class NeoModel
                end
        end
 end
+
+redef class MPackage
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               var root = root
+               if root != null then
+                       node.out_edges.add(new NeoEdge(node, "ROOT", root.to_node(nodes, model_name)))
+               end
+               return node
+       end
+end
+
+redef class MGroup
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               var parent = parent
+               node.out_edges.add(new NeoEdge(node, "PROJECT", mpackage.to_node(nodes, model_name)))
+               if parent != null then
+                       node.out_edges.add(new NeoEdge(node, "PARENT", parent.to_node(nodes, model_name)))
+               end
+               for mmodule in mmodules do
+                       node.out_edges.add(new NeoEdge(node, "DECLARES", mmodule.to_node(nodes, model_name)))
+               end
+               for subgroup in in_nesting.direct_smallers do
+                       node.in_edges.add(new NeoEdge(node, "NESTS", subgroup.to_node(nodes, model_name)))
+               end
+               return node
+       end
+end
+
+redef class MModule
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               for parent in in_importation.direct_greaters do
+                       node.out_edges.add(new NeoEdge(node, "IMPORTS", parent.to_node(nodes, model_name)))
+               end
+               for mclass in intro_mclasses do
+                       node.out_edges.add(new NeoEdge(node, "INTRODUCES", mclass.to_node(nodes, model_name)))
+               end
+               for mclassdef in mclassdefs do
+                       node.out_edges.add(new NeoEdge(node, "DEFINES", mclassdef.to_node(nodes, model_name)))
+               end
+               return node
+       end
+end
+
+redef class MClass
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node["kind"] = kind.to_s
+               node["visibility"] = visibility.to_s
+               if not mparameters.is_empty then
+                       var parameter_names = new Array[String]
+                       for p in mparameters do parameter_names.add(p.name)
+                       node["parameter_names"] = new JsonArray.from(parameter_names)
+               end
+               node.out_edges.add(new NeoEdge(node, "CLASSTYPE", mclass_type.to_node(nodes, model_name)))
+               return node
+       end
+end
+
+redef class MClassDef
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node.out_edges.add(new NeoEdge(node, "BOUNDTYPE", bound_mtype.to_node(nodes, model_name)))
+               node.out_edges.add(new NeoEdge(node, "MCLASS", mclass.to_node(nodes, model_name)))
+               for mproperty in intro_mproperties do
+                       node.out_edges.add(new NeoEdge(node, "INTRODUCES", mproperty.to_node(nodes, model_name)))
+               end
+               for mpropdef in mpropdefs do
+                       node.out_edges.add(new NeoEdge(node, "DECLARES", mpropdef.to_node(nodes, model_name)))
+               end
+               for sup in supertypes do
+                       node.out_edges.add(new NeoEdge(node, "INHERITS", sup.to_node(nodes, model_name)))
+               end
+               return node
+       end
+end
+
+redef class MProperty
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = make_node(nodes, model_name)
+               node.labels.add "MProperty"
+               node["visibility"] = visibility.to_s
+               node.out_edges.add(new NeoEdge(node, "INTRO_CLASSDEF", intro_mclassdef.to_node(nodes, model_name)))
+               node.labels.add self.class_name
+               return node
+       end
+end
+
+redef class MMethod
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node["is_init"] = is_init
+               return node
+       end
+end
+
+redef class MInnerClass
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node.out_edges.add(new NeoEdge(node, "NESTS", inner.to_node(nodes, model_name)))
+               return node
+       end
+end
+
+redef class MPropDef
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = make_node(nodes, model_name)
+               node.labels.add "MPropDef"
+               node.out_edges.add(new NeoEdge(node, "DEFINES", mproperty.to_node(nodes, model_name)))
+               node.labels.add self.class_name
+               return node
+       end
+end
+
+redef class MMethodDef
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node["is_abstract"] = is_abstract
+               node["is_intern"] = is_intern
+               node["is_extern"] = is_extern
+               var msignature = msignature
+               if msignature != null then
+                       node.out_edges.add(new NeoEdge(node, "SIGNATURE", msignature.to_node(nodes, model_name)))
+               end
+               return node
+       end
+end
+
+redef class MAttributeDef
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               var static_mtype = static_mtype
+               if static_mtype != null then
+                       node.out_edges.add(new NeoEdge(node, "TYPE", static_mtype.to_node(nodes, model_name)))
+               end
+               return node
+       end
+end
+
+redef class MVirtualTypeDef
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               var bound = bound
+               if bound != null then
+                       node.out_edges.add(new NeoEdge(node, "BOUND", bound.to_node(nodes, model_name)))
+               end
+               return node
+       end
+end
+
+redef class MInnerClassDef
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node.out_edges.add(new NeoEdge(node, "NESTS", inner.to_node(nodes, model_name)))
+               return node
+       end
+end
+
+redef class MTypePart
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               if self.target != null then
+                       var target_node = self.target.as(not null).to_node(nodes, model_name)
+                       node.out_edges.add(new NeoEdge(node, "TARGET", target_node))
+               end
+               return node
+       end
+end
+
+redef class MParameter
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node["name"] = self.name
+               node["is_vararg"] = self.is_vararg
+               node.out_edges.add(new NeoEdge(node, "TYPE", self.mtype.to_node(nodes, model_name)))
+               return node
+       end
+end
+
+redef class MClassType
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node.labels.add "MClassType"
+               node.out_edges.add(new NeoEdge(node, "CLASS", mclass.to_node(nodes, model_name)))
+               for arg in arguments do
+                       node.out_edges.add(new NeoEdge(node, "ARGUMENT", arg.to_node(nodes, model_name)))
+               end
+               return node
+       end
+end
+
+
+redef class MGenericType
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node.labels.add "MGenericType"
+               return node
+       end
+end
+
+redef class MVirtualType
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node.labels.add "MVirtualType"
+               node.out_edges.add(new NeoEdge(node, "PROPERTY", mproperty.to_node(nodes, model_name)))
+               return node
+       end
+end
+
+redef class MParameterType
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node.labels.add "MParameterType"
+               node["rank"] = rank
+               node.out_edges.add(new NeoEdge(node, "CLASS", mclass.to_node(nodes, model_name)))
+               return node
+       end
+end
+
+redef class MNullableType
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node.labels.add "MNullableType"
+               node.out_edges.add(new NeoEdge(node, "TYPE", mtype.to_node(nodes, model_name)))
+               return node
+       end
+end
+
+redef class MSignature
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node.labels.add "MSignature"
+               var names = new JsonArray
+               var rank = 0
+               for mparameter in mparameters do
+                       names.add mparameter.name
+                       var pnode = mparameter.to_node(nodes, model_name)
+                       pnode["rank"] = rank
+                       node.out_edges.add(new NeoEdge(node, "PARAMETER", pnode))
+                       rank += 1
+               end
+               if not names.is_empty then node["parameter_names"] = names
+               var return_mtype = return_mtype
+               if return_mtype != null then
+                       node.out_edges.add(new NeoEdge(node, "RETURNTYPE", return_mtype.to_node(nodes, model_name)))
+               end
+               return node
+       end
+end
+
+redef class MRawType
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = super
+               node.labels.add "MRawType"
+               var text = new JsonArray
+               var rank = 0
+               for part in self.parts do
+                       text.add part.text
+                       if part.target != null then
+                               var pnode = part.to_node(nodes, model_name)
+                               pnode["rank"] = rank
+                               node.out_edges.add(new NeoEdge(node, "LINK", pnode))
+                       end
+                       rank += 1
+               end
+               if not text.is_empty then node["text"] = text
+               return node
+       end
+end
+
+redef class MType
+       redef fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = make_node(nodes, model_name)
+               node.labels.add "MType"
+               return node
+       end
+end
+
+redef class MEntity
+       # Build a `NeoNode` representing `self`.
+       private fun to_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               if nodes.has_key(self) then return nodes[self]
+               var node = make_node(nodes, model_name)
+               node.labels.add self.class_name
+               return node
+       end
+
+       # Make a new `NeoNode` based on `mentity`.
+       private fun make_node(nodes: HashMap[MEntity, NeoNode], model_name: nullable String): NeoNode do
+               var node = new NeoNode
+               nodes[self] = node
+               node.labels.add "MEntity"
+               if model_name != null then node.labels.add model_name
+               node["name"] = self.name
+               if not self isa MSignature then
+                       #FIXME: MSignature is a MEntity, but has no model :/
+                       node["location"] = self.location.to_s
+               end
+               var mdoc = self.mdoc
+               if mdoc != null then
+                       node["mdoc"] = new JsonArray.from(mdoc.content)
+                       node["mdoc_location"] = mdoc.location.to_s
+               end
+               return node
+       end
+end