Actors model injection
authorBlackMinou <romain.chanoir@viacesi.fr>
Mon, 30 Jan 2017 17:09:10 +0000 (12:09 -0500)
committerBlackMinou <romain.chanoir@viacesi.fr>
Tue, 21 Feb 2017 01:33:00 +0000 (20:33 -0500)
Signed-off-by: BlackMinou <romain.chanoir@viacesi.fr>

src/frontend/actors_injection_phase.nit [new file with mode: 0644]

diff --git a/src/frontend/actors_injection_phase.nit b/src/frontend/actors_injection_phase.nit
new file mode 100644 (file)
index 0000000..ded6a70
--- /dev/null
@@ -0,0 +1,114 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Injects model for the classes annotated with "is actor" so
+# that the later generated code can be properly used for compilation
+module actors_injection_phase
+
+import modelize_class
+intrude import modelize_property
+import parser_util
+
+redef class ModelBuilder
+       redef fun build_a_mclass(nmodule, nclassdef)
+       do
+               super
+
+               # Catch the wanted annotation
+               var at = nclassdef.get_single_annotation("actor", self)
+               if at == null then return
+
+               # Get context information
+               var mod = nmodule.mmodule
+               if mod == null then return
+               var mclass = nclassdef.mclass
+               if mclass == null then return
+               if mclass.intro_mmodule != mod then
+                       error(at, "`actor` can only be used at introductions.")
+                       return
+               end
+
+               var l = at.location
+
+               var injected_name = "Proxy"
+
+               # Create the actor class
+               var actor_class = new MClass(mod, injected_name + mclass.name, l, null, concrete_kind, public_visibility)
+               var actor_class_definition = new MClassDef(mod, actor_class.mclass_type, l)
+               actor_class_definition.set_supertypes([mod.object_type])
+               var proxy_classes = mclass.model.get_mclasses_by_name("Proxy")
+               assert proxy_classes != null
+               var proxy_class = proxy_classes.first
+               actor_class_definition.supertypes.add(proxy_class.mclass_type)
+
+               # Register it
+               mclass.actor = actor_class
+       end
+
+       redef fun build_properties(nclassdef)
+       do
+               if nclassdef.build_properties_is_done then return
+               super
+
+               # Get context information
+               var mclass = nclassdef.mclass
+               if mclass == null then return
+               var mclass_def = nclassdef.mclassdef
+               if mclass_def == null then return
+
+               var actor_mclass = mclass.actor
+               if actor_mclass == null then return
+               var actor_mclass_def = actor_mclass.mclassdefs.first
+
+               # Adds an `async` attribute in the worker class which is the actor class
+               if mclass_def.is_intro then
+                       var async = new MMethod(mclass_def, "async", mclass.location, public_visibility)
+                       var async_def = new MMethodDef(mclass_def, async, mclass.location)
+                       async_def.msignature = new MSignature(new Array[MParameter], actor_mclass.mclass_type)
+                       async_def.is_abstract = true
+               end
+
+               # For each introduced property
+               for method in mclass_def.mpropdefs do
+                       if not method isa MMethodDef then continue
+                       if not method.is_intro then continue
+                       if method.name == "async" then continue
+
+                       # Create a proxied method
+                       var actor_method = new MMethod(actor_mclass_def, method.name, actor_mclass.location, method.mproperty.visibility)
+                       var actor_method_def = new MMethodDef(actor_mclass_def, actor_method, actor_mclass.location)
+
+                       # Get the signature of the method ( replacing the return value with a Future if there is one)
+                       var signature = method.msignature
+                       if signature != null then
+                               var parameters = signature.mparameters
+                               var s_return_type = signature.return_mtype
+                               if s_return_type != null then
+                                       var future_mclasses = mclass_def.model.get_mclasses_by_name("Future")
+                                       assert future_mclasses != null
+                                       var future_mclass = future_mclasses.first
+                                       var return_type = future_mclass.get_mtype([s_return_type])
+                                       actor_method_def.msignature = new MSignature(parameters, return_type)
+                               else
+                                       actor_method_def.msignature = new MSignature(parameters, null)
+                               end
+                       end
+                       actor_method_def.is_abstract = true
+               end
+       end
+end
+
+redef class MClass
+       # Adding the actor class to a class annotated with "is actor"
+       var actor: nullable MClass = null
+end