fun generate_actor_classes(mclassdef: MClassDef, mmod: MModule) do
if not mmod.generate_actor_submodule then mmod.generate_actor_submodule = true
# Get the name of the annotated class
var classname = mclassdef.name
# Generate the actor class
if mclassdef.is_intro then actors.add(
"""
class Actor{{{classname}}}
super Actor
redef type E: nullable {{{classname}}}
end
""")
######## Generate the Messages classes ########
# Get all the methods definitions
var propdefs = new Array[MPropDef]
for propdef in mclassdef.mpropdefs do
if propdef.is_intro then propdefs.add(propdef)
end
var methods = new Array[MMethodDef]
for p in propdefs do
if p isa MMethodDef then
# TODO: find a better way to exclude constructors,
# getters/setters and the "async" (the actor)
if p.name.has("=") or p.name.has("async") or p.mproperty.is_init then continue
methods.add(p)
end
end
# Generate the superclass for all Messages classes (each actor has its own Message super class)
var msg_class_name = "Message" + classname
if mclassdef.is_intro then messages.add(
"""
class {{{msg_class_name}}}
super Message
redef type E: {{{classname}}}
end
""")
# Generate every Message class based on the methods of the annotated class
var proxy_calls = new Array[String]
for m in methods do
# Signature of the method
var signature = m.msignature
# Name of the method
var method_name = m.name
# Attributes of the `Message` class if needed
# Corresponds to the parameters of the proxied method
var msg_attributes = new Array[String]
# Signature of the proxy corresponding method
var proxy_sign = ""
# Values for the body of the `invoke` method of the generated Message class
# Used if the call must return a value
var return_value = ""
var return_parenthesis = ""
# Params to send to `instance` in the `invoke` method
var params = ""
# Values for the generated proxy method
var return_signature = ""
var return_statement = ""
if signature != null then
var proxy_params= new Array[String]
# Deal with parameters
var mparameters = signature.mparameters
if mparameters.length > 0 then
var parameters = new Array[String]
proxy_sign += "("
for p in mparameters do
var n = p.name
var t = p.mtype.name
msg_attributes.add("var " + n + ": " + t)
proxy_params.add(n + ": " + t)
parameters.add(n)
end
proxy_sign += proxy_params.join(", ") + ")"
params = "(" + parameters.join(", ") + ")"
end
# Deal with the return if any
var ret_type = signature.return_mtype
if ret_type != null then
msg_attributes.add("var ret = new Future[{ret_type.name}]")
return_value = "ret.set_value("
return_parenthesis = ")"
return_signature = ": Future[{ret_type.name}]"
return_statement = "return msg.ret"
end
end
# Name of the Message class
var name = classname + "Message" + method_name
# The effective Message Class
messages.add(
"""
class {{{name}}}
super {{{msg_class_name}}}
{{{msg_attributes.join("\n")}}}
redef fun invoke(instance) do {{{return_value}}}instance.{{{method_name}}}{{{params}}}{{{return_parenthesis}}}
end
""")
# The actual proxy call
proxy_calls.add(
"""
redef fun {{{method_name}}}{{{proxy_sign}}}{{{return_signature}}} do
var msg = new {{{name}}}{{{params}}}
actor.mailbox.push(msg)
{{{return_statement}}}
end
""")
end
# At this point, all msg classes should be good
# All of the functions of the proxy too
# Let's generate the proxy class then
var redef_virtual_type = ""
if mclassdef.is_intro then redef_virtual_type = "redef type E: Actor{classname}"
proxys.add(
"""
redef class Proxy{{{classname}}}
{{{redef_virtual_type}}}
init proxy(base_class: {{{classname}}}) do
actor = new Actor{{{classname}}}(base_class)
actor.start
end
{{{proxy_calls.join("\n\n")}}}
end
""")
if mclassdef.is_intro then redef_classes.add(
"""
redef class {{{classname}}}
var m = new Mutex
var lazy_proxy: Proxy{{{classname}}} is lazy do return new Proxy{{{classname}}}.proxy(self)
redef fun async: Proxy{{{classname}}} do
m.lock
var p = lazy_proxy
m.unlock
return p
end
end
""")
end
src/frontend/actors_generation_phase.nit:40,2--204,4