Async RPC support #540

Merged
sb10q merged 2 commits from mwojcik/nac3:async_rpc into master 2024-09-13 12:12:14 +08:00
3 changed files with 48 additions and 32 deletions
Showing only changes of commit 5a5656e983 - Show all commits

View File

@ -832,7 +832,6 @@ fn rpc_codegen_callback_fn<'ctx>(
let ptr_type = int8.ptr_type(AddressSpace::default());
let tag_ptr_type = ctx.ctx.struct_type(&[ptr_type.into(), size_type.into()], false);
let service_id = int32.const_int(fun.1 .0 as u64, false);
// -- setup rpc tags
let mut tag = Vec::new();
@ -950,7 +949,11 @@ fn rpc_codegen_callback_fn<'ctx>(
)
});
ctx.builder
.build_call(rpc_send_async, &[service_id.into(), tag_ptr.into(), args_ptr.into()], "rpc.send")
.build_call(
rpc_send_async,
&[service_id.into(), tag_ptr.into(), args_ptr.into()],
"rpc.send",
)
.unwrap();
} else {
let rpc_send = ctx.module.get_function("rpc_send").unwrap_or_else(|| {

View File

@ -34,16 +34,12 @@ use nac3core::inkwell::{
targets::*,
OptimizationLevel,
};
use itertools::Itertools;
use nac3core::codegen::{gen_func_impl, CodeGenLLVMOptions, CodeGenTargetMachineOptions};
use nac3core::toplevel::builtins::get_exn_constructor;
use nac3core::typecheck::typedef::{into_var_map, TypeEnum, Unifier, VarMap};
use nac3parser::{
use nac3core::nac3parser::{
ast::{Constant, ExprKind, Located, Stmt, StmtKind, StrRef},
parser::parse_program,
};
use nac3core::toplevel::builtins::get_exn_constructor;
use nac3core::typecheck::typedef::{into_var_map, TypeEnum, Unifier, VarMap};
use pyo3::create_exception;
use pyo3::prelude::*;
use pyo3::{exceptions, types::PyBytes, types::PyDict, types::PySet};
@ -139,7 +135,7 @@ struct Nac3 {
string_store: Arc<RwLock<HashMap<String, i32>>>,
exception_ids: Arc<RwLock<HashMap<usize, usize>>>,
deferred_eval_store: DeferredEvaluationStore,
/// LLVM-related options for code generzation.
/// LLVM-related options for code generation.
llvm_options: CodeGenLLVMOptions,
}
@ -479,12 +475,24 @@ impl Nac3 {
match &stmt.node {
StmtKind::FunctionDef { decorator_list, .. } => {
if decorator_list.iter().any(|decorator| decorator_id_string(decorator) == Some("rpc".to_string())) {
store_fun.call1(py, (def_id.0.into_py(py), module.getattr(py, name.to_string().as_str()).unwrap())).unwrap();
let is_async = decorator_list.iter().any(
|decorator| decorator_get_flags(decorator).iter().any(
|constant| *constant == Constant::Str("async".into())
));
if decorator_list
.iter()
.any(|decorator| decorator_id_string(decorator) == Some("rpc".to_string()))
{
store_fun
.call1(
py,
(
def_id.0.into_py(py),
module.getattr(py, name.to_string().as_str()).unwrap(),
),
)
.unwrap();
let is_async = decorator_list.iter().any(|decorator| {
decorator_get_flags(decorator)
.iter()
.any(|constant| *constant == Constant::Str("async".into()))
});
rpc_ids.push((None, def_id, is_async));
}
}
@ -493,11 +501,14 @@ impl Nac3 {
let class_obj = module.getattr(py, class_name.as_str()).unwrap();
for stmt in body {
if let StmtKind::FunctionDef { name, decorator_list, .. } = &stmt.node {
if decorator_list.iter().any(|decorator| decorator_id_string(decorator) == Some("rpc".to_string())) {
let is_async = decorator_list.iter().any(
|decorator| decorator_get_flags(decorator).iter().any(
|constant| *constant == Constant::Str("async".into())
));
if decorator_list.iter().any(|decorator| {
decorator_id_string(decorator) == Some("rpc".to_string())
}) {
let is_async = decorator_list.iter().any(|decorator| {
decorator_get_flags(decorator)
.iter()
.any(|constant| *constant == Constant::Str("async".into()))
});
if name == &"__init__".into() {
return Err(CompileError::new_err(format!(
"compilation failed\n----------\nThe constructor of class {} should not be decorated with rpc decorator (at {})",
@ -509,7 +520,7 @@ impl Nac3 {
}
}
}
_ => ()
_ => (),
}
let id = *name_to_pyid.get(&name).unwrap();
@ -852,8 +863,8 @@ impl Nac3 {
}
}
/// Retrieves the Name.id from a decorator, supports decorators with arguments.

Please add documentation for these two functions.

Please add documentation for these two functions.
fn decorator_id_string(decorator: &Located<ExprKind>) -> Option<String> {
/// Retrieves the Name.id from a decorator, supports decorators with arguments.
if let ExprKind::Name { id, .. } = decorator.node {
// Bare decorator
return Some(id.to_string());
@ -867,11 +878,11 @@ fn decorator_id_string(decorator: &Located<ExprKind>) -> Option<String> {
None
}
/// Retrieves flags from a decorator, if any.
fn decorator_get_flags(decorator: &Located<ExprKind>) -> Vec<Constant> {
/// Retrieves flags from a decorator, if any.
let mut flags = vec![];
if let ExprKind::Call { keywords, .. } = &decorator.node {
for keyword in keywords.iter() {
for keyword in keywords {
if keyword.node.arg != Some("flags".into()) {
continue;
}

View File

@ -1673,7 +1673,7 @@ impl TopLevelComposer {
// they may be changed with our use of placeholders
for (def, _) in definition_ast_list.iter().skip(self.builtin_num) {
if let TopLevelDef::Function { signature, var_id, .. } = &mut *def.write() {
if let TypeEnum::TFunc(FunSignature { args, ret, vars}) =
if let TypeEnum::TFunc(FunSignature { args, ret, vars }) =
unifier.get_ty(*signature).as_ref()
{
let new_var_ids = vars
@ -1894,7 +1894,8 @@ impl TopLevelComposer {
} = &mut *function_def
{
let signature_ty_enum = unifier.get_ty(*signature);
let TypeEnum::TFunc(FunSignature { args, ret, vars, .. }) = signature_ty_enum.as_ref()
let TypeEnum::TFunc(FunSignature { args, ret, vars, .. }) =
signature_ty_enum.as_ref()
else {
unreachable!("must be typeenum::tfunc")
};
@ -2060,7 +2061,8 @@ impl TopLevelComposer {
if !decorator_list.is_empty() {
if let ast::ExprKind::Call { func, .. } = &decorator_list[0].node {
if matches!(&func.node,
ast::ExprKind::Name{ id, .. } if id == &"rpc".into()) {
ast::ExprKind::Name{ id, .. } if id == &"rpc".into())
{
instance_to_symbol.insert(String::new(), simple_name.to_string());

What exactly is this for?

What exactly is this for?

Mimics the check above it - for decorators with arguments the Name is behind a Call as I mentioned before. Without it, it wouldn't be considered an external function and compiler would try to compile it together with the kernel.

Mimics the check above it - for decorators with arguments the Name is behind a Call as I mentioned before. Without it, it wouldn't be considered an external function and compiler would try to compile it together with the kernel.
continue;
}