├── examples ├── chain │ ├── run.erl │ ├── Elixir.Chain.erl │ ├── Elixir.Range.erl │ └── timer.erl ├── fib │ └── fib.erl └── simple_server │ ├── foo.erl │ ├── Elixir.ExampleServer.erl │ ├── gen.erl │ ├── Elixir.Keyword.erl │ ├── proc_lib.erl │ └── Elixir.GenServer.erl ├── maps.erl ├── src ├── native │ ├── mod.rs │ ├── lists.rs │ ├── logger.rs │ ├── lumen_intrinsics.rs │ ├── maps.rs │ └── erlang.rs ├── lib.rs ├── code │ ├── return_clean.rs │ ├── return_ok.rs │ ├── interpreter_mfa.rs │ ├── return_throw.rs │ └── interpreter_closure.rs ├── code.rs ├── bin.rs ├── exec │ ├── match.rs │ └── mod.rs ├── vm.rs ├── module.rs ├── tests │ └── mod.rs └── call_result.rs ├── Cargo.toml └── README.md /examples/chain/run.erl: -------------------------------------------------------------------------------- 1 | -module(run). 2 | 3 | run() -> 'Elixir.Chain':run(10). 4 | -------------------------------------------------------------------------------- /maps.erl: -------------------------------------------------------------------------------- 1 | -module(maps). 2 | 3 | -export([run/0]). 4 | 5 | run() -> maps:get(a, #{a => 1}) + maps:get(b, #{a => 1}, 5). 6 | -------------------------------------------------------------------------------- /examples/fib/fib.erl: -------------------------------------------------------------------------------- 1 | -module(fib). 2 | 3 | -export([run/0]). 4 | 5 | run() -> 6 | N = fib(8), 7 | lumen_intrinsics:println(N), 8 | N. 9 | 10 | fib(0) -> 0; 11 | fib(1) -> 1; 12 | fib(X) -> fib(X-1) + fib(X-2). 13 | -------------------------------------------------------------------------------- /src/native/mod.rs: -------------------------------------------------------------------------------- 1 | mod erlang; 2 | pub use erlang::make_erlang; 3 | 4 | mod lists; 5 | pub use lists::make_lists; 6 | 7 | mod maps; 8 | pub use maps::make_maps; 9 | 10 | mod logger; 11 | pub use logger::make_logger; 12 | 13 | mod lumen_intrinsics; 14 | pub use lumen_intrinsics::make_lumen_intrinsics; 15 | -------------------------------------------------------------------------------- /examples/simple_server/foo.erl: -------------------------------------------------------------------------------- 1 | -module(foo). 2 | 3 | -export([run/0]). 4 | 5 | run() -> 6 | {ok, Pid} = 'Elixir.ExampleServer':start_link([]), 7 | ok = 'Elixir.GenServer':cast(Pid, {push, yay}), 8 | ok = 'Elixir.GenServer':cast(Pid, {push, yay2}), 9 | yay2 = 'Elixir.GenServer':call(Pid, pop, infinity), 10 | yay = 'Elixir.GenServer':call(Pid, pop, infinity). 11 | 12 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(warnings)] 2 | 3 | pub mod code; 4 | mod exec; 5 | mod module; 6 | pub use module::NativeModule; 7 | pub mod call_result; 8 | mod native; 9 | pub use lumen_rt_core as runtime; 10 | mod vm; 11 | 12 | #[cfg(test)] 13 | mod tests; 14 | 15 | use self::vm::VMState; 16 | use lazy_static::lazy_static; 17 | 18 | lazy_static! { 19 | pub static ref VM: VMState = VMState::new(); 20 | } 21 | -------------------------------------------------------------------------------- /src/native/lists.rs: -------------------------------------------------------------------------------- 1 | use liblumen_alloc::erts::term::prelude::Atom; 2 | 3 | use liblumen_otp::lists; 4 | 5 | use crate::module::NativeModule; 6 | 7 | pub fn make_lists() -> NativeModule { 8 | let mut native = NativeModule::new(Atom::try_from_str("lists").unwrap()); 9 | 10 | native.add_simple(Atom::try_from_str("keyfind").unwrap(), 3, |_proc, args| { 11 | lists::keyfind_3::result(args[0], args[1], args[2]) 12 | }); 13 | 14 | native.add_simple(Atom::try_from_str("member").unwrap(), 2, |_proc, args| { 15 | lists::member_2::result(args[0], args[1]) 16 | }); 17 | 18 | native 19 | } 20 | -------------------------------------------------------------------------------- /src/code/return_clean.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_void; 2 | 3 | use liblumen_alloc::erts::exception; 4 | use liblumen_alloc::erts::process::Process; 5 | use liblumen_alloc::erts::term::prelude::*; 6 | 7 | pub fn closure(process: &Process) -> exception::Result { 8 | let function = Atom::try_from_str("return_clean").unwrap(); 9 | const ARITY: u8 = 1; 10 | 11 | process 12 | .export_closure( 13 | super::module(), 14 | function, 15 | ARITY, 16 | Some(native as *const c_void), 17 | ) 18 | .map_err(|error| error.into()) 19 | } 20 | 21 | #[native_implemented::function(lumen_eir_interpreter_intrinsics:return_clean/1)] 22 | pub fn result(argument_list: Term) -> Term { 23 | argument_list 24 | } 25 | -------------------------------------------------------------------------------- /src/native/logger.rs: -------------------------------------------------------------------------------- 1 | use liblumen_alloc::erts::term::prelude::Atom; 2 | 3 | use crate::module::NativeModule; 4 | 5 | macro_rules! trace { 6 | ($($t:tt)*) => (crate::runtime::sys::io::puts(&format_args!($($t)*).to_string())) 7 | } 8 | 9 | pub fn make_logger() -> NativeModule { 10 | let mut native = NativeModule::new(Atom::try_from_str("logger").unwrap()); 11 | 12 | native.add_simple(Atom::try_from_str("allow").unwrap(), 2, |_proc, _args| { 13 | Ok(true.into()) 14 | }); 15 | 16 | native.add_simple( 17 | Atom::try_from_str("macro_log").unwrap(), 18 | 4, 19 | |_proc, args| { 20 | trace!("{} {} {} {}", args[0], args[1], args[2], args[3]); 21 | Ok(true.into()) 22 | }, 23 | ); 24 | 25 | native 26 | } 27 | -------------------------------------------------------------------------------- /src/native/lumen_intrinsics.rs: -------------------------------------------------------------------------------- 1 | use liblumen_alloc::erts::term::prelude::*; 2 | 3 | use crate::module::NativeModule; 4 | 5 | pub fn make_lumen_intrinsics() -> NativeModule { 6 | let mut native = NativeModule::new(Atom::try_from_str("lumen_intrinsics").unwrap()); 7 | 8 | native.add_simple(Atom::try_from_str("println").unwrap(), 1, |_proc, args| { 9 | crate::runtime::sys::io::puts(&format!("{}", args[0])); 10 | Ok(Atom::str_to_term("ok")) 11 | }); 12 | 13 | native.add_simple(Atom::try_from_str("format").unwrap(), 1, |proc, args| { 14 | let string = format!("{}", args[0]); 15 | let term = proc.binary_from_str(&string).unwrap(); 16 | Ok(term) 17 | }); 18 | 19 | native.add_simple( 20 | Atom::try_from_str("dump_process_heap").unwrap(), 21 | 0, 22 | |proc, _args| { 23 | crate::runtime::sys::io::puts(&format!("{:?}", proc.acquire_heap())); 24 | Ok(Atom::str_to_term("ok")) 25 | }, 26 | ); 27 | 28 | native 29 | } 30 | -------------------------------------------------------------------------------- /src/code/return_ok.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::c_void; 2 | 3 | use liblumen_alloc::erts::exception; 4 | use liblumen_alloc::erts::process::Process; 5 | use liblumen_alloc::erts::term::prelude::*; 6 | 7 | pub fn closure(process: &Process) -> exception::Result { 8 | let function = Atom::try_from_str("return_ok").unwrap(); 9 | const ARITY: u8 = 1; 10 | 11 | process 12 | .export_closure( 13 | super::module(), 14 | function, 15 | ARITY, 16 | Some(native as *const c_void), 17 | ) 18 | .map_err(|error| error.into()) 19 | } 20 | 21 | #[native_implemented::function(lumen_eir_interpreter_intrinsics:return_ok/1)] 22 | pub fn result(argument_list: Term) -> Term { 23 | let mut argument_vec: Vec = Vec::new(); 24 | match argument_list.decode().unwrap() { 25 | TypedTerm::Nil => (), 26 | TypedTerm::List(argument_cons) => { 27 | for result in argument_cons.into_iter() { 28 | let element = result.unwrap(); 29 | 30 | argument_vec.push(element); 31 | } 32 | } 33 | _ => panic!(), 34 | } 35 | assert_eq!(argument_vec.len(), 1); 36 | 37 | argument_vec[0] 38 | } 39 | -------------------------------------------------------------------------------- /src/code/interpreter_mfa.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use liblumen_alloc::erts::process::Process; 4 | use liblumen_alloc::erts::term::prelude::*; 5 | 6 | use crate::exec::CallExecutor; 7 | 8 | /// Expects the following on stack: 9 | /// * arity integer 10 | /// * argument list 11 | #[native_implemented::function(lumen_eir_interpreter_intrinsics:interpreter_mfa/1)] 12 | pub fn result(arc_process: Arc, argument_list: Term) -> Term { 13 | let mfa = arc_process.current_module_function_arity().unwrap(); 14 | 15 | let mut argument_vec: Vec = Vec::new(); 16 | match argument_list.decode().unwrap() { 17 | TypedTerm::Nil => (), 18 | TypedTerm::List(argument_cons) => { 19 | for result in argument_cons.into_iter() { 20 | let element = result.unwrap(); 21 | 22 | argument_vec.push(element); 23 | } 24 | } 25 | _ => panic!(), 26 | } 27 | assert!(mfa.arity as usize == argument_vec.len() - 2); 28 | 29 | let mut exec = CallExecutor::new(); 30 | exec.call( 31 | &crate::VM, 32 | &arc_process, 33 | mfa.module, 34 | mfa.function, 35 | argument_vec.len() - 2, 36 | &mut argument_vec, 37 | ); 38 | 39 | Term::NONE 40 | } 41 | -------------------------------------------------------------------------------- /src/code.rs: -------------------------------------------------------------------------------- 1 | pub mod interpreter_closure; 2 | pub mod interpreter_mfa; 3 | pub mod return_clean; 4 | pub mod return_ok; 5 | pub mod return_throw; 6 | 7 | use std::convert::TryInto; 8 | 9 | use liblumen_alloc::erts::process::Frame; 10 | use liblumen_alloc::erts::term::prelude::*; 11 | use liblumen_alloc::erts::ModuleFunctionArity; 12 | 13 | use crate::runtime::process::current_process; 14 | 15 | fn module() -> Atom { 16 | Atom::try_from_str("lumen_eir_interpreter_intrinsics").unwrap() 17 | } 18 | 19 | fn module_id() -> usize { 20 | module().id() 21 | } 22 | 23 | pub extern "C" fn apply(module_term: Term, function_term: Term, argument_list: Term) -> Term { 24 | let module: Atom = module_term.try_into().unwrap(); 25 | let function: Atom = function_term.try_into().unwrap(); 26 | 27 | let arity; 28 | match argument_list.decode().unwrap() { 29 | TypedTerm::Nil => panic!(), 30 | TypedTerm::List(argument_cons) => arity = argument_cons.into_iter().count() - 2, 31 | _ => panic!(), 32 | } 33 | 34 | let module_function_arity = ModuleFunctionArity { 35 | module, 36 | function, 37 | arity: arity.try_into().unwrap(), 38 | }; 39 | 40 | let frame = Frame::new(module_function_arity, interpreter_mfa::NATIVE); 41 | let frame_with_arguments = frame.with_arguments(false, &[argument_list]); 42 | 43 | current_process().queue_frame_with_arguments(frame_with_arguments); 44 | 45 | Term::NONE 46 | } 47 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lumen_interpreter" 3 | version = "0.1.0" 4 | authors = ["Hans Elias B. Josephsen ", "Luke Imhoff "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [[bin]] 10 | name = "run_file" 11 | path = "src/bin.rs" 12 | 13 | [dependencies] 14 | anyhow = "1.0.11" 15 | clap = "2.33.0" 16 | cranelift-entity = "0.56.0" 17 | lazy_static = "1.3.0" 18 | 19 | # eirproject/eir crates 20 | libeir_diagnostics = { git = "https://github.com/eirproject/eir.git", branch = "lumen" } 21 | libeir_intern = { git = "https://github.com/eirproject/eir.git", branch = "lumen" } 22 | libeir_ir = { git = "https://github.com/eirproject/eir.git", branch = "lumen" } 23 | libeir_passes = { git = "https://github.com/eirproject/eir.git", branch = "lumen" } 24 | libeir_syntax_erl = { git = "https://github.com/eirproject/eir.git", branch = "lumen" } 25 | libeir_util_parse = { git = "https://github.com/eirproject/eir.git", branch = "lumen" } 26 | 27 | # workspace crates 28 | liblumen_alloc = { path = "../liblumen_alloc" } 29 | liblumen_core = { path = "../liblumen_core" } 30 | native_implemented = { path = "../native_implemented/macro" } 31 | liblumen_otp = { path = "../native_implemented/otp" } 32 | 33 | [dependencies.hashbrown] 34 | version = "0.7" 35 | features = ["nightly"] 36 | 37 | [dependencies.lumen_rt_core] 38 | path = "../runtimes/core" 39 | features = ["time_web_sys"] 40 | -------------------------------------------------------------------------------- /src/code/return_throw.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryInto; 2 | use std::ffi::c_void; 3 | 4 | use anyhow::*; 5 | 6 | use liblumen_alloc::erts::exception; 7 | use liblumen_alloc::erts::process::Process; 8 | use liblumen_alloc::erts::term::prelude::*; 9 | 10 | pub fn closure(process: &Process) -> exception::Result { 11 | let function = Atom::try_from_str("return_throw").unwrap(); 12 | const ARITY: u8 = 3; 13 | 14 | process 15 | .export_closure( 16 | super::module(), 17 | function, 18 | ARITY, 19 | Some(native as *const c_void), 20 | ) 21 | .map_err(|error| error.into()) 22 | } 23 | 24 | #[native_implemented::function(lumen_eir_interpreter_intrinsics:return_throw/1)] 25 | pub fn result(argument_list: Term) -> exception::Result { 26 | let mut argument_vec: Vec = Vec::new(); 27 | match argument_list.decode().unwrap() { 28 | TypedTerm::Nil => (), 29 | TypedTerm::List(argument_cons) => { 30 | for result in argument_cons.into_iter() { 31 | let element = result.unwrap(); 32 | 33 | argument_vec.push(element); 34 | } 35 | } 36 | _ => panic!(), 37 | } 38 | 39 | let class: exception::Class = argument_vec[0].try_into().unwrap(); 40 | 41 | let reason = argument_vec[1]; 42 | let stacktrace = Some(argument_vec[2]); 43 | let exception = exception::raise( 44 | class, 45 | reason, 46 | stacktrace, 47 | anyhow!("explicit raise from Erlang").into(), 48 | ); 49 | 50 | Err(exception.into()) 51 | } 52 | -------------------------------------------------------------------------------- /examples/chain/Elixir.Chain.erl: -------------------------------------------------------------------------------- 1 | -file("lib/chain.ex", 1). 2 | 3 | -module('Elixir.Chain'). 4 | 5 | -compile([no_auto_import]). 6 | 7 | -export(['__info__'/1,counter/1,create_processes/1,run/1]). 8 | 9 | -spec '__info__'(attributes | 10 | compile | 11 | functions | 12 | macros | 13 | md5 | 14 | module | 15 | deprecated) -> 16 | any(). 17 | 18 | '__info__'(module) -> 19 | 'Elixir.Chain'; 20 | '__info__'(functions) -> 21 | [{counter,1},{create_processes,1},{run,1}]; 22 | '__info__'(macros) -> 23 | []; 24 | '__info__'(Key = attributes) -> 25 | erlang:get_module_info('Elixir.Chain', Key); 26 | '__info__'(Key = compile) -> 27 | erlang:get_module_info('Elixir.Chain', Key); 28 | '__info__'(Key = md5) -> 29 | erlang:get_module_info('Elixir.Chain', Key); 30 | '__info__'(deprecated) -> 31 | []. 32 | 33 | counter(_next_pid@1) -> 34 | receive 35 | _n@1 -> 36 | erlang:send(_next_pid@1, _n@1 + 1) 37 | end. 38 | 39 | create_processes(_n@1) -> 40 | _last@1 = 41 | 'Elixir.Enum':reduce('Elixir.Range':new(1, _n@1), 42 | self(), 43 | fun(_, _send_to@1) -> 44 | spawn('Elixir.Chain', 45 | counter, 46 | [_send_to@1]) 47 | end), 48 | erlang:send(_last@1, 0), 49 | receive 50 | _final_answer@1 when is_integer(_final_answer@1) -> 51 | <<"Result is ", 52 | ('Elixir.Kernel':inspect(_final_answer@1))/binary>> 53 | end. 54 | 55 | run(_n@1) -> 56 | 'Elixir.IO':puts('Elixir.Kernel':inspect(timer:tc('Elixir.Chain', 57 | create_processes, 58 | [_n@1]))). 59 | 60 | -------------------------------------------------------------------------------- /src/native/maps.rs: -------------------------------------------------------------------------------- 1 | use liblumen_alloc::erts::term::prelude::*; 2 | 3 | use liblumen_otp::maps; 4 | 5 | use crate::module::NativeModule; 6 | 7 | pub fn make_maps() -> NativeModule { 8 | let mut native = NativeModule::new(Atom::try_from_str("maps").unwrap()); 9 | 10 | native.add_simple(Atom::try_from_str("find").unwrap(), 2, |proc, args| { 11 | maps::find_2::result(proc, args[0], args[1]) 12 | }); 13 | 14 | native.add_simple(Atom::try_from_str("from_list").unwrap(), 1, |proc, args| { 15 | maps::from_list_1::result(proc, args[0]) 16 | }); 17 | 18 | native.add_simple(Atom::try_from_str("get").unwrap(), 2, |proc, args| { 19 | maps::get_3::result(proc, args[0], args[1], Atom::str_to_term("nil")) 20 | }); 21 | 22 | native.add_simple(Atom::try_from_str("get").unwrap(), 3, |proc, args| { 23 | maps::get_3::result(proc, args[0], args[1], args[2]) 24 | }); 25 | 26 | native.add_simple(Atom::try_from_str("is_key").unwrap(), 2, |proc, args| { 27 | maps::is_key_2::result(proc, args[0], args[1]) 28 | }); 29 | 30 | native.add_simple(Atom::try_from_str("keys").unwrap(), 1, |proc, args| { 31 | maps::keys_1::result(proc, args[0]) 32 | }); 33 | 34 | native.add_simple(Atom::try_from_str("merge").unwrap(), 2, |proc, args| { 35 | maps::merge_2::result(proc, args[0], args[1]) 36 | }); 37 | 38 | native.add_simple(Atom::try_from_str("put").unwrap(), 3, |proc, args| { 39 | maps::put_3::result(proc, args[0], args[1], args[2]) 40 | }); 41 | 42 | native.add_simple(Atom::try_from_str("remove").unwrap(), 2, |proc, args| { 43 | maps::remove_2::result(proc, args[0], args[1]) 44 | }); 45 | 46 | native.add_simple(Atom::try_from_str("take").unwrap(), 2, |proc, args| { 47 | maps::take_2::result(proc, args[0], args[1]) 48 | }); 49 | 50 | native.add_simple(Atom::try_from_str("update").unwrap(), 3, |proc, args| { 51 | maps::update_3::result(proc, args[0], args[1], args[2]) 52 | }); 53 | 54 | native.add_simple(Atom::try_from_str("values").unwrap(), 1, |proc, args| { 55 | maps::values_1::result(proc, args[0]) 56 | }); 57 | 58 | native 59 | } 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lumen Interpreter 2 | 3 | EIR interpreter for the Lumen runtime. 4 | 5 | This crate is primarily a library crate, but can also be run as a binary. 6 | 7 | ## Running code 8 | 9 | ### Quickstart 10 | 11 | 1. Make sure rust is installed 12 | 2. `cargo run -- --ident fib:run/0 examples/fib/fib.erl` 13 | 3. An execution trace is printed, ending with the return value of the function. 14 | 15 | `cargo run --` runs the binary in this crate. Everything after `--` is passed to the binary as command line arguments. 16 | 17 | The binary takes two things as arguments: 18 | 19 | * `--ident foo:bar/0`: The initial function that should be called. Must be of arity 0, there is no way to specify function arguments (yet). 20 | * `ERL_FILES`: Any number of erlang files that should be compiled and added to the interpreter environment. 21 | 22 | ### Elixir code 23 | 24 | In order to run Elixir code, it needs to be transformed to Erlang. This is not very ergonomic at the moment. This is all very temporary and will be improved greatly very soon. 25 | 26 | 1. Install [this](https://github.com/michalmuskala/decompile) hex archive: `mix archive.install github michalmuskala/decompile` 27 | 2. Create a new Elixir project with the modules you want to run. 28 | 3. Run `mix decompile --to erl ` 29 | 4. Repeat for all of the modules involved in your program: move all the generated `.erl` files into a new directory. If you are unsure if you got all files, no worries, you can jump back here later. 30 | 5. Because of a bug in the decompiler, the decompiled code is wrong for Elixir modules. Open the Elixir modules and remove the `-compile([no_auto_imports])` (or similar) line, it should be near the top. 31 | 6. `cargo run -- --ident my:entry/0 my_erl_dir/*` 32 | 33 | This should print an execution trace. 34 | * If the interpreter crashes with module not found, you most likely need to decompile and add this module. 35 | * If the interpreter crashes with a compilation error, open an issue [here](https://github.com/eirproject/eir). 36 | * If the interpreter crashes while running, open an issue on this repository. 37 | * If you are unsure exactly what went wrong, feel free to ping `hansihe` or open an issue on this repo. 38 | -------------------------------------------------------------------------------- /src/code/interpreter_closure.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryInto; 2 | use std::sync::Arc; 3 | 4 | use cranelift_entity::EntityRef; 5 | 6 | use libeir_ir::{Block, FunctionIndex}; 7 | 8 | use liblumen_alloc::erts::process::Process; 9 | use liblumen_alloc::erts::term::closure::Definition; 10 | use liblumen_alloc::erts::term::prelude::*; 11 | 12 | use crate::exec::CallExecutor; 13 | 14 | /// Expects the following on stack: 15 | /// * arity integer 16 | /// * argument list 17 | /// * block id integer 18 | /// * environment list 19 | #[native_implemented::function(lumen_eir_interpreter_intrinsics:interpreter_closure/2)] 20 | pub fn result(arc_process: Arc, argument_list: Term, closure_term: Term) -> Term { 21 | let closure: Boxed = closure_term.try_into().unwrap(); 22 | println!("{:?}", closure); 23 | 24 | let mfa = arc_process.current_module_function_arity().unwrap(); 25 | let definition = arc_process.current_definition().unwrap(); 26 | 27 | let block_id; 28 | let function_index; 29 | match definition { 30 | Definition::Anonymous { 31 | index, old_unique, .. 32 | } => { 33 | block_id = index; 34 | function_index = old_unique; 35 | } 36 | _ => unreachable!(), 37 | } 38 | 39 | //let block_id: usize = closure.env_slice()[0].try_into().unwrap(); 40 | let block = Block::new(block_id as usize); 41 | 42 | let mut argument_vec: Vec = Vec::new(); 43 | match argument_list.decode().unwrap() { 44 | TypedTerm::Nil => (), 45 | TypedTerm::List(argument_cons) => { 46 | for result in argument_cons.into_iter() { 47 | let element = result.unwrap(); 48 | 49 | argument_vec.push(element); 50 | } 51 | } 52 | _ => panic!(), 53 | } 54 | 55 | let mut environment_vec: Vec = closure.env_slice().to_owned(); 56 | 57 | let mut exec = CallExecutor::new(); 58 | exec.call_block( 59 | &crate::VM, 60 | &arc_process, 61 | mfa.module, 62 | FunctionIndex::new(function_index as usize), 63 | //mfa.function, 64 | //arity as usize, 65 | &mut argument_vec, 66 | block, 67 | &mut environment_vec, 68 | ); 69 | 70 | Term::NONE 71 | } 72 | -------------------------------------------------------------------------------- /src/bin.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | use std::sync::Arc; 3 | 4 | use clap::{App, Arg}; 5 | 6 | use libeir_diagnostics::*; 7 | 8 | use libeir_ir::{FunctionIdent, Module}; 9 | 10 | use libeir_passes::PassManager; 11 | 12 | use libeir_syntax_erl::ast::Module as ErlAstModule; 13 | use libeir_syntax_erl::lower_module; 14 | use libeir_syntax_erl::{Parse, ParseConfig, Parser}; 15 | 16 | use libeir_util_parse::Errors; 17 | 18 | use lumen_interpreter::call_result::call_run_erlang; 19 | use lumen_interpreter::runtime::scheduler; 20 | use lumen_interpreter::VM; 21 | 22 | use liblumen_alloc::erts::term::prelude::Atom; 23 | 24 | fn parse_file(path: P, config: ParseConfig) -> (T, Arc) 25 | where 26 | T: Parse, 27 | P: AsRef, 28 | { 29 | let codemap: Arc = Default::default(); 30 | let parser = Parser::new(config, codemap.clone()); 31 | let mut errors = Errors::new(); 32 | match parser.parse_file(&mut errors, path) { 33 | Ok(ast) => return (ast, codemap), 34 | Err(errs) => errs, 35 | }; 36 | errors.print(&codemap); 37 | panic!("parse failed"); 38 | } 39 | 40 | fn lower_file

(path: P, config: ParseConfig) -> Result 41 | where 42 | P: AsRef, 43 | { 44 | let (parsed, codemap): (ErlAstModule, _) = parse_file(path, config); 45 | let mut errors = Errors::new(); 46 | let res = lower_module(&mut errors, codemap.clone(), &parsed); 47 | errors.print(&codemap); 48 | 49 | res 50 | } 51 | 52 | fn main() { 53 | let matches = App::new("Lumen Interpreter") 54 | .version("alpha") 55 | .arg( 56 | Arg::from_usage(" 'load files into the interpreter'") 57 | .multiple(true) 58 | .required(false), 59 | ) 60 | .arg( 61 | Arg::from_usage(" -i,--ident 'select single function'") 62 | .required(true), 63 | ) 64 | .get_matches(); 65 | 66 | let ident = FunctionIdent::parse(matches.value_of("FUN_IDENT").unwrap()).unwrap(); 67 | 68 | &*VM; 69 | 70 | let arc_scheduler = scheduler::current(); 71 | let init_arc_process = arc_scheduler.spawn_init(0).unwrap(); 72 | 73 | let module = Atom::try_from_str(&ident.module.as_str()).unwrap(); 74 | let function = Atom::try_from_str(&ident.name.as_str()).unwrap(); 75 | assert!(ident.arity == 0); 76 | 77 | for file in matches.values_of("LOAD_ERL_FILES").unwrap() { 78 | let config = ParseConfig::default(); 79 | let mut eir_mod = lower_file(file, config).unwrap(); 80 | 81 | for fun_def in eir_mod.function_iter() { 82 | let fun = fun_def.function(); 83 | fun.graph_validate_global(); 84 | } 85 | 86 | let mut pass_manager = PassManager::default(); 87 | pass_manager.run(&mut eir_mod); 88 | 89 | VM.modules.write().unwrap().register_erlang_module(eir_mod); 90 | } 91 | 92 | let res = call_run_erlang(init_arc_process, module, function, &[]); 93 | println!("Returned with {:?}", res.result); 94 | } 95 | -------------------------------------------------------------------------------- /src/exec/match.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use libeir_ir::{BasicType, Block, MatchKind}; 4 | 5 | use liblumen_alloc::erts::exception::SystemException; 6 | use liblumen_alloc::erts::process::Process; 7 | use liblumen_alloc::erts::term::prelude::{Encoded, ExactEq, TypedTerm}; 8 | 9 | use super::{CallExecutor, OpResult}; 10 | use crate::module::ErlangFunction; 11 | 12 | pub fn match_op( 13 | exec: &mut CallExecutor, 14 | proc: &Arc, 15 | fun: &ErlangFunction, 16 | branches: &[MatchKind], 17 | block: Block, 18 | ) -> std::result::Result { 19 | let reads = fun.fun.block_reads(block); 20 | 21 | let branches_dests = reads[0]; 22 | 23 | let unpack_term = exec 24 | .make_term(proc, fun, reads[1]) 25 | .unwrap() 26 | .decode() 27 | .unwrap(); 28 | 29 | for (idx, kind) in branches.iter().enumerate() { 30 | let branch = fun.fun.value_list_get_n(branches_dests, idx).unwrap(); 31 | 32 | let branch_args_val = reads[idx + 2]; 33 | let branch_args_len = fun.fun.value_list_length(branch_args_val); 34 | 35 | match kind { 36 | MatchKind::Value => { 37 | assert!(branch_args_len == 1); 38 | let arg = fun.fun.value_list_get_n(branch_args_val, 0).unwrap(); 39 | let rhs = exec.make_term(proc, fun, arg).unwrap(); 40 | 41 | if unpack_term.exact_eq(&rhs.decode().unwrap()) { 42 | return exec.val_call(proc, fun, branch); 43 | } 44 | } 45 | MatchKind::Type(BasicType::Map) => { 46 | assert!(branch_args_len == 0); 47 | if unpack_term.is_map() { 48 | return exec.val_call(proc, fun, branch); 49 | } 50 | } 51 | MatchKind::MapItem => { 52 | assert!(branch_args_len == 1); 53 | let arg = fun.fun.value_list_get_n(branch_args_val, 0).unwrap(); 54 | let key = exec.make_term(proc, fun, arg).unwrap(); 55 | 56 | match unpack_term { 57 | TypedTerm::Map(map) => { 58 | if let Some(val) = map.get(key) { 59 | exec.next_args.push(val); 60 | return exec.val_call(proc, fun, branch); 61 | } 62 | } 63 | _ => unreachable!(), 64 | } 65 | } 66 | MatchKind::Tuple(arity) => { 67 | assert!(branch_args_len == 0); 68 | 69 | match unpack_term { 70 | TypedTerm::Tuple(tup) => { 71 | if tup.len() == *arity { 72 | exec.next_args.extend(tup.iter()); 73 | return exec.val_call(proc, fun, branch); 74 | } 75 | } 76 | _ => (), 77 | } 78 | } 79 | MatchKind::ListCell => { 80 | assert!(branch_args_len == 0); 81 | 82 | match unpack_term { 83 | TypedTerm::List(cons) => { 84 | exec.next_args.push(cons.head); 85 | exec.next_args.push(cons.tail); 86 | return exec.val_call(proc, fun, branch); 87 | } 88 | _ => (), 89 | } 90 | } 91 | MatchKind::Wildcard => { 92 | assert!(branch_args_len == 0); 93 | return exec.val_call(proc, fun, branch); 94 | } 95 | kind => unimplemented!("{:?}", kind), 96 | } 97 | } 98 | 99 | panic!() 100 | } 101 | -------------------------------------------------------------------------------- /examples/chain/Elixir.Range.erl: -------------------------------------------------------------------------------- 1 | -file("/home/build/elixir/lib/elixir/lib/range.ex", 1). 2 | 3 | -module('Elixir.Range'). 4 | 5 | %-compile([no_auto_import,{inline,[{normalize,2}]}]). 6 | 7 | -spec new(integer(), integer()) -> t(). 8 | 9 | -spec 'disjoint?'(t(), t()) -> boolean(). 10 | 11 | -export_type([t/2]). 12 | 13 | %-type t(first, last) :: 14 | % #{'__struct__' := 'Elixir.Range', 15 | % first := first, 16 | % last := last}. 17 | 18 | -export_type([t/0]). 19 | 20 | -type t() :: 21 | #{'__struct__' := 'Elixir.Range', 22 | first := integer(), 23 | last := integer()}. 24 | 25 | -export(['__info__'/1, 26 | '__struct__'/0, 27 | '__struct__'/1, 28 | 'disjoint?'/2, 29 | new/2, 30 | 'range?'/1]). 31 | 32 | -spec '__info__'(attributes | 33 | compile | 34 | functions | 35 | macros | 36 | md5 | 37 | module | 38 | deprecated) -> 39 | any(). 40 | 41 | '__info__'(module) -> 42 | 'Elixir.Range'; 43 | '__info__'(functions) -> 44 | [{'__struct__',0}, 45 | {'__struct__',1}, 46 | {'disjoint?',2}, 47 | {new,2}, 48 | {'range?',1}]; 49 | '__info__'(macros) -> 50 | []; 51 | '__info__'(Key = attributes) -> 52 | erlang:get_module_info('Elixir.Range', Key); 53 | '__info__'(Key = compile) -> 54 | erlang:get_module_info('Elixir.Range', Key); 55 | '__info__'(Key = md5) -> 56 | erlang:get_module_info('Elixir.Range', Key); 57 | '__info__'(deprecated) -> 58 | [{{'range?',1},<<"Pattern match on first..last instead">>}]. 59 | 60 | '__struct__'() -> 61 | #{'__struct__' => 'Elixir.Range',first => nil,last => nil}. 62 | 63 | '__struct__'(__@1) -> 64 | 'Elixir.Enum':reduce(__@1, 65 | #{'__struct__' => 'Elixir.Range', 66 | first => nil, 67 | last => nil}, 68 | fun({__@2,__@3}, __@4) -> 69 | maps:update(__@2, __@3, __@4) 70 | end). 71 | 72 | 'disjoint?'(#{'__struct__' := 'Elixir.Range', 73 | first := _first1@1, 74 | last := _last1@1}, 75 | #{'__struct__' := 'Elixir.Range', 76 | first := _first2@1, 77 | last := _last2@1}) -> 78 | {_first1@2,_last1@2} = normalize(_first1@1, _last1@1), 79 | {_first2@2,_last2@2} = normalize(_first2@1, _last2@1), 80 | case _last2@2 < _first1@2 of 81 | false -> 82 | _last1@2 < _first2@2; 83 | true -> 84 | true 85 | end. 86 | 87 | new(_first@1, _last@1) 88 | when 89 | is_integer(_first@1) 90 | andalso 91 | is_integer(_last@1) -> 92 | #{first => _first@1,last => _last@1,'__struct__' => 'Elixir.Range'}; 93 | new(_first@1, _last@1) -> 94 | error('Elixir.ArgumentError':exception(<<"ranges (first..last) expe" 95 | "ct both sides to be integ" 96 | "ers, ", 97 | "got: ", 98 | ('Elixir.Kernel':inspect(_first@1))/binary, 99 | "..", 100 | ('Elixir.Kernel':inspect(_last@1))/binary>>)). 101 | 102 | normalize(_first@1, _last@1) when _first@1 > _last@1 -> 103 | {_last@1,_first@1}; 104 | normalize(_first@1, _last@1) -> 105 | {_first@1,_last@1}. 106 | 107 | 'range?'(#{'__struct__' := 'Elixir.Range', 108 | first := _first@1, 109 | last := _last@1}) 110 | when 111 | is_integer(_first@1) 112 | andalso 113 | is_integer(_last@1) -> 114 | true; 115 | 'range?'(_) -> 116 | false. 117 | 118 | -------------------------------------------------------------------------------- /examples/simple_server/Elixir.ExampleServer.erl: -------------------------------------------------------------------------------- 1 | -file("lib/example_server.ex", 1). 2 | 3 | -module('Elixir.ExampleServer'). 4 | 5 | -behaviour('Elixir.GenServer'). 6 | 7 | -export(['__info__'/1, 8 | child_spec/1, 9 | code_change/3, 10 | handle_call/3, 11 | handle_cast/2, 12 | handle_info/2, 13 | init/1, 14 | pop/1, 15 | push/2, 16 | start_link/1, 17 | terminate/2]). 18 | 19 | -spec '__info__'(attributes | 20 | compile | 21 | functions | 22 | macros | 23 | md5 | 24 | module | 25 | deprecated) -> 26 | any(). 27 | 28 | '__info__'(module) -> 29 | 'Elixir.ExampleServer'; 30 | '__info__'(functions) -> 31 | [{child_spec,1}, 32 | {code_change,3}, 33 | {handle_call,3}, 34 | {handle_cast,2}, 35 | {handle_info,2}, 36 | {init,1}, 37 | {pop,1}, 38 | {push,2}, 39 | {start_link,1}, 40 | {terminate,2}]; 41 | '__info__'(macros) -> 42 | []; 43 | '__info__'(Key = attributes) -> 44 | erlang:get_module_info('Elixir.ExampleServer', Key); 45 | '__info__'(Key = compile) -> 46 | erlang:get_module_info('Elixir.ExampleServer', Key); 47 | '__info__'(Key = md5) -> 48 | erlang:get_module_info('Elixir.ExampleServer', Key); 49 | '__info__'(deprecated) -> 50 | []. 51 | 52 | handle_call(pop, __from@1, [_head@1|_tail@1]) -> 53 | {reply,_head@1,_tail@1}. 54 | 55 | handle_cast({push,_element@1}, _state@1) -> 56 | {noreply,[_element@1 | _state@1]}. 57 | 58 | init(_stack@1) -> 59 | {ok,_stack@1}. 60 | 61 | pop(_pid@1) -> 62 | 'Elixir.GenServer':call(_pid@1, pop). 63 | 64 | push(_pid@1, _element@1) -> 65 | 'Elixir.GenServer':cast(_pid@1, {push,_element@1}). 66 | 67 | start_link(_default@1) when is_list(_default@1) -> 68 | 'Elixir.GenServer':start_link('Elixir.ExampleServer', _default@1). 69 | 70 | -file("lib/gen_server.ex", 729). 71 | 72 | child_spec(__@1) -> 73 | __@2 = 74 | #{id => 'Elixir.ExampleServer', 75 | start => {'Elixir.ExampleServer',start_link,[__@1]}}, 76 | 'Elixir.Supervisor':child_spec(__@2, []). 77 | 78 | -file("lib/gen_server.ex", 798). 79 | 80 | code_change(__@1, __@2, __@3) -> 81 | {ok,__@2}. 82 | 83 | -file("lib/gen_server.ex", 762). 84 | 85 | handle_info(__@1, __@2) -> 86 | __@4 = 87 | case 'Elixir.Process':info(self(), registered_name) of 88 | {_,[]} -> 89 | self(); 90 | {_,__@3} -> 91 | __@3 92 | end, 93 | __@5 = 94 | [126, 95 | 112, 96 | 32, 97 | 126, 98 | 112, 99 | 32, 100 | 114, 101 | 101, 102 | 99, 103 | 101, 104 | 105, 105 | 118, 106 | 101, 107 | 100, 108 | 32, 109 | 117, 110 | 110, 111 | 101, 112 | 120, 113 | 112, 114 | 101, 115 | 99, 116 | 116, 117 | 101, 118 | 100, 119 | 32, 120 | 109, 121 | 101, 122 | 115, 123 | 115, 124 | 97, 125 | 103, 126 | 101, 127 | 32, 128 | 105, 129 | 110, 130 | 32, 131 | 104, 132 | 97, 133 | 110, 134 | 100, 135 | 108, 136 | 101, 137 | 95, 138 | 105, 139 | 110, 140 | 102, 141 | 111, 142 | 47, 143 | 50, 144 | 58, 145 | 32, 146 | 126, 147 | 112, 148 | 126, 149 | 110], 150 | error_logger:error_msg(__@5, ['Elixir.ExampleServer',__@4,__@1]), 151 | {noreply,__@2}. 152 | 153 | -file("lib/gen_server.ex", 793). 154 | 155 | terminate(__@1, __@2) -> 156 | ok. 157 | 158 | -------------------------------------------------------------------------------- /src/vm.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | use std::sync::{Arc, RwLock}; 3 | 4 | use libeir_ir::FunctionIdent; 5 | 6 | use liblumen_alloc::atom; 7 | use liblumen_alloc::erts::exception::RuntimeException; 8 | use liblumen_alloc::erts::process::{Process, Status}; 9 | use liblumen_alloc::erts::term::prelude::*; 10 | 11 | use crate::runtime::process::spawn::options::Options; 12 | use crate::runtime::scheduler; 13 | use crate::runtime::sys; 14 | 15 | use super::module::ModuleRegistry; 16 | 17 | pub struct VMState { 18 | pub modules: RwLock, 19 | pub closure_hack: RwLock>>, 20 | pub init: Arc, 21 | } 22 | 23 | impl VMState { 24 | pub fn new() -> Self { 25 | liblumen_otp::erlang::apply_3::set_native(crate::code::apply); 26 | 27 | let mut modules = ModuleRegistry::new(); 28 | modules.register_native_module(crate::native::make_erlang()); 29 | modules.register_native_module(crate::native::make_lists()); 30 | modules.register_native_module(crate::native::make_maps()); 31 | modules.register_native_module(crate::native::make_logger()); 32 | modules.register_native_module(crate::native::make_lumen_intrinsics()); 33 | 34 | let arc_scheduler = scheduler::current(); 35 | let init_arc_process = arc_scheduler.spawn_init(0).unwrap(); 36 | 37 | VMState { 38 | modules: RwLock::new(modules), 39 | closure_hack: RwLock::new(Vec::new()), 40 | init: init_arc_process, 41 | } 42 | } 43 | 44 | pub fn call( 45 | &mut self, 46 | fun: &FunctionIdent, 47 | args: &[Term], 48 | ) -> Result, (Rc, Rc, Rc)> { 49 | let arc_scheduler = scheduler::current(); 50 | let init_arc_process = arc_scheduler.spawn_init(0).unwrap(); 51 | 52 | let module = Atom::try_from_str(&fun.module.as_str()).unwrap(); 53 | let function = Atom::try_from_str(&fun.name.as_str()).unwrap(); 54 | let arguments = init_arc_process 55 | .list_from_slice(args) 56 | // if not enough memory here, resize `spawn_init` heap 57 | .unwrap(); 58 | 59 | let mut options: Options = Default::default(); 60 | options.min_heap_size = Some(4 + 1000 * 2); 61 | 62 | let run_process_spawned = crate::runtime::process::spawn::apply_3( 63 | &init_arc_process, 64 | options, 65 | module, 66 | function, 67 | arguments) 68 | // if this fails a bigger sized heap 69 | .unwrap(); 70 | 71 | let run_arc_process_spawned = run_process_spawned.schedule_with_parent(&init_arc_process); 72 | let run_arc_process = run_arc_process_spawned.arc_process; 73 | 74 | loop { 75 | let ran = scheduler::run_through(&run_arc_process); 76 | 77 | match *run_arc_process.status.read() { 78 | Status::Unrunnable => unreachable!("{:?} was not made runnable", run_arc_process), 79 | Status::SystemException(ref system_exception) => { 80 | unimplemented!("{:?}", system_exception) 81 | } 82 | Status::RuntimeException(ref exception) => match exception { 83 | RuntimeException::Exit(err) => { 84 | let reason = err.reason(); 85 | if reason != atom!("normal") { 86 | panic!("Process exited: {:?}", reason); 87 | } else { 88 | panic!("yay!"); 89 | } 90 | } 91 | _ => { 92 | panic!( 93 | "Process exception: {:?}\n{:?}", 94 | exception, 95 | run_arc_process.stacktrace() 96 | ); 97 | } 98 | }, 99 | Status::Waiting => { 100 | if ran { 101 | sys::io::puts(&format!( 102 | "WAITING Run queues len = {:?}", 103 | scheduler::current().run_queues_len() 104 | )); 105 | } else { 106 | panic!( 107 | "{:?} did not run. Deadlock likely in {:#?}", 108 | run_arc_process, 109 | scheduler::current() 110 | ); 111 | } 112 | } 113 | Status::Runnable => { 114 | sys::io::puts(&format!( 115 | "RUNNABLE Run queues len = {:?}", 116 | scheduler::current().run_queues_len() 117 | )); 118 | } 119 | Status::Running => { 120 | sys::io::puts(&format!( 121 | "RUNNING Run queues len = {:?}", 122 | scheduler::current().run_queues_len() 123 | )); 124 | } 125 | } 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/module.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{BTreeMap, HashMap}; 2 | use std::sync::Arc; 3 | 4 | use libeir_ir::{Function, FunctionIndex, LiveValues, Module}; 5 | 6 | use liblumen_alloc::erts::exception; 7 | use liblumen_alloc::erts::exception::SystemException; 8 | use liblumen_alloc::erts::process::Process; 9 | use liblumen_alloc::erts::term::prelude::*; 10 | 11 | macro_rules! trace { 12 | ($($t:tt)*) => (crate::runtime::sys::io::puts(&format_args!($($t)*).to_string())) 13 | } 14 | //macro_rules! trace { 15 | // ($($t:tt)*) => () 16 | //} 17 | 18 | pub enum ResolvedFunction<'a> { 19 | Native(NativeFunctionKind), 20 | Erlang(&'a ErlangFunction), 21 | } 22 | 23 | pub struct ModuleRegistry { 24 | map: HashMap, 25 | } 26 | 27 | impl ModuleRegistry { 28 | pub fn new() -> Self { 29 | ModuleRegistry { 30 | map: HashMap::new(), 31 | } 32 | } 33 | 34 | pub fn register_erlang_module(&mut self, module: Module) { 35 | let erl_module = ErlangModule::from_eir(module); 36 | match self.map.remove(&erl_module.name) { 37 | None => self 38 | .map 39 | .insert(erl_module.name, ModuleType::Erlang(erl_module)), 40 | Some(ModuleType::Native(native)) => self 41 | .map 42 | .insert(erl_module.name, ModuleType::Overlayed(erl_module, native)), 43 | _ => panic!(), 44 | }; 45 | } 46 | 47 | pub fn register_native_module(&mut self, native: NativeModule) { 48 | match self.map.remove(&native.name) { 49 | None => self.map.insert(native.name, ModuleType::Native(native)), 50 | Some(ModuleType::Erlang(erl)) => self 51 | .map 52 | .insert(native.name, ModuleType::Overlayed(erl, native)), 53 | _ => panic!(), 54 | }; 55 | } 56 | 57 | pub fn lookup_function( 58 | &self, 59 | module: Atom, 60 | function: Atom, 61 | arity: usize, 62 | ) -> Option { 63 | trace!("LOOKUP {}:{}/{}", module, function, arity); 64 | match self.map.get(&module) { 65 | None => None, 66 | Some(ModuleType::Erlang(erl)) => erl 67 | .name_map 68 | .get(&(function, arity)) 69 | .map(|i| &erl.funs[i]) 70 | .map(ResolvedFunction::Erlang), 71 | Some(ModuleType::Native(nat)) => nat 72 | .functions 73 | .get(&(function, arity)) 74 | .cloned() 75 | .map(ResolvedFunction::Native), 76 | Some(ModuleType::Overlayed(erl, nat)) => { 77 | if let Some(nat_fun) = nat.functions.get(&(function, arity)) { 78 | Some(ResolvedFunction::Native(*nat_fun)) 79 | } else { 80 | erl.name_map 81 | .get(&(function, arity)) 82 | .map(|i| &erl.funs[i]) 83 | .map(ResolvedFunction::Erlang) 84 | } 85 | } 86 | } 87 | } 88 | 89 | pub fn lookup_function_idx( 90 | &self, 91 | module: Atom, 92 | index: FunctionIndex, 93 | ) -> Option<&ErlangFunction> { 94 | let ret = match self.map.get(&module) { 95 | None => None, 96 | Some(ModuleType::Erlang(erl)) => Some(&erl.funs[&index]), 97 | Some(ModuleType::Overlayed(erl, _)) => Some(&erl.funs[&index]), 98 | Some(ModuleType::Native(_)) => unreachable!(), 99 | }; 100 | 101 | if let Some(erl) = ret.as_ref() { 102 | trace!("LOOKUP IDX {}", erl.fun.ident()); 103 | } 104 | 105 | ret 106 | } 107 | } 108 | 109 | #[derive(Copy, Clone)] 110 | pub enum NativeFunctionKind { 111 | Simple(fn(&Arc, &[Term]) -> exception::Result), 112 | Yielding(fn(&Arc, &[Term]) -> Result<(), SystemException>), 113 | } 114 | 115 | pub struct NativeModule { 116 | pub name: Atom, 117 | pub functions: HashMap<(Atom, usize), NativeFunctionKind>, 118 | } 119 | impl NativeModule { 120 | pub fn new(name: Atom) -> Self { 121 | NativeModule { 122 | name, 123 | functions: HashMap::new(), 124 | } 125 | } 126 | 127 | pub fn add_simple( 128 | &mut self, 129 | name: Atom, 130 | arity: usize, 131 | fun: fn(&Arc, &[Term]) -> exception::Result, 132 | ) { 133 | self.functions 134 | .insert((name, arity), NativeFunctionKind::Simple(fun)); 135 | } 136 | 137 | pub fn add_yielding( 138 | &mut self, 139 | name: Atom, 140 | arity: usize, 141 | fun: fn(&Arc, &[Term]) -> Result<(), SystemException>, 142 | ) { 143 | self.functions 144 | .insert((name, arity), NativeFunctionKind::Yielding(fun)); 145 | } 146 | } 147 | 148 | pub struct ErlangFunction { 149 | pub fun: Function, 150 | pub index: FunctionIndex, 151 | pub live: LiveValues, 152 | } 153 | 154 | pub struct ErlangModule { 155 | pub name: Atom, 156 | pub funs: BTreeMap, 157 | pub name_map: BTreeMap<(Atom, usize), FunctionIndex>, 158 | } 159 | 160 | impl ErlangModule { 161 | pub fn from_eir(module: Module) -> Self { 162 | let name_atom = Atom::try_from_str(module.name().as_str()).unwrap(); 163 | 164 | let funs = module 165 | .function_iter() 166 | .map(|fun_def| { 167 | let fun = fun_def.function(); 168 | let nfun = ErlangFunction { 169 | live: fun.live_values(), 170 | index: fun_def.index(), 171 | fun: fun.clone(), 172 | }; 173 | (fun_def.index(), nfun) 174 | }) 175 | .collect(); 176 | 177 | let name_map = module 178 | .function_iter() 179 | .map(|fun_def| { 180 | let fun = fun_def.function(); 181 | let ident = fun.ident(); 182 | let name = Atom::try_from_str(ident.name.as_str()).unwrap(); 183 | ((name, ident.arity), fun_def.index()) 184 | }) 185 | .collect(); 186 | 187 | ErlangModule { 188 | name: name_atom, 189 | funs, 190 | name_map, 191 | } 192 | } 193 | } 194 | 195 | pub enum ModuleType { 196 | Erlang(ErlangModule), 197 | Overlayed(ErlangModule, NativeModule), 198 | Native(NativeModule), 199 | } 200 | -------------------------------------------------------------------------------- /src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | use super::VM; 2 | 3 | use std::sync::Arc; 4 | 5 | use libeir_diagnostics::CodeMap; 6 | 7 | use libeir_ir::Module; 8 | 9 | use libeir_passes::PassManager; 10 | 11 | use libeir_syntax_erl::ast::Module as ErlAstModule; 12 | use libeir_syntax_erl::lower_module; 13 | use libeir_syntax_erl::{Parse, ParseConfig, Parser}; 14 | 15 | use libeir_util_parse::Errors; 16 | 17 | use liblumen_alloc::erts::term::prelude::*; 18 | 19 | use crate::runtime::scheduler; 20 | 21 | fn parse(input: &str, config: ParseConfig) -> (T, Arc) 22 | where 23 | T: Parse, 24 | { 25 | let codemap: Arc = Default::default(); 26 | let parser = Parser::new(config, codemap.clone()); 27 | let mut errors = Errors::new(); 28 | match parser.parse_string::(&mut errors, input) { 29 | Ok(ast) => return (ast, codemap), 30 | Err(errs) => errs, 31 | }; 32 | errors.print(&codemap); 33 | panic!("parse failed"); 34 | } 35 | 36 | pub fn lower(input: &str, config: ParseConfig) -> Result { 37 | let (parsed, codemap): (ErlAstModule, _) = parse(input, config); 38 | 39 | let mut errors = Errors::new(); 40 | let res = lower_module(&mut errors, codemap.clone(), &parsed); 41 | errors.print(&codemap); 42 | 43 | res 44 | } 45 | 46 | pub fn compile(input: &str) -> Module { 47 | let config = ParseConfig::default(); 48 | let mut eir_mod = lower(input, config).unwrap(); 49 | 50 | for fun_def in eir_mod.function_iter() { 51 | let fun = fun_def.function(); 52 | fun.graph_validate_global(); 53 | } 54 | 55 | let mut pass_manager = PassManager::default(); 56 | pass_manager.run(&mut eir_mod); 57 | 58 | eir_mod 59 | } 60 | 61 | #[cfg(test)] 62 | fn run_once() { 63 | crate::runtime::test::once(&[liblumen_otp::erlang::apply_3::function_symbol()]); 64 | } 65 | 66 | #[test] 67 | fn simple_function() { 68 | run_once(); 69 | 70 | &*VM; 71 | 72 | let arc_scheduler = scheduler::current(); 73 | let init_arc_process = arc_scheduler.spawn_init(0).unwrap(); 74 | 75 | let module = Atom::try_from_str("simple_function_test").unwrap(); 76 | let function = Atom::try_from_str("run").unwrap(); 77 | 78 | let eir_mod = compile( 79 | " 80 | -module(simple_function_test). 81 | 82 | run() -> yay. 83 | ", 84 | ); 85 | 86 | VM.modules.write().unwrap().register_erlang_module(eir_mod); 87 | 88 | let res = crate::call_result::call_run_erlang(init_arc_process, module, function, &[]); 89 | assert!(res.result == Ok(Atom::str_to_term("yay"))); 90 | } 91 | 92 | #[test] 93 | fn fib() { 94 | run_once(); 95 | 96 | &*VM; 97 | 98 | let arc_scheduler = scheduler::current(); 99 | let init_arc_process = arc_scheduler.spawn_init(0).unwrap(); 100 | 101 | let module = Atom::try_from_str("fib").unwrap(); 102 | let function = Atom::try_from_str("fib").unwrap(); 103 | 104 | let eir_mod = compile( 105 | " 106 | -module(fib). 107 | 108 | fib(0) -> 0; 109 | fib(1) -> 1; 110 | fib(X) -> fib(X - 1) + fib(X - 2). 111 | ", 112 | ); 113 | 114 | VM.modules.write().unwrap().register_erlang_module(eir_mod); 115 | 116 | let int = init_arc_process.integer(5).unwrap(); 117 | let res = 118 | crate::call_result::call_run_erlang(init_arc_process.clone(), module, function, &[int]); 119 | 120 | let int = init_arc_process.integer(5).unwrap(); 121 | assert!(res.result == Ok(int)); 122 | } 123 | 124 | #[test] 125 | fn exception_test() { 126 | run_once(); 127 | 128 | &*VM; 129 | 130 | let arc_scheduler = scheduler::current(); 131 | let init_arc_process = arc_scheduler.spawn_init(0).unwrap(); 132 | 133 | let module = Atom::try_from_str("exception_test").unwrap(); 134 | let function = Atom::try_from_str("a").unwrap(); 135 | 136 | let eir_mod = compile( 137 | " 138 | -module(exception_test). 139 | 140 | a() -> 1 + a. 141 | ", 142 | ); 143 | 144 | VM.modules.write().unwrap().register_erlang_module(eir_mod); 145 | 146 | let res = crate::call_result::call_run_erlang(init_arc_process.clone(), module, function, &[]); 147 | 148 | assert!(res.result.is_err()); 149 | if let Err((typ, reason, _trace)) = res.result { 150 | assert!(typ == Atom::str_to_term("error")); 151 | assert!(reason == Atom::str_to_term("badarith")); 152 | } 153 | } 154 | 155 | #[test] 156 | fn fib_gc() { 157 | run_once(); 158 | 159 | &*VM; 160 | 161 | let arc_scheduler = scheduler::current(); 162 | let init_arc_process = arc_scheduler.spawn_init(0).unwrap(); 163 | 164 | let module = Atom::try_from_str("fib2").unwrap(); 165 | let function = Atom::try_from_str("fib").unwrap(); 166 | 167 | let eir_mod = compile( 168 | " 169 | -module(fib2). 170 | 171 | fib(0) -> 0; 172 | fib(1) -> 1; 173 | fib(X) -> fib(X - 1) + fib(X - 2). 174 | ", 175 | ); 176 | 177 | VM.modules.write().unwrap().register_erlang_module(eir_mod); 178 | 179 | let int = init_arc_process.integer(14).unwrap(); 180 | let res = 181 | crate::call_result::call_run_erlang(init_arc_process.clone(), module, function, &[int]); 182 | 183 | let int = init_arc_process.integer(377).unwrap(); 184 | assert!(res.result == Ok(int)); 185 | } 186 | 187 | #[test] 188 | fn ping_pong() { 189 | run_once(); 190 | 191 | &*VM; 192 | 193 | let arc_scheduler = scheduler::current(); 194 | let init_arc_process = arc_scheduler.spawn_init(0).unwrap(); 195 | 196 | let module = Atom::try_from_str("ping_pong").unwrap(); 197 | let function = Atom::try_from_str("run").unwrap(); 198 | 199 | let eir_mod = compile( 200 | " 201 | -module(ping_pong). 202 | 203 | proc_a(A) -> 204 | receive 205 | {b, R} -> R ! c 206 | end. 207 | 208 | proc_b(A, B) -> 209 | receive 210 | a -> 211 | B ! {b, self()}, 212 | proc_b(A, B); 213 | c -> 214 | A ! d 215 | end. 216 | 217 | run() -> 218 | P1 = spawn(ping_pong, proc_a, [self()]), 219 | P2 = spawn(ping_pong, proc_b, [self(), P1]), 220 | P2 ! a, 221 | receive 222 | Res -> Res 223 | end. 224 | ", 225 | ); 226 | 227 | VM.modules.write().unwrap().register_erlang_module(eir_mod); 228 | 229 | let res = crate::call_result::call_run_erlang(init_arc_process.clone(), module, function, &[]); 230 | 231 | assert!(res.result == Ok(Atom::str_to_term("d"))); 232 | } 233 | 234 | #[test] 235 | #[ignore] 236 | fn ping_pong_count() { 237 | run_once(); 238 | 239 | &*VM; 240 | 241 | let arc_scheduler = scheduler::current(); 242 | let init_arc_process = arc_scheduler.spawn_init(0).unwrap(); 243 | 244 | let module = Atom::try_from_str("ping_pong_count").unwrap(); 245 | let function = Atom::try_from_str("run").unwrap(); 246 | 247 | let eir_mod = compile( 248 | " 249 | -module(ping_pong_count). 250 | 251 | other_proc({add, A, B}, Ret) -> Ret ! {result, A + B}. 252 | 253 | this_proc(0, Acc) -> 254 | Acc; 255 | this_proc(N, Acc) -> 256 | spawn(ping_pong_count, other_proc, [{add, 1, Acc}, self()]), 257 | receive 258 | {result, Res} -> this_proc(N - 1, Res) 259 | end. 260 | 261 | run(N) -> this_proc(N, 0). 262 | ", 263 | ); 264 | 265 | VM.modules.write().unwrap().register_erlang_module(eir_mod); 266 | 267 | let int = init_arc_process.integer(10).unwrap(); 268 | let res = 269 | crate::call_result::call_run_erlang(init_arc_process.clone(), module, function, &[int]); 270 | 271 | println!("{:?}", res.result); 272 | //assert!(res.result == Ok(100)); 273 | } 274 | 275 | #[test] 276 | #[ignore] 277 | fn ping_pong_count_large() { 278 | run_once(); 279 | 280 | &*VM; 281 | 282 | let arc_scheduler = scheduler::current(); 283 | let init_arc_process = arc_scheduler.spawn_init(0).unwrap(); 284 | 285 | let module = Atom::try_from_str("ping_pong_count_large").unwrap(); 286 | let function = Atom::try_from_str("run").unwrap(); 287 | 288 | let eir_mod = compile( 289 | " 290 | -module(ping_pong_count_large). 291 | 292 | other_proc({add, A, B}, Ret) -> Ret ! {result, A + B}. 293 | 294 | this_proc(0, Acc) -> 295 | Acc; 296 | this_proc(N, Acc) -> 297 | spawn(ping_pong_count_large, other_proc, [{add, 1, Acc}, self()]), 298 | receive 299 | {result, Res} -> this_proc(N - 1, Res) 300 | end. 301 | 302 | run(N) -> this_proc(N, 0). 303 | ", 304 | ); 305 | 306 | VM.modules.write().unwrap().register_erlang_module(eir_mod); 307 | 308 | let int = init_arc_process.integer(100).unwrap(); 309 | let res = 310 | crate::call_result::call_run_erlang(init_arc_process.clone(), module, function, &[int]); 311 | 312 | println!("{:?}", res.result); 313 | //assert!(res.result == Ok(100)); 314 | } 315 | -------------------------------------------------------------------------------- /src/call_result.rs: -------------------------------------------------------------------------------- 1 | use core::ptr::NonNull; 2 | 3 | use std::convert::TryInto; 4 | use std::ffi::c_void; 5 | use std::sync::mpsc::{channel, Receiver, Sender}; 6 | use std::sync::Arc; 7 | 8 | use anyhow::*; 9 | 10 | use liblumen_alloc::borrow::clone_to_process::CloneToProcess; 11 | use liblumen_alloc::erts::exception; 12 | use liblumen_alloc::erts::process::{Process, Status}; 13 | use liblumen_alloc::erts::term::prelude::*; 14 | use liblumen_alloc::erts::HeapFragment; 15 | 16 | use crate::runtime::process::spawn::options::Options; 17 | use crate::runtime::scheduler; 18 | use crate::runtime::sys; 19 | 20 | /// A sort of ghetto-future used to get the result from a process 21 | /// spawn. 22 | pub struct ProcessResultReceiver { 23 | pub process: Arc, 24 | rx: Receiver, 25 | } 26 | 27 | impl ProcessResultReceiver { 28 | pub fn try_get(&self) -> Option { 29 | self.rx.try_recv().ok() 30 | } 31 | } 32 | 33 | pub struct ProcessResult { 34 | pub heap: NonNull, 35 | pub result: Result, 36 | } 37 | 38 | struct ProcessResultSender { 39 | tx: Sender, 40 | } 41 | 42 | pub fn call_run_erlang( 43 | proc: Arc, 44 | module: Atom, 45 | function: Atom, 46 | args: &[Term], 47 | ) -> ProcessResult { 48 | let recv = call_erlang(proc, module, function, args); 49 | let run_arc_process = recv.process.clone(); 50 | 51 | loop { 52 | let ran = scheduler::run_through(&run_arc_process); 53 | 54 | match *run_arc_process.status.read() { 55 | Status::Unrunnable => unreachable!( 56 | "Process ({:?}) not made runnable before running through", 57 | run_arc_process 58 | ), 59 | Status::RuntimeException(_) => { 60 | return recv.try_get().unwrap(); 61 | } 62 | Status::SystemException(ref system_exception) => { 63 | unimplemented!("SystemException {:?}", system_exception) 64 | } 65 | Status::Waiting => { 66 | if ran { 67 | sys::io::puts(&format!( 68 | "WAITING Run queues len = {:?}", 69 | scheduler::current().run_queues_len() 70 | )); 71 | } else { 72 | panic!( 73 | "{:?} did not run. Deadlock likely in {:#?}", 74 | run_arc_process, 75 | scheduler::current() 76 | ); 77 | } 78 | } 79 | Status::Runnable => { 80 | sys::io::puts(&format!( 81 | "RUNNABLE Run queues len = {:?}", 82 | scheduler::current().run_queues_len() 83 | )); 84 | } 85 | Status::Running => { 86 | sys::io::puts(&format!( 87 | "RUNNING Run queues len = {:?}", 88 | scheduler::current().run_queues_len() 89 | )); 90 | } 91 | } 92 | } 93 | } 94 | 95 | pub fn call_erlang( 96 | proc: Arc, 97 | module: Atom, 98 | function: Atom, 99 | args: &[Term], 100 | ) -> ProcessResultReceiver { 101 | let (tx, rx) = channel(); 102 | 103 | let sender = ProcessResultSender { tx }; 104 | let sender_term = proc.resource(sender).unwrap(); 105 | 106 | let return_ok = { 107 | let module = Atom::try_from_str("lumen_eir_interpreter_intrinsics").unwrap(); 108 | const ARITY: u8 = 1; 109 | 110 | proc.anonymous_closure_with_env_from_slice( 111 | module, 112 | // TODO assign `index` scoped to `module` 113 | 0, 114 | // TODO calculate `old_unique` for `return_ok` with `sender_term` captured. 115 | Default::default(), 116 | // TODO calculate `unique` for `return_ok` with `sender_term` captured. 117 | Default::default(), 118 | ARITY, 119 | Some(return_ok as *const c_void), 120 | proc.pid().into(), 121 | &[sender_term], 122 | ) 123 | .unwrap() 124 | }; 125 | 126 | let return_throw = { 127 | let module = Atom::try_from_str("lumen_eir_interpreter_intrinsics").unwrap(); 128 | const ARITY: u8 = 1; 129 | 130 | proc.anonymous_closure_with_env_from_slice( 131 | module, 132 | // TODO assign `index` scoped to `module` 133 | 1, 134 | // TODO calculate `unique` for `return_throw` with `sender_term` captured. 135 | Default::default(), 136 | // TODO calculate `unique` for `return_throw` with `sender_term` captured. 137 | Default::default(), 138 | ARITY, 139 | Some(return_throw as *const c_void), 140 | proc.pid().into(), 141 | &[sender_term], 142 | ) 143 | .unwrap() 144 | }; 145 | 146 | let mut args_vec = vec![return_ok, return_throw]; 147 | args_vec.extend(args.iter().cloned()); 148 | 149 | let arguments = proc.list_from_slice(&args_vec).unwrap(); 150 | 151 | let options: Options = Default::default(); 152 | //options.min_heap_size = Some(100_000); 153 | 154 | let run_process_spawned = 155 | crate::runtime::process::spawn::apply_3(&proc, options, module, function, arguments) 156 | .unwrap(); 157 | let run_arc_process_spawned = run_process_spawned.schedule_with_parent(&proc); 158 | let run_arc_process = run_arc_process_spawned.arc_process; 159 | 160 | ProcessResultReceiver { 161 | process: run_arc_process, 162 | rx, 163 | } 164 | } 165 | 166 | extern "C" fn return_ok(argument_list: Term, closure_term: Term) -> Term { 167 | let mut argument_vec: Vec = Vec::new(); 168 | match argument_list.decode().unwrap() { 169 | TypedTerm::Nil => (), 170 | TypedTerm::List(argument_cons) => { 171 | for result in argument_cons.into_iter() { 172 | let element = result.unwrap(); 173 | 174 | argument_vec.push(element); 175 | } 176 | } 177 | _ => panic!(), 178 | } 179 | assert!(argument_vec.len() == 1); 180 | 181 | let closure: Boxed = closure_term.try_into().unwrap(); 182 | let sender_resource: Boxed = closure.env_slice()[0].try_into().unwrap(); 183 | let sender_any: Resource = sender_resource.into(); 184 | let sender: &ProcessResultSender = sender_any.downcast_ref().unwrap(); 185 | 186 | let mut fragment = HeapFragment::new_from_word_size(100).unwrap(); 187 | let frag_mut = unsafe { fragment.as_mut() }; 188 | let ret = argument_vec[0].clone_to_heap(frag_mut).unwrap(); 189 | 190 | sender 191 | .tx 192 | .send(ProcessResult { 193 | heap: fragment, 194 | result: Ok(ret), 195 | }) 196 | .unwrap(); 197 | 198 | argument_vec[0] 199 | } 200 | 201 | fn return_throw(arc_process: &Arc) -> Term { 202 | let argument_list = arc_process.stack_pop().unwrap(); 203 | let closure_term = arc_process.stack_pop().unwrap(); 204 | 205 | let mut argument_vec: Vec = Vec::new(); 206 | match argument_list.decode().unwrap() { 207 | TypedTerm::Nil => (), 208 | TypedTerm::List(argument_cons) => { 209 | for result in argument_cons.into_iter() { 210 | let element = result.unwrap(); 211 | 212 | argument_vec.push(element); 213 | } 214 | } 215 | _ => panic!(), 216 | } 217 | 218 | let closure: Boxed = closure_term.try_into().unwrap(); 219 | let sender_resource: Boxed = closure.env_slice()[0].try_into().unwrap(); 220 | let sender_any: Resource = sender_resource.into(); 221 | let sender: &ProcessResultSender = sender_any.downcast_ref().unwrap(); 222 | 223 | let mut fragment = HeapFragment::new_from_word_size(100).unwrap(); 224 | let frag_mut = unsafe { fragment.as_mut() }; 225 | 226 | let ret_type = argument_vec[0].clone_to_heap(frag_mut).unwrap(); 227 | let ret_reason = argument_vec[1].clone_to_heap(frag_mut).unwrap(); 228 | let ret_trace = argument_vec[2].clone_to_heap(frag_mut).unwrap(); 229 | 230 | sender 231 | .tx 232 | .send(ProcessResult { 233 | heap: fragment, 234 | result: Err((ret_type, ret_reason, ret_trace)), 235 | }) 236 | .unwrap(); 237 | 238 | let class: exception::Class = argument_vec[0].try_into().unwrap(); 239 | 240 | let reason = argument_vec[1]; 241 | let stacktrace = Some(argument_vec[2]); 242 | let exc = exception::raise( 243 | class, 244 | reason, 245 | stacktrace, 246 | anyhow!("explicit throw from Erlang").into(), 247 | ); 248 | arc_process.exception(exc); 249 | 250 | Term::NONE 251 | } 252 | -------------------------------------------------------------------------------- /src/native/erlang.rs: -------------------------------------------------------------------------------- 1 | use liblumen_alloc::erts::process::Status; 2 | use liblumen_alloc::erts::term::prelude::*; 3 | 4 | use liblumen_otp::erlang; 5 | 6 | use crate::module::NativeModule; 7 | 8 | pub fn make_erlang() -> NativeModule { 9 | let mut native = NativeModule::new(Atom::try_from_str("erlang").unwrap()); 10 | 11 | native.add_simple(Atom::try_from_str("*").unwrap(), 2, |proc, args| { 12 | erlang::multiply_2::result(proc, args[0], args[1]) 13 | }); 14 | 15 | native.add_simple(Atom::try_from_str("/").unwrap(), 2, |proc, args| { 16 | erlang::div_2::result(proc, args[0], args[1]) 17 | }); 18 | 19 | native.add_simple(Atom::try_from_str("<").unwrap(), 2, |_proc, args| { 20 | Ok(erlang::is_less_than_2::result(args[0], args[1])) 21 | }); 22 | native.add_simple(Atom::try_from_str(">").unwrap(), 2, |_proc, args| { 23 | Ok(erlang::is_greater_than_2::result(args[0], args[1])) 24 | }); 25 | native.add_simple(Atom::try_from_str("=<").unwrap(), 2, |_proc, args| { 26 | Ok(erlang::is_equal_or_less_than_2::result(args[0], args[1])) 27 | }); 28 | native.add_simple(Atom::try_from_str(">=").unwrap(), 2, |_proc, args| { 29 | Ok(erlang::is_greater_than_or_equal_2::result(args[0], args[1])) 30 | }); 31 | native.add_simple(Atom::try_from_str("==").unwrap(), 2, |_proc, args| { 32 | Ok(erlang::are_equal_after_conversion_2::result( 33 | args[0], args[1], 34 | )) 35 | }); 36 | native.add_simple(Atom::try_from_str("=:=").unwrap(), 2, |_proc, args| { 37 | Ok(erlang::are_exactly_equal_2::result(args[0], args[1])) 38 | }); 39 | 40 | native.add_simple(Atom::try_from_str("spawn_opt").unwrap(), 4, |proc, args| { 41 | match args[3].decode().unwrap() { 42 | TypedTerm::List(cons) => { 43 | let mut iter = cons.into_iter(); 44 | assert!(iter.next() == Some(Ok(Atom::str_to_term("link").into()))); 45 | assert!(iter.next() == None); 46 | } 47 | t => panic!("{:?}", t), 48 | } 49 | 50 | let ret = crate::code::return_clean::closure(proc)?; 51 | 52 | let inner_args = proc.cons(ret, proc.cons(ret, args[2])?)?; 53 | 54 | let res = erlang::spawn_link_3::result(proc, args[0], args[1], inner_args)?; 55 | Ok(res) 56 | }); 57 | 58 | native.add_simple(Atom::try_from_str("spawn").unwrap(), 3, |proc, args| { 59 | let ret = crate::code::return_clean::closure(proc)?; 60 | 61 | let inner_args = proc.cons(ret, proc.cons(ret, args[2])?)?; 62 | erlang::spawn_3::result(proc, args[0], args[1], inner_args) 63 | }); 64 | 65 | native.add_simple( 66 | Atom::try_from_str("spawn_link").unwrap(), 67 | 3, 68 | |proc, args| { 69 | let ret = crate::code::return_clean::closure(proc)?; 70 | 71 | let inner_args = proc.cons(ret, proc.cons(ret, args[2])?)?; 72 | erlang::spawn_link_3::result(proc, args[0], args[1], inner_args) 73 | }, 74 | ); 75 | 76 | native.add_simple(Atom::try_from_str("exit").unwrap(), 1, |_proc, args| { 77 | panic!("{:?}", args[0]); 78 | //Ok(erlang::exit_1::result(args[0]).unwrap()) 79 | }); 80 | 81 | native.add_simple(Atom::try_from_str("monitor").unwrap(), 2, |proc, args| { 82 | erlang::monitor_2::result(proc, args[0], args[1]) 83 | }); 84 | native.add_simple(Atom::try_from_str("demonitor").unwrap(), 2, |proc, args| { 85 | erlang::demonitor_2::result(proc, args[0], args[1]) 86 | }); 87 | 88 | native.add_simple(Atom::try_from_str("register").unwrap(), 2, |proc, args| { 89 | erlang::register_2::result(proc.clone(), args[0], args[1]) 90 | }); 91 | native.add_simple( 92 | Atom::try_from_str("process_flag").unwrap(), 93 | 2, 94 | |proc, args| erlang::process_flag_2::result(proc, args[0], args[1]), 95 | ); 96 | 97 | let send_2_module_function_arity = erlang::send_2::module_function_arity(); 98 | native.add_simple( 99 | send_2_module_function_arity.function, 100 | send_2_module_function_arity.arity as usize, 101 | |proc, args| erlang::send_2::result(proc, args[0], args[1]), 102 | ); 103 | 104 | native.add_simple(Atom::try_from_str("send").unwrap(), 3, |proc, args| { 105 | erlang::send_2::result(proc, args[0], args[1]) 106 | }); 107 | 108 | native.add_simple(Atom::try_from_str("!").unwrap(), 2, |proc, args| { 109 | erlang::send_2::result(proc, args[0], args[1]) 110 | }); 111 | 112 | native.add_simple(Atom::try_from_str("-").unwrap(), 2, |proc, args| { 113 | erlang::subtract_2::result(proc, args[0], args[1]) 114 | }); 115 | 116 | native.add_simple(Atom::try_from_str("+").unwrap(), 2, |proc, args| { 117 | erlang::add_2::result(proc, args[0], args[1]) 118 | }); 119 | 120 | native.add_simple(Atom::try_from_str("self").unwrap(), 0, |proc, _args| { 121 | Ok(proc.pid_term()) 122 | }); 123 | 124 | native.add_simple( 125 | Atom::try_from_str("is_integer").unwrap(), 126 | 1, 127 | |_proc, args| { 128 | assert!(args.len() == 1); 129 | Ok(erlang::is_integer_1::result(args[0])) 130 | }, 131 | ); 132 | native.add_simple(Atom::try_from_str("is_list").unwrap(), 1, |_proc, args| { 133 | Ok(erlang::is_list_1::result(args[0])) 134 | }); 135 | native.add_simple( 136 | Atom::try_from_str("is_binary").unwrap(), 137 | 1, 138 | |_proc, args| Ok(erlang::is_binary_1::result(args[0])), 139 | ); 140 | native.add_simple(Atom::try_from_str("is_atom").unwrap(), 1, |_proc, args| { 141 | Ok(erlang::is_atom_1::result(args[0])) 142 | }); 143 | native.add_simple(Atom::try_from_str("is_pid").unwrap(), 1, |_proc, args| { 144 | Ok(erlang::is_pid_1::result(args[0])) 145 | }); 146 | native.add_simple( 147 | Atom::try_from_str("is_function").unwrap(), 148 | 1, 149 | |_proc, args| Ok(erlang::is_function_1::result(args[0])), 150 | ); 151 | native.add_simple( 152 | Atom::try_from_str("is_function").unwrap(), 153 | 2, 154 | |_proc, args| Ok(erlang::is_function_1::result(args[0])), 155 | ); 156 | native.add_simple(Atom::try_from_str("is_tuple").unwrap(), 1, |_proc, args| { 157 | Ok(erlang::is_tuple_1::result(args[0])) 158 | }); 159 | native.add_simple(Atom::try_from_str("is_map").unwrap(), 1, |_proc, args| { 160 | Ok(erlang::is_map_1::result(args[0])) 161 | }); 162 | native.add_simple( 163 | Atom::try_from_str("is_bitstring").unwrap(), 164 | 1, 165 | |_proc, args| Ok(erlang::is_bitstring_1::result(args[0])), 166 | ); 167 | native.add_simple(Atom::try_from_str("is_float").unwrap(), 1, |_proc, args| { 168 | Ok(erlang::is_bitstring_1::result(args[0])) 169 | }); 170 | 171 | native.add_simple( 172 | Atom::try_from_str("monotonic_time").unwrap(), 173 | 0, 174 | |proc, _args| erlang::monotonic_time_0::result(proc), 175 | ); 176 | 177 | native.add_yielding(Atom::try_from_str("apply").unwrap(), 3, |proc, args| { 178 | let inner_args = proc.cons(args[0], proc.cons(args[1], args[4])?)?; 179 | 180 | let term = crate::code::apply(args[2], args[3], inner_args); 181 | 182 | if term.is_none() { 183 | match *proc.status.read() { 184 | Status::SystemException(ref system_exception) => Err(system_exception.clone()), 185 | _ => Ok(()), 186 | } 187 | } else { 188 | Ok(()) 189 | } 190 | }); 191 | 192 | native.add_simple(Atom::try_from_str("node").unwrap(), 0, |_proc, _args| { 193 | Ok(erlang::node_0::result()) 194 | }); 195 | native.add_simple(Atom::try_from_str("node").unwrap(), 1, |_proc, _args| { 196 | Ok(Atom::str_to_term("nonode@nohost")) 197 | }); 198 | native.add_simple(Atom::try_from_str("whereis").unwrap(), 1, |_proc, args| { 199 | erlang::whereis_1::result(args[0]) 200 | }); 201 | 202 | native.add_simple( 203 | Atom::try_from_str("process_info").unwrap(), 204 | 2, 205 | |proc, args| erlang::process_info_2::result(proc, args[0], args[1]), 206 | ); 207 | 208 | native.add_simple(Atom::try_from_str("get").unwrap(), 1, |proc, args| { 209 | Ok(erlang::get_1::result(proc, args[0])) 210 | }); 211 | native.add_simple(Atom::try_from_str("put").unwrap(), 2, |proc, args| { 212 | Ok(proc.put(args[0], args[1])?) 213 | }); 214 | 215 | native.add_simple( 216 | Atom::try_from_str("convert_time_unit").unwrap(), 217 | 3, 218 | |proc, args| erlang::convert_time_unit_3::result(proc, args[0], args[1], args[2]), 219 | ); 220 | 221 | native.add_simple(Atom::try_from_str("element").unwrap(), 2, |_proc, args| { 222 | erlang::element_2::result(args[0], args[1]) 223 | }); 224 | 225 | native 226 | } 227 | -------------------------------------------------------------------------------- /examples/simple_server/gen.erl: -------------------------------------------------------------------------------- 1 | -file("gen.erl", 1). 2 | 3 | -module(gen). 4 | 5 | -compile({inline,[{get_node,1}]}). 6 | 7 | -export([start/5, 8 | start/6, 9 | debug_options/2, 10 | hibernate_after/1, 11 | name/1, 12 | unregister_name/1, 13 | get_proc_name/1, 14 | get_parent/0, 15 | call/3, 16 | call/4, 17 | reply/2, 18 | stop/1, 19 | stop/3]). 20 | 21 | -export([init_it/6,init_it/7]). 22 | 23 | -export([format_status_header/2]). 24 | 25 | -type linkage() :: link | nolink. 26 | 27 | -type emgr_name() :: 28 | {local, atom()} | 29 | {global, term()} | 30 | {via, Module :: module(), Name :: term()}. 31 | 32 | -type start_ret() :: {ok, pid()} | ignore | {error, term()}. 33 | 34 | -type debug_flag() :: 35 | trace | log | statistics | debug | {logfile, string()}. 36 | 37 | -type option() :: 38 | {timeout, timeout()} | 39 | {debug, [debug_flag()]} | 40 | {hibernate_after, timeout()} | 41 | {spawn_opt, [proc_lib:spawn_option()]}. 42 | 43 | -type options() :: [option()]. 44 | 45 | -spec start(module(), 46 | linkage(), 47 | emgr_name(), 48 | module(), 49 | term(), 50 | options()) -> 51 | start_ret(). 52 | 53 | start(GenMod, LinkP, Name, Mod, Args, Options) -> 54 | case where(Name) of 55 | undefined -> 56 | do_spawn(GenMod, LinkP, Name, Mod, Args, Options); 57 | Pid -> 58 | {error,{already_started,Pid}} 59 | end. 60 | 61 | -spec start(module(), linkage(), module(), term(), options()) -> 62 | start_ret(). 63 | 64 | start(GenMod, LinkP, Mod, Args, Options) -> 65 | do_spawn(GenMod, LinkP, Mod, Args, Options). 66 | 67 | do_spawn(GenMod, link, Mod, Args, Options) -> 68 | Time = timeout(Options), 69 | proc_lib:start_link(gen, 70 | init_it, 71 | [GenMod,self(),self(),Mod,Args,Options], 72 | Time, 73 | spawn_opts(Options)); 74 | do_spawn(GenMod, _, Mod, Args, Options) -> 75 | Time = timeout(Options), 76 | proc_lib:start(gen, 77 | init_it, 78 | [GenMod,self(),self,Mod,Args,Options], 79 | Time, 80 | spawn_opts(Options)). 81 | 82 | do_spawn(GenMod, link, Name, Mod, Args, Options) -> 83 | Time = timeout(Options), 84 | proc_lib:start_link(gen, 85 | init_it, 86 | [GenMod,self(),self(),Name,Mod,Args,Options], 87 | Time, 88 | spawn_opts(Options)); 89 | do_spawn(GenMod, _, Name, Mod, Args, Options) -> 90 | Time = timeout(Options), 91 | proc_lib:start(gen, 92 | init_it, 93 | [GenMod,self(),self,Name,Mod,Args,Options], 94 | Time, 95 | spawn_opts(Options)). 96 | 97 | init_it(GenMod, Starter, Parent, Mod, Args, Options) -> 98 | init_it2(GenMod, Starter, Parent, self(), Mod, Args, Options). 99 | 100 | init_it(GenMod, Starter, Parent, Name, Mod, Args, Options) -> 101 | case register_name(Name) of 102 | true -> 103 | init_it2(GenMod, Starter, Parent, Name, Mod, Args, Options); 104 | {false,Pid} -> 105 | proc_lib:init_ack(Starter, {error,{already_started,Pid}}) 106 | end. 107 | 108 | init_it2(GenMod, Starter, Parent, Name, Mod, Args, Options) -> 109 | GenMod:init_it(Starter, Parent, Name, Mod, Args, Options). 110 | 111 | call(Process, Label, Request) -> 112 | call(Process, Label, Request, 5000). 113 | 114 | call(Process, Label, Request, Timeout) 115 | when 116 | is_pid(Process), 117 | Timeout =:= infinity 118 | orelse 119 | is_integer(Timeout) 120 | andalso 121 | Timeout >= 0 -> 122 | do_call(Process, Label, Request, Timeout); 123 | call(Process, Label, Request, Timeout) 124 | when Timeout =:= infinity; is_integer(Timeout), Timeout >= 0 -> 125 | Fun = 126 | fun(Pid) -> 127 | do_call(Pid, Label, Request, Timeout) 128 | end, 129 | do_for_proc(Process, Fun). 130 | 131 | do_call(Process, Label, Request, Timeout) 132 | when is_atom(Process) =:= false -> 133 | Mref = monitor(process, Process), 134 | erlang:send(Process, {Label,{self(),Mref},Request}, [noconnect]), 135 | receive 136 | {Mref,Reply} -> 137 | demonitor(Mref, [flush]), 138 | {ok,Reply}; 139 | {'DOWN',Mref,_,_,noconnection} -> 140 | Node = get_node(Process), 141 | exit({nodedown,Node}); 142 | {'DOWN',Mref,_,_,Reason} -> 143 | exit(Reason) 144 | after 145 | Timeout -> 146 | demonitor(Mref, [flush]), 147 | exit(timeout) 148 | end. 149 | 150 | get_node(Process) -> 151 | case Process of 152 | {_S,N} when is_atom(N) -> 153 | N; 154 | _ when is_pid(Process) -> 155 | node(Process) 156 | end. 157 | 158 | reply({To,Tag}, Reply) -> 159 | Msg = {Tag,Reply}, 160 | try 161 | To ! Msg 162 | catch 163 | _:_ -> 164 | Msg 165 | end. 166 | 167 | stop(Process) -> 168 | stop(Process, normal, infinity). 169 | 170 | stop(Process, Reason, Timeout) 171 | when Timeout =:= infinity; is_integer(Timeout), Timeout >= 0 -> 172 | Fun = 173 | fun(Pid) -> 174 | proc_lib:stop(Pid, Reason, Timeout) 175 | end, 176 | do_for_proc(Process, Fun). 177 | 178 | do_for_proc(Pid, Fun) when is_pid(Pid) -> 179 | Fun(Pid); 180 | do_for_proc(Name, Fun) when is_atom(Name) -> 181 | case whereis(Name) of 182 | Pid when is_pid(Pid) -> 183 | Fun(Pid); 184 | undefined -> 185 | exit(noproc) 186 | end; 187 | do_for_proc(Process, Fun) 188 | when 189 | tuple_size(Process) == 2 190 | andalso 191 | element(1, Process) == global 192 | orelse 193 | tuple_size(Process) == 3 194 | andalso 195 | element(1, Process) == via -> 196 | case where(Process) of 197 | Pid when is_pid(Pid) -> 198 | Node = node(Pid), 199 | try 200 | Fun(Pid) 201 | catch 202 | exit:{nodedown,Node} -> 203 | exit(noproc) 204 | end; 205 | undefined -> 206 | exit(noproc) 207 | end; 208 | do_for_proc({Name,Node}, Fun) when Node =:= node() -> 209 | do_for_proc(Name, Fun); 210 | do_for_proc({_Name,Node} = Process, Fun) when is_atom(Node) -> 211 | if 212 | node() =:= nonode@nohost -> 213 | exit({nodedown,Node}); 214 | true -> 215 | Fun(Process) 216 | end. 217 | 218 | where({global,Name}) -> 219 | global:whereis_name(Name); 220 | where({via,Module,Name}) -> 221 | Module:whereis_name(Name); 222 | where({local,Name}) -> 223 | whereis(Name). 224 | 225 | register_name({local,Name} = LN) -> 226 | try register(Name, self()) of 227 | true -> 228 | true 229 | catch 230 | error:_ -> 231 | {false,where(LN)} 232 | end; 233 | register_name({global,Name} = GN) -> 234 | case global:register_name(Name, self()) of 235 | yes -> 236 | true; 237 | no -> 238 | {false,where(GN)} 239 | end; 240 | register_name({via,Module,Name} = GN) -> 241 | case Module:register_name(Name, self()) of 242 | yes -> 243 | true; 244 | no -> 245 | {false,where(GN)} 246 | end. 247 | 248 | name({local,Name}) -> 249 | Name; 250 | name({global,Name}) -> 251 | Name; 252 | name({via,_,Name}) -> 253 | Name; 254 | name(Pid) when is_pid(Pid) -> 255 | Pid. 256 | 257 | unregister_name({local,Name}) -> 258 | try unregister(Name) of 259 | _ -> 260 | ok 261 | catch 262 | _:_ -> 263 | ok 264 | end; 265 | unregister_name({global,Name}) -> 266 | _ = global:unregister_name(Name), 267 | ok; 268 | unregister_name({via,Mod,Name}) -> 269 | _ = Mod:unregister_name(Name), 270 | ok; 271 | unregister_name(Pid) when is_pid(Pid) -> 272 | ok. 273 | 274 | get_proc_name(Pid) when is_pid(Pid) -> 275 | Pid; 276 | get_proc_name({local,Name}) -> 277 | case process_info(self(), registered_name) of 278 | {registered_name,Name} -> 279 | Name; 280 | {registered_name,_Name} -> 281 | exit(process_not_registered); 282 | [] -> 283 | exit(process_not_registered) 284 | end; 285 | get_proc_name({global,Name}) -> 286 | case global:whereis_name(Name) of 287 | undefined -> 288 | exit(process_not_registered_globally); 289 | Pid when Pid =:= self() -> 290 | Name; 291 | _Pid -> 292 | exit(process_not_registered_globally) 293 | end; 294 | get_proc_name({via,Mod,Name}) -> 295 | case Mod:whereis_name(Name) of 296 | undefined -> 297 | exit({process_not_registered_via,Mod}); 298 | Pid when Pid =:= self() -> 299 | Name; 300 | _Pid -> 301 | exit({process_not_registered_via,Mod}) 302 | end. 303 | 304 | get_parent() -> 305 | case get('$ancestors') of 306 | [Parent|_] when is_pid(Parent) -> 307 | Parent; 308 | [Parent|_] when is_atom(Parent) -> 309 | name_to_pid(Parent); 310 | _ -> 311 | exit(process_was_not_started_by_proc_lib) 312 | end. 313 | 314 | name_to_pid(Name) -> 315 | case whereis(Name) of 316 | undefined -> 317 | case global:whereis_name(Name) of 318 | undefined -> 319 | exit(could_not_find_registered_name); 320 | Pid -> 321 | Pid 322 | end; 323 | Pid -> 324 | Pid 325 | end. 326 | 327 | timeout(Options) -> 328 | case lists:keyfind(timeout, 1, Options) of 329 | {_,Time} -> 330 | Time; 331 | false -> 332 | infinity 333 | end. 334 | 335 | spawn_opts(Options) -> 336 | case lists:keyfind(spawn_opt, 1, Options) of 337 | {_,Opts} -> 338 | Opts; 339 | false -> 340 | [] 341 | end. 342 | 343 | hibernate_after(Options) -> 344 | case lists:keyfind(hibernate_after, 1, Options) of 345 | {_,HibernateAfterTimeout} -> 346 | HibernateAfterTimeout; 347 | false -> 348 | infinity 349 | end. 350 | 351 | debug_options(Name, Opts) -> 352 | case lists:keyfind(debug, 1, Opts) of 353 | {_,Options} -> 354 | try 355 | sys:debug_options(Options) 356 | catch 357 | _:_ -> 358 | error_logger:format("~tp: ignoring erroneous debug " 359 | "options - ~tp~n", 360 | [Name,Options]), 361 | [] 362 | end; 363 | false -> 364 | [] 365 | end. 366 | 367 | format_status_header(TagLine, Pid) when is_pid(Pid) -> 368 | lists:concat([TagLine," ",pid_to_list(Pid)]); 369 | format_status_header(TagLine, RegName) when is_atom(RegName) -> 370 | lists:concat([TagLine," ",RegName]); 371 | format_status_header(TagLine, Name) -> 372 | {TagLine,Name}. 373 | 374 | 375 | 376 | -------------------------------------------------------------------------------- /examples/chain/timer.erl: -------------------------------------------------------------------------------- 1 | -file("timer.erl", 1). 2 | 3 | -module(timer). 4 | 5 | -export([apply_after/4, 6 | send_after/3, 7 | send_after/2, 8 | exit_after/3, 9 | exit_after/2, 10 | kill_after/2, 11 | kill_after/1, 12 | apply_interval/4, 13 | send_interval/3, 14 | send_interval/2, 15 | cancel/1, 16 | sleep/1, 17 | tc/1, 18 | tc/2, 19 | tc/3, 20 | now_diff/2, 21 | seconds/1, 22 | minutes/1, 23 | hours/1, 24 | hms/3]). 25 | 26 | -export([start_link/0, 27 | start/0, 28 | handle_call/3, 29 | handle_info/2, 30 | init/1, 31 | code_change/3, 32 | handle_cast/2, 33 | terminate/2]). 34 | 35 | -export([get_status/0]). 36 | 37 | -export_type([tref/0]). 38 | 39 | -opaque tref() :: {integer(), reference()}. 40 | 41 | -type time() :: non_neg_integer(). 42 | 43 | -spec apply_after(Time, Module, Function, Arguments) -> 44 | {ok, TRef} | {error, Reason} 45 | when 46 | Time :: time(), 47 | Module :: module(), 48 | Function :: atom(), 49 | Arguments :: [term()], 50 | TRef :: tref(), 51 | Reason :: term(). 52 | 53 | apply_after(Time, M, F, A) -> 54 | req(apply_after, {Time,{M,F,A}}). 55 | 56 | -spec send_after(Time, Pid, Message) -> {ok, TRef} | {error, Reason} 57 | when 58 | Time :: time(), 59 | Pid :: pid() | (RegName :: atom()), 60 | Message :: term(), 61 | TRef :: tref(), 62 | Reason :: term(). 63 | 64 | send_after(Time, Pid, Message) -> 65 | req(apply_after, {Time,{timer,send,[Pid,Message]}}). 66 | 67 | -spec send_after(Time, Message) -> {ok, TRef} | {error, Reason} 68 | when 69 | Time :: time(), 70 | Message :: term(), 71 | TRef :: tref(), 72 | Reason :: term(). 73 | 74 | send_after(Time, Message) -> 75 | send_after(Time, self(), Message). 76 | 77 | -spec exit_after(Time, Pid, Reason1) -> {ok, TRef} | {error, Reason2} 78 | when 79 | Time :: time(), 80 | Pid :: pid() | (RegName :: atom()), 81 | TRef :: tref(), 82 | Reason1 :: term(), 83 | Reason2 :: term(). 84 | 85 | exit_after(Time, Pid, Reason) -> 86 | req(apply_after, {Time,{erlang,exit,[Pid,Reason]}}). 87 | 88 | -spec exit_after(Time, Reason1) -> {ok, TRef} | {error, Reason2} 89 | when 90 | Time :: time(), 91 | TRef :: tref(), 92 | Reason1 :: term(), 93 | Reason2 :: term(). 94 | 95 | exit_after(Time, Reason) -> 96 | exit_after(Time, self(), Reason). 97 | 98 | -spec kill_after(Time, Pid) -> {ok, TRef} | {error, Reason2} 99 | when 100 | Time :: time(), 101 | Pid :: pid() | (RegName :: atom()), 102 | TRef :: tref(), 103 | Reason2 :: term(). 104 | 105 | kill_after(Time, Pid) -> 106 | exit_after(Time, Pid, kill). 107 | 108 | -spec kill_after(Time) -> {ok, TRef} | {error, Reason2} 109 | when 110 | Time :: time(), 111 | TRef :: tref(), 112 | Reason2 :: term(). 113 | 114 | kill_after(Time) -> 115 | exit_after(Time, self(), kill). 116 | 117 | -spec apply_interval(Time, Module, Function, Arguments) -> 118 | {ok, TRef} | {error, Reason} 119 | when 120 | Time :: time(), 121 | Module :: module(), 122 | Function :: atom(), 123 | Arguments :: [term()], 124 | TRef :: tref(), 125 | Reason :: term(). 126 | 127 | apply_interval(Time, M, F, A) -> 128 | req(apply_interval, {Time,self(),{M,F,A}}). 129 | 130 | -spec send_interval(Time, Pid, Message) -> {ok, TRef} | {error, Reason} 131 | when 132 | Time :: time(), 133 | Pid :: pid() | (RegName :: atom()), 134 | Message :: term(), 135 | TRef :: tref(), 136 | Reason :: term(). 137 | 138 | send_interval(Time, Pid, Message) -> 139 | req(apply_interval, {Time,Pid,{timer,send,[Pid,Message]}}). 140 | 141 | -spec send_interval(Time, Message) -> {ok, TRef} | {error, Reason} 142 | when 143 | Time :: time(), 144 | Message :: term(), 145 | TRef :: tref(), 146 | Reason :: term(). 147 | 148 | send_interval(Time, Message) -> 149 | send_interval(Time, self(), Message). 150 | 151 | -spec cancel(TRef) -> {ok, cancel} | {error, Reason} 152 | when TRef :: tref(), Reason :: term(). 153 | 154 | cancel(BRef) -> 155 | req(cancel, BRef). 156 | 157 | -spec sleep(Time) -> ok when Time :: timeout(). 158 | 159 | sleep(T) -> 160 | receive after T -> ok end. 161 | 162 | -spec tc(Fun) -> {Time, Value} 163 | when Fun :: function(), Time :: integer(), Value :: term(). 164 | 165 | tc(F) -> 166 | T1 = erlang:monotonic_time(), 167 | Val = F(), 168 | T2 = erlang:monotonic_time(), 169 | Time = erlang:convert_time_unit(T2 - T1, native, microsecond), 170 | {Time,Val}. 171 | 172 | -spec tc(Fun, Arguments) -> {Time, Value} 173 | when 174 | Fun :: function(), 175 | Arguments :: [term()], 176 | Time :: integer(), 177 | Value :: term(). 178 | 179 | tc(F, A) -> 180 | T1 = erlang:monotonic_time(), 181 | Val = apply(F, A), 182 | T2 = erlang:monotonic_time(), 183 | Time = erlang:convert_time_unit(T2 - T1, native, microsecond), 184 | {Time,Val}. 185 | 186 | -spec tc(Module, Function, Arguments) -> {Time, Value} 187 | when 188 | Module :: module(), 189 | Function :: atom(), 190 | Arguments :: [term()], 191 | Time :: integer(), 192 | Value :: term(). 193 | 194 | tc(M, F, A) -> 195 | T1 = erlang:monotonic_time(), 196 | Val = apply(M, F, A), 197 | T2 = erlang:monotonic_time(), 198 | Time = erlang:convert_time_unit(T2 - T1, native, microsecond), 199 | {Time,Val}. 200 | 201 | -spec now_diff(T2, T1) -> Tdiff 202 | when 203 | T1 :: erlang:timestamp(), 204 | T2 :: erlang:timestamp(), 205 | Tdiff :: integer(). 206 | 207 | now_diff({A2,B2,C2}, {A1,B1,C1}) -> 208 | ((A2 - A1) * 1000000 + B2 - B1) * 1000000 + C2 - C1. 209 | 210 | -spec seconds(Seconds) -> MilliSeconds 211 | when 212 | Seconds :: non_neg_integer(), 213 | MilliSeconds :: non_neg_integer(). 214 | 215 | seconds(Seconds) -> 216 | 1000 * Seconds. 217 | 218 | -spec minutes(Minutes) -> MilliSeconds 219 | when 220 | Minutes :: non_neg_integer(), 221 | MilliSeconds :: non_neg_integer(). 222 | 223 | minutes(Minutes) -> 224 | 1000 * 60 * Minutes. 225 | 226 | -spec hours(Hours) -> MilliSeconds 227 | when 228 | Hours :: non_neg_integer(), 229 | MilliSeconds :: non_neg_integer(). 230 | 231 | hours(Hours) -> 232 | 1000 * 60 * 60 * Hours. 233 | 234 | -spec hms(Hours, Minutes, Seconds) -> MilliSeconds 235 | when 236 | Hours :: non_neg_integer(), 237 | Minutes :: non_neg_integer(), 238 | Seconds :: non_neg_integer(), 239 | MilliSeconds :: non_neg_integer(). 240 | 241 | hms(H, M, S) -> 242 | hours(H) + minutes(M) + seconds(S). 243 | 244 | -spec start() -> ok. 245 | 246 | start() -> 247 | ensure_started(). 248 | 249 | -spec start_link() -> {ok, pid()} | {error, term()}. 250 | 251 | start_link() -> 252 | gen_server:start_link({local,timer_server}, timer, [], []). 253 | 254 | -spec init([]) -> {ok, [], infinity}. 255 | 256 | init([]) -> 257 | process_flag(trap_exit, true), 258 | timer_tab = ets:new(timer_tab, [named_table,ordered_set,protected]), 259 | timer_interval_tab = 260 | ets:new(timer_interval_tab, [named_table,protected]), 261 | {ok,[],infinity}. 262 | 263 | -spec ensure_started() -> ok. 264 | 265 | ensure_started() -> 266 | case whereis(timer_server) of 267 | undefined -> 268 | C = {timer_server, 269 | {timer,start_link,[]}, 270 | permanent, 271 | 1000, 272 | worker, 273 | [timer]}, 274 | _ = supervisor:start_child(kernel_safe_sup, C), 275 | ok; 276 | _ -> 277 | ok 278 | end. 279 | 280 | req(Req, Arg) -> 281 | SysTime = system_time(), 282 | ensure_started(), 283 | gen_server:call(timer_server, {Req,Arg,SysTime}, infinity). 284 | 285 | -type timers() :: term(). 286 | 287 | -spec handle_call(term(), term(), timers()) -> 288 | {reply, term(), timers(), timeout()} | 289 | {noreply, timers(), timeout()}. 290 | 291 | handle_call({apply_after,{Time,Op},Started}, _From, _Ts) 292 | when is_integer(Time), Time >= 0 -> 293 | BRef = {Started + 1000 * Time,make_ref()}, 294 | Timer = {BRef,timeout,Op}, 295 | ets:insert(timer_tab, Timer), 296 | Timeout = timer_timeout(system_time()), 297 | {reply,{ok,BRef},[],Timeout}; 298 | handle_call({apply_interval,{Time,To,MFA},Started}, _From, _Ts) 299 | when is_integer(Time), Time >= 0 -> 300 | case get_pid(To) of 301 | Pid when is_pid(Pid) -> 302 | catch link(Pid), 303 | SysTime = system_time(), 304 | Ref = make_ref(), 305 | BRef1 = {interval,Ref}, 306 | Interval = Time * 1000, 307 | BRef2 = {Started + Interval,Ref}, 308 | Timer = {BRef2,{repeat,Interval,Pid},MFA}, 309 | ets:insert(timer_interval_tab, {BRef1,BRef2,Pid}), 310 | ets:insert(timer_tab, Timer), 311 | Timeout = timer_timeout(SysTime), 312 | {reply,{ok,BRef1},[],Timeout}; 313 | _ -> 314 | {reply,{error,badarg},[],next_timeout()} 315 | end; 316 | handle_call({cancel,BRef = {_Time,Ref},_}, _From, Ts) 317 | when is_reference(Ref) -> 318 | delete_ref(BRef), 319 | {reply,{ok,cancel},Ts,next_timeout()}; 320 | handle_call({cancel,_BRef,_}, _From, Ts) -> 321 | {reply,{error,badarg},Ts,next_timeout()}; 322 | handle_call({apply_after,_,_}, _From, Ts) -> 323 | {reply,{error,badarg},Ts,next_timeout()}; 324 | handle_call({apply_interval,_,_}, _From, Ts) -> 325 | {reply,{error,badarg},Ts,next_timeout()}; 326 | handle_call(_Else, _From, Ts) -> 327 | {noreply,Ts,next_timeout()}. 328 | 329 | -spec handle_info(term(), timers()) -> {noreply, timers(), timeout()}. 330 | 331 | handle_info(timeout, Ts) -> 332 | Timeout = timer_timeout(system_time()), 333 | {noreply,Ts,Timeout}; 334 | handle_info({'EXIT',Pid,_Reason}, Ts) -> 335 | pid_delete(Pid), 336 | {noreply,Ts,next_timeout()}; 337 | handle_info(_OtherMsg, Ts) -> 338 | {noreply,Ts,next_timeout()}. 339 | 340 | -spec handle_cast(term(), timers()) -> {noreply, timers(), timeout()}. 341 | 342 | handle_cast(_Req, Ts) -> 343 | {noreply,Ts,next_timeout()}. 344 | 345 | -spec terminate(term(), _State) -> ok. 346 | 347 | terminate(_Reason, _State) -> 348 | ok. 349 | 350 | -spec code_change(term(), State, term()) -> {ok, State}. 351 | 352 | code_change(_OldVsn, State, _Extra) -> 353 | {ok,State}. 354 | 355 | timer_timeout(SysTime) -> 356 | case ets:first(timer_tab) of 357 | '$end_of_table' -> 358 | infinity; 359 | {Time,_Ref} when Time > SysTime -> 360 | Timeout = (Time - SysTime + 999) div 1000, 361 | min(Timeout, 8388608); 362 | Key -> 363 | case ets:lookup(timer_tab, Key) of 364 | [{Key,timeout,MFA}] -> 365 | ets:delete(timer_tab, Key), 366 | do_apply(MFA), 367 | timer_timeout(SysTime); 368 | [{{Time,Ref},Repeat = {repeat,Interv,To},MFA}] -> 369 | ets:delete(timer_tab, Key), 370 | NewTime = Time + Interv, 371 | ets:insert(timer_interval_tab, 372 | {{interval,Ref},{NewTime,Ref},To}), 373 | do_apply(MFA), 374 | ets:insert(timer_tab, {{NewTime,Ref},Repeat,MFA}), 375 | timer_timeout(SysTime) 376 | end 377 | end. 378 | 379 | delete_ref(BRef = {interval,_}) -> 380 | case ets:lookup(timer_interval_tab, BRef) of 381 | [{_,BRef2,_Pid}] -> 382 | ets:delete(timer_interval_tab, BRef), 383 | ets:delete(timer_tab, BRef2); 384 | _ -> 385 | ok 386 | end; 387 | delete_ref(BRef) -> 388 | ets:delete(timer_tab, BRef). 389 | 390 | -spec pid_delete(pid()) -> ok. 391 | 392 | pid_delete(Pid) -> 393 | IntervalTimerList = 394 | ets:select(timer_interval_tab, 395 | [{{'_','_','$1'},[{'==','$1',Pid}],['$_']}]), 396 | lists:foreach(fun({IntKey,TimerKey,_}) -> 397 | ets:delete(timer_interval_tab, IntKey), 398 | ets:delete(timer_tab, TimerKey) 399 | end, 400 | IntervalTimerList). 401 | 402 | -spec next_timeout() -> timeout(). 403 | 404 | next_timeout() -> 405 | case ets:first(timer_tab) of 406 | '$end_of_table' -> 407 | infinity; 408 | {Time,_} -> 409 | min(positive((Time - system_time() + 999) div 1000), 410 | 8388608) 411 | end. 412 | 413 | do_apply({M,F,A}) -> 414 | case {M,F,A} of 415 | {timer,send,A} -> 416 | catch send(A); 417 | {erlang,exit,[Name,Reason]} -> 418 | catch exit(get_pid(Name), Reason); 419 | _ -> 420 | catch spawn(M, F, A) 421 | end. 422 | 423 | positive(X) -> 424 | max(X, 0). 425 | 426 | system_time() -> 427 | erlang:monotonic_time(1000000). 428 | 429 | send([Pid,Msg]) -> 430 | Pid ! Msg. 431 | 432 | get_pid(Name) when is_pid(Name) -> 433 | Name; 434 | get_pid(undefined) -> 435 | undefined; 436 | get_pid(Name) when is_atom(Name) -> 437 | get_pid(whereis(Name)); 438 | get_pid(_) -> 439 | undefined. 440 | 441 | -spec get_status() -> 442 | {{timer_tab, non_neg_integer()}, 443 | {timer_interval_tab, non_neg_integer()}}. 444 | 445 | get_status() -> 446 | Info1 = ets:info(timer_tab), 447 | {size,TotalNumTimers} = lists:keyfind(size, 1, Info1), 448 | Info2 = ets:info(timer_interval_tab), 449 | {size,NumIntervalTimers} = lists:keyfind(size, 1, Info2), 450 | {{timer_tab,TotalNumTimers},{timer_interval_tab,NumIntervalTimers}}. 451 | 452 | 453 | 454 | -------------------------------------------------------------------------------- /examples/simple_server/Elixir.Keyword.erl: -------------------------------------------------------------------------------- 1 | -file("/home/build/elixir/lib/elixir/lib/keyword.ex", 1). 2 | 3 | -module('Elixir.Keyword'). 4 | 5 | -compile([inline_list_funcs,{inline,[{delete,2}]}]). 6 | 7 | -spec 'equal?'(t(), t()) -> boolean(). 8 | 9 | -spec new() -> []. 10 | 11 | -spec put_new(t(), key(), value()) -> t(). 12 | 13 | -spec drop(t(), [key()]) -> t(). 14 | 15 | -spec get(t(), key(), value()) -> value(). 16 | 17 | -spec split(t(), [key()]) -> {t(), t()}. 18 | 19 | -spec merge(t(), t(), fun((key(), value(), value()) -> value())) -> t(). 20 | 21 | -spec 'has_key?'(t(), key()) -> boolean(). 22 | 23 | -spec pop_lazy(t(), key(), fun(() -> value())) -> {value(), t()}. 24 | 25 | -spec pop(t(), key(), value()) -> {value(), t()}. 26 | 27 | -spec get_values(t(), key()) -> [value()]. 28 | 29 | -spec 'keyword?'(term()) -> boolean(). 30 | 31 | -spec put_new_lazy(t(), key(), fun(() -> value())) -> t(). 32 | 33 | -spec keys(t()) -> [key()]. 34 | 35 | -spec fetch(t(), key()) -> {ok, value()} | error. 36 | 37 | -spec delete(t(), key()) -> t(). 38 | 39 | -spec get_and_update(t(), key(), fun((value()) -> {get, value()} | pop)) -> 40 | {get, t()} 41 | when get :: term(). 42 | 43 | -spec take(t(), [key()]) -> t(). 44 | 45 | -spec delete(t(), key(), value()) -> t(). 46 | 47 | -spec update(t(), key(), value(), fun((value()) -> value())) -> t(). 48 | 49 | -spec pop_first(t(), key(), value()) -> {value(), t()}. 50 | 51 | -spec 'update!'(t(), key(), fun((value()) -> value())) -> t(). 52 | 53 | -spec new('Elixir.Enum':t()) -> t(). 54 | 55 | -spec 'replace!'(t(), key(), value()) -> t(). 56 | 57 | -spec get_lazy(t(), key(), fun(() -> value())) -> value(). 58 | 59 | -spec delete_first(t(), key()) -> t(). 60 | 61 | -spec new('Elixir.Enum':t(), fun((term()) -> {key(), value()})) -> t(). 62 | 63 | -spec merge(t(), t()) -> t(). 64 | 65 | -spec values(t()) -> [value()]. 66 | 67 | -spec 'fetch!'(t(), key()) -> value(). 68 | 69 | -spec 'get_and_update!'(t(), key(), fun((value()) -> {get, value()})) -> 70 | {get, t()} 71 | when get :: term(). 72 | 73 | -spec put(t(), key(), value()) -> t(). 74 | 75 | -spec to_list(t()) -> t(). 76 | 77 | -export_type([t/1]). 78 | 79 | -type t(value) :: [{key(), value}]. 80 | 81 | -export_type([t/0]). 82 | 83 | -type t() :: [{key(), value()}]. 84 | 85 | -export_type([value/0]). 86 | 87 | -type value() :: any(). 88 | 89 | -export_type([key/0]). 90 | 91 | -type key() :: atom(). 92 | 93 | -export(['__info__'/1, 94 | delete/2, 95 | delete/3, 96 | delete_first/2, 97 | drop/2, 98 | 'equal?'/2, 99 | fetch/2, 100 | 'fetch!'/2, 101 | get/2, 102 | get/3, 103 | get_and_update/3, 104 | 'get_and_update!'/3, 105 | get_lazy/3, 106 | get_values/2, 107 | 'has_key?'/2, 108 | keys/1, 109 | 'keyword?'/1, 110 | merge/2, 111 | merge/3, 112 | new/0, 113 | new/1, 114 | new/2, 115 | pop/2, 116 | pop/3, 117 | pop_first/2, 118 | pop_first/3, 119 | pop_lazy/3, 120 | put/3, 121 | put_new/3, 122 | put_new_lazy/3, 123 | replace/3, 124 | 'replace!'/3, 125 | size/1, 126 | split/2, 127 | take/2, 128 | to_list/1, 129 | update/4, 130 | 'update!'/3, 131 | values/1]). 132 | 133 | -spec '__info__'(attributes | 134 | compile | 135 | functions | 136 | macros | 137 | md5 | 138 | module | 139 | deprecated) -> 140 | any(). 141 | 142 | '__info__'(module) -> 143 | 'Elixir.Keyword'; 144 | '__info__'(functions) -> 145 | [{delete,2}, 146 | {delete,3}, 147 | {delete_first,2}, 148 | {drop,2}, 149 | {'equal?',2}, 150 | {fetch,2}, 151 | {'fetch!',2}, 152 | {get,2}, 153 | {get,3}, 154 | {get_and_update,3}, 155 | {'get_and_update!',3}, 156 | {get_lazy,3}, 157 | {get_values,2}, 158 | {'has_key?',2}, 159 | {keys,1}, 160 | {'keyword?',1}, 161 | {merge,2}, 162 | {merge,3}, 163 | {new,0}, 164 | {new,1}, 165 | {new,2}, 166 | {pop,2}, 167 | {pop,3}, 168 | {pop_first,2}, 169 | {pop_first,3}, 170 | {pop_lazy,3}, 171 | {put,3}, 172 | {put_new,3}, 173 | {put_new_lazy,3}, 174 | {replace,3}, 175 | {'replace!',3}, 176 | {size,1}, 177 | {split,2}, 178 | {take,2}, 179 | {to_list,1}, 180 | {update,4}, 181 | {'update!',3}, 182 | {values,1}]; 183 | '__info__'(macros) -> 184 | []; 185 | '__info__'(Key = attributes) -> 186 | erlang:get_module_info('Elixir.Keyword', Key); 187 | '__info__'(Key = compile) -> 188 | erlang:get_module_info('Elixir.Keyword', Key); 189 | '__info__'(Key = md5) -> 190 | erlang:get_module_info('Elixir.Keyword', Key); 191 | '__info__'(deprecated) -> 192 | [{{replace,3},<<"Use Keyword.fetch/2 + Keyword.put/3 instead">>}, 193 | {{size,1},<<"Use Kernel.length/1 instead">>}]. 194 | 195 | delete(_keywords@1, _key@1) 196 | when 197 | is_list(_keywords@1) 198 | andalso 199 | is_atom(_key@1) -> 200 | case lists:keymember(_key@1, 1, _keywords@1) of 201 | true -> 202 | delete_key(_keywords@1, _key@1); 203 | _ -> 204 | _keywords@1 205 | end. 206 | 207 | delete(_keywords@1, _key@1, _value@1) 208 | when 209 | is_list(_keywords@1) 210 | andalso 211 | is_atom(_key@1) -> 212 | case lists:keymember(_key@1, 1, _keywords@1) of 213 | true -> 214 | delete_key_value(_keywords@1, _key@1, _value@1); 215 | _ -> 216 | _keywords@1 217 | end. 218 | 219 | delete_first(_keywords@1, _key@1) 220 | when 221 | is_list(_keywords@1) 222 | andalso 223 | is_atom(_key@1) -> 224 | case lists:keymember(_key@1, 1, _keywords@1) of 225 | true -> 226 | delete_first_key(_keywords@1, _key@1); 227 | _ -> 228 | _keywords@1 229 | end. 230 | 231 | delete_first_key([{_key@1,_}|_tail@1], _key@1) -> 232 | _tail@1; 233 | delete_first_key([{_,_} = _pair@1|_tail@1], _key@1) -> 234 | [_pair@1|delete_first_key(_tail@1, _key@1)]; 235 | delete_first_key([], __key@1) -> 236 | []. 237 | 238 | delete_key([{_key@1,_}|_tail@1], _key@1) -> 239 | delete_key(_tail@1, _key@1); 240 | delete_key([{_,_} = _pair@1|_tail@1], _key@1) -> 241 | [_pair@1|delete_key(_tail@1, _key@1)]; 242 | delete_key([], __key@1) -> 243 | []. 244 | 245 | delete_key_value([{_key@1,_value@1}|_tail@1], _key@1, _value@1) -> 246 | delete_key_value(_tail@1, _key@1, _value@1); 247 | delete_key_value([{_,_} = _pair@1|_tail@1], _key@1, _value@1) -> 248 | [_pair@1|delete_key_value(_tail@1, _key@1, _value@1)]; 249 | delete_key_value([], __key@1, __value@1) -> 250 | []. 251 | 252 | do_merge([{_key@1,_value2@1}|_tail@1], 253 | _acc@1, 254 | _rest@1, 255 | _original@1, 256 | _fun@1, 257 | _keywords2@1) 258 | when is_atom(_key@1) -> 259 | case lists:keyfind(_key@1, 1, _original@1) of 260 | {_key@1,_value1@1} -> 261 | _acc@2 = 262 | [{_key@1,_fun@1(_key@1, _value1@1, _value2@1)}|_acc@1], 263 | _original@2 = lists:keydelete(_key@1, 1, _original@1), 264 | do_merge(_tail@1, 265 | _acc@2, 266 | delete(_rest@1, _key@1), 267 | _original@2, 268 | _fun@1, 269 | _keywords2@1); 270 | false -> 271 | do_merge(_tail@1, 272 | [{_key@1,_value2@1}|_acc@1], 273 | _rest@1, 274 | _original@1, 275 | _fun@1, 276 | _keywords2@1) 277 | end; 278 | do_merge([], _acc@1, _rest@1, __original@1, __fun@1, __keywords2@1) -> 279 | _rest@1 ++ lists:reverse(_acc@1); 280 | do_merge(__other@1, 281 | __acc@1, 282 | __rest@1, 283 | __original@1, 284 | __fun@1, 285 | _keywords2@1) -> 286 | error('Elixir.ArgumentError':exception(<<"expected a keyword list a" 287 | "s the second argument, go" 288 | "t: ", 289 | ('Elixir.Kernel':inspect(_keywords2@1))/binary>>)). 290 | 291 | drop(_keywords@1, _keys@1) when is_list(_keywords@1) -> 292 | lists:filter(fun({_key@1,_}) -> 293 | not 'Elixir.Enum':'member?'(_keys@1, _key@1) 294 | end, 295 | _keywords@1). 296 | 297 | 'equal?'(_left@1, _right@1) 298 | when 299 | is_list(_left@1) 300 | andalso 301 | is_list(_right@1) -> 302 | lists:sort(_left@1) == lists:sort(_right@1). 303 | 304 | fetch(_keywords@1, _key@1) 305 | when 306 | is_list(_keywords@1) 307 | andalso 308 | is_atom(_key@1) -> 309 | case lists:keyfind(_key@1, 1, _keywords@1) of 310 | {_key@1,_value@1} -> 311 | {ok,_value@1}; 312 | false -> 313 | error 314 | end. 315 | 316 | 'fetch!'(_keywords@1, _key@1) 317 | when 318 | is_list(_keywords@1) 319 | andalso 320 | is_atom(_key@1) -> 321 | case lists:keyfind(_key@1, 1, _keywords@1) of 322 | {_key@1,_value@1} -> 323 | _value@1; 324 | false -> 325 | error('Elixir.KeyError':exception([{key,_key@1}, 326 | {term,_keywords@1}])) 327 | end. 328 | 329 | get(__@1, __@2) -> 330 | get(__@1, __@2, nil). 331 | 332 | get(_keywords@1, _key@1, _default@1) 333 | when 334 | is_list(_keywords@1) 335 | andalso 336 | is_atom(_key@1) -> 337 | case lists:keyfind(_key@1, 1, _keywords@1) of 338 | {_key@1,_value@1} -> 339 | _value@1; 340 | false -> 341 | _default@1 342 | end. 343 | 344 | get_and_update(_keywords@1, _key@1, _fun@1) 345 | when 346 | is_list(_keywords@1) 347 | andalso 348 | is_atom(_key@1) -> 349 | get_and_update(_keywords@1, [], _key@1, _fun@1). 350 | 351 | get_and_update([{_key@1,_current@1}|_t@1], _acc@1, _key@1, _fun@1) -> 352 | case _fun@1(_current@1) of 353 | {_get@1,_value@1} -> 354 | {_get@1,lists:reverse(_acc@1, [{_key@1,_value@1}|_t@1])}; 355 | pop -> 356 | {_current@1,lists:reverse(_acc@1, _t@1)}; 357 | _other@1 -> 358 | error('Elixir.RuntimeError':exception(<<"the given function" 359 | " must return a two" 360 | "-element tuple or " 361 | ":pop, got: ", 362 | ('Elixir.Kernel':inspect(_other@1))/binary>>)) 363 | end; 364 | get_and_update([{_,_} = _h@1|_t@1], _acc@1, _key@1, _fun@1) -> 365 | get_and_update(_t@1, [_h@1|_acc@1], _key@1, _fun@1); 366 | get_and_update([], _acc@1, _key@1, _fun@1) -> 367 | case _fun@1(nil) of 368 | {_get@1,_update@1} -> 369 | {_get@1,[{_key@1,_update@1}|lists:reverse(_acc@1)]}; 370 | pop -> 371 | {nil,lists:reverse(_acc@1)}; 372 | _other@1 -> 373 | error('Elixir.RuntimeError':exception(<<"the given function" 374 | " must return a two" 375 | "-element tuple or " 376 | ":pop, got: ", 377 | ('Elixir.Kernel':inspect(_other@1))/binary>>)) 378 | end. 379 | 380 | 'get_and_update!'(_keywords@1, _key@1, _fun@1) -> 381 | 'get_and_update!'(_keywords@1, _key@1, _fun@1, []). 382 | 383 | 'get_and_update!'([{_key@1,_value@1}|_keywords@1], 384 | _key@1, 385 | _fun@1, 386 | _acc@1) -> 387 | case _fun@1(_value@1) of 388 | {_get@1,_value@2} -> 389 | {_get@1, 390 | lists:reverse(_acc@1, 391 | [{_key@1,_value@2}| 392 | delete(_keywords@1, _key@1)])}; 393 | pop -> 394 | {_value@1,lists:reverse(_acc@1, _keywords@1)}; 395 | _other@1 -> 396 | error('Elixir.RuntimeError':exception(<<"the given function" 397 | " must return a two" 398 | "-element tuple or " 399 | ":pop, got: ", 400 | ('Elixir.Kernel':inspect(_other@1))/binary>>)) 401 | end; 402 | 'get_and_update!'([{_,_} = _e@1|_keywords@1], _key@1, _fun@1, _acc@1) -> 403 | 'get_and_update!'(_keywords@1, _key@1, _fun@1, [_e@1|_acc@1]); 404 | 'get_and_update!'([], _key@1, __fun@1, _acc@1) when is_atom(_key@1) -> 405 | error('Elixir.KeyError':exception([{key,_key@1},{term,_acc@1}])). 406 | 407 | get_lazy(_keywords@1, _key@1, _fun@1) 408 | when 409 | (is_list(_keywords@1) 410 | andalso 411 | is_atom(_key@1)) 412 | andalso 413 | is_function(_fun@1, 0) -> 414 | case lists:keyfind(_key@1, 1, _keywords@1) of 415 | {_key@1,_value@1} -> 416 | _value@1; 417 | false -> 418 | _fun@1() 419 | end. 420 | 421 | get_values(_keywords@1, _key@1) 422 | when 423 | is_list(_keywords@1) 424 | andalso 425 | is_atom(_key@1) -> 426 | _fun@1 = 427 | fun({_key@2,_val@1}) when _key@1 =:= _key@2 -> 428 | {true,_val@1}; 429 | ({_,_}) -> 430 | false 431 | end, 432 | lists:filtermap(_fun@1, _keywords@1). 433 | 434 | 'has_key?'(_keywords@1, _key@1) 435 | when 436 | is_list(_keywords@1) 437 | andalso 438 | is_atom(_key@1) -> 439 | lists:keymember(_key@1, 1, _keywords@1). 440 | 441 | keys(_keywords@1) when is_list(_keywords@1) -> 442 | lists:map(fun({_k@1,_}) -> 443 | _k@1 444 | end, 445 | _keywords@1). 446 | 447 | 'keyword?'([{_key@1,__value@1}|_rest@1]) when is_atom(_key@1) -> 448 | 'keyword?'(_rest@1); 449 | 'keyword?'([]) -> 450 | true; 451 | 'keyword?'(__other@1) -> 452 | false. 453 | 454 | merge(_keywords1@1, []) when is_list(_keywords1@1) -> 455 | _keywords1@1; 456 | merge([], _keywords2@1) when is_list(_keywords2@1) -> 457 | _keywords2@1; 458 | merge(_keywords1@1, _keywords2@1) 459 | when 460 | is_list(_keywords1@1) 461 | andalso 462 | is_list(_keywords2@1) -> 463 | case 'keyword?'(_keywords2@1) of 464 | __@1 465 | when 466 | __@1 =:= nil 467 | orelse 468 | __@1 =:= false -> 469 | error('Elixir.ArgumentError':exception(<<"expected a keywor" 470 | "d list as the sec" 471 | "ond argument, got" 472 | ": ", 473 | ('Elixir.Kernel':inspect(_keywords2@1))/binary>>)); 474 | _ -> 475 | _fun@1 = 476 | fun({_key@1,__value@1}) when is_atom(_key@1) -> 477 | not 'has_key?'(_keywords2@1, _key@1); 478 | (_) -> 479 | error('Elixir.ArgumentError':exception(<<"expect" 480 | "ed a k" 481 | "eyword" 482 | " list " 483 | "as the" 484 | " first" 485 | " argum" 486 | "ent, g" 487 | "ot: ", 488 | ('Elixir.Kernel':inspect(_keywords1@1))/binary>>)) 489 | end, 490 | lists:filter(_fun@1, _keywords1@1) ++ _keywords2@1 491 | end. 492 | 493 | merge(_keywords1@1, _keywords2@1, _fun@1) 494 | when 495 | (is_list(_keywords1@1) 496 | andalso 497 | is_list(_keywords2@1)) 498 | andalso 499 | is_function(_fun@1, 3) -> 500 | case 'keyword?'(_keywords1@1) of 501 | __@1 502 | when 503 | __@1 =:= nil 504 | orelse 505 | __@1 =:= false -> 506 | error('Elixir.ArgumentError':exception(<<"expected a keywor" 507 | "d list as the fir" 508 | "st argument, got:" 509 | " ", 510 | ('Elixir.Kernel':inspect(_keywords1@1))/binary>>)); 511 | _ -> 512 | do_merge(_keywords2@1, 513 | [], 514 | _keywords1@1, 515 | _keywords1@1, 516 | _fun@1, 517 | _keywords2@1) 518 | end. 519 | 520 | new() -> 521 | []. 522 | 523 | new(_pairs@1) -> 524 | new(_pairs@1, 525 | fun(_pair@1) -> 526 | _pair@1 527 | end). 528 | 529 | new(_pairs@1, _transform@1) -> 530 | _fun@1 = 531 | fun(_el@1, _acc@1) -> 532 | {_k@1,_v@1} = _transform@1(_el@1), 533 | put_new(_acc@1, _k@1, _v@1) 534 | end, 535 | lists:foldl(_fun@1, [], 'Elixir.Enum':reverse(_pairs@1)). 536 | 537 | pop(__@1, __@2) -> 538 | pop(__@1, __@2, nil). 539 | 540 | pop(_keywords@1, _key@1, _default@1) when is_list(_keywords@1) -> 541 | case fetch(_keywords@1, _key@1) of 542 | {ok,_value@1} -> 543 | {_value@1,delete(_keywords@1, _key@1)}; 544 | error -> 545 | {_default@1,_keywords@1} 546 | end. 547 | 548 | pop_first(__@1, __@2) -> 549 | pop_first(__@1, __@2, nil). 550 | 551 | pop_first(_keywords@1, _key@1, _default@1) when is_list(_keywords@1) -> 552 | case lists:keytake(_key@1, 1, _keywords@1) of 553 | {value,{_key@1,_value@1},_rest@1} -> 554 | {_value@1,_rest@1}; 555 | false -> 556 | {_default@1,_keywords@1} 557 | end. 558 | 559 | pop_lazy(_keywords@1, _key@1, _fun@1) 560 | when 561 | is_list(_keywords@1) 562 | andalso 563 | is_function(_fun@1, 0) -> 564 | case fetch(_keywords@1, _key@1) of 565 | {ok,_value@1} -> 566 | {_value@1,delete(_keywords@1, _key@1)}; 567 | error -> 568 | {_fun@1(),_keywords@1} 569 | end. 570 | 571 | put(_keywords@1, _key@1, _value@1) 572 | when 573 | is_list(_keywords@1) 574 | andalso 575 | is_atom(_key@1) -> 576 | [{_key@1,_value@1}|delete(_keywords@1, _key@1)]. 577 | 578 | put_new(_keywords@1, _key@1, _value@1) 579 | when 580 | is_list(_keywords@1) 581 | andalso 582 | is_atom(_key@1) -> 583 | case lists:keyfind(_key@1, 1, _keywords@1) of 584 | {_key@1,_} -> 585 | _keywords@1; 586 | false -> 587 | [{_key@1,_value@1}|_keywords@1] 588 | end. 589 | 590 | put_new_lazy(_keywords@1, _key@1, _fun@1) 591 | when 592 | (is_list(_keywords@1) 593 | andalso 594 | is_atom(_key@1)) 595 | andalso 596 | is_function(_fun@1, 0) -> 597 | case lists:keyfind(_key@1, 1, _keywords@1) of 598 | {_key@1,_} -> 599 | _keywords@1; 600 | false -> 601 | [{_key@1,_fun@1()}|_keywords@1] 602 | end. 603 | 604 | replace(_keywords@1, _key@1, _value@1) 605 | when 606 | is_list(_keywords@1) 607 | andalso 608 | is_atom(_key@1) -> 609 | case lists:keyfind(_key@1, 1, _keywords@1) of 610 | {_key@1,_} -> 611 | [{_key@1,_value@1}|delete(_keywords@1, _key@1)]; 612 | false -> 613 | _keywords@1 614 | end. 615 | 616 | 'replace!'(_keywords@1, _key@1, _value@1) 617 | when 618 | is_list(_keywords@1) 619 | andalso 620 | is_atom(_key@1) -> 621 | case lists:keyfind(_key@1, 1, _keywords@1) of 622 | {_key@1,_} -> 623 | [{_key@1,_value@1}|delete(_keywords@1, _key@1)]; 624 | false -> 625 | error('Elixir.KeyError':exception([{key,_key@1}, 626 | {term,_keywords@1}])) 627 | end. 628 | 629 | size(_keyword@1) -> 630 | length(_keyword@1). 631 | 632 | split(_keywords@1, _keys@1) when is_list(_keywords@1) -> 633 | _fun@1 = 634 | fun({_k@1,_v@1}, {_take@1,_drop@1}) -> 635 | case 'Elixir.Enum':'member?'(_keys@1, _k@1) of 636 | true -> 637 | {[{_k@1,_v@1}|_take@1],_drop@1}; 638 | false -> 639 | {_take@1,[{_k@1,_v@1}|_drop@1]} 640 | end 641 | end, 642 | _acc@1 = {[],[]}, 643 | {_take@2,_drop@2} = lists:foldl(_fun@1, _acc@1, _keywords@1), 644 | {lists:reverse(_take@2),lists:reverse(_drop@2)}. 645 | 646 | take(_keywords@1, _keys@1) when is_list(_keywords@1) -> 647 | lists:filter(fun({_k@1,_}) -> 648 | 'Elixir.Enum':'member?'(_keys@1, _k@1) 649 | end, 650 | _keywords@1). 651 | 652 | to_list(_keyword@1) when is_list(_keyword@1) -> 653 | _keyword@1. 654 | 655 | update([{_key@1,_value@1}|_keywords@1], _key@1, __initial@1, _fun@1) -> 656 | [{_key@1,_fun@1(_value@1)}|delete(_keywords@1, _key@1)]; 657 | update([{_,_} = _e@1|_keywords@1], _key@1, _initial@1, _fun@1) -> 658 | [_e@1|update(_keywords@1, _key@1, _initial@1, _fun@1)]; 659 | update([], _key@1, _initial@1, __fun@1) when is_atom(_key@1) -> 660 | [{_key@1,_initial@1}]. 661 | 662 | 'update!'(_keywords@1, _key@1, _fun@1) -> 663 | 'update!'(_keywords@1, _key@1, _fun@1, _keywords@1). 664 | 665 | 'update!'([{_key@1,_value@1}|_keywords@1], _key@1, _fun@1, __dict@1) -> 666 | [{_key@1,_fun@1(_value@1)}|delete(_keywords@1, _key@1)]; 667 | 'update!'([{_,_} = _e@1|_keywords@1], _key@1, _fun@1, _dict@1) -> 668 | [_e@1|'update!'(_keywords@1, _key@1, _fun@1, _dict@1)]; 669 | 'update!'([], _key@1, __fun@1, _dict@1) when is_atom(_key@1) -> 670 | error('Elixir.KeyError':exception([{key,_key@1},{term,_dict@1}])). 671 | 672 | values(_keywords@1) when is_list(_keywords@1) -> 673 | lists:map(fun({_,_v@1}) -> 674 | _v@1 675 | end, 676 | _keywords@1). 677 | 678 | -------------------------------------------------------------------------------- /src/exec/mod.rs: -------------------------------------------------------------------------------- 1 | use std::convert::{AsRef, TryInto}; 2 | use std::ffi::c_void; 3 | use std::process::abort; 4 | use std::sync::Arc; 5 | 6 | use hashbrown::HashMap; 7 | 8 | use cranelift_entity::EntityRef; 9 | use libeir_intern::Symbol; 10 | use libeir_ir::constant::{AtomicTerm, Const, ConstKind}; 11 | use libeir_ir::{ 12 | BinOp, BinaryEntrySpecifier, Block, FunctionIndex, LogicOp, MapPutUpdate, OpKind, PrimOpKind, 13 | Value, ValueKind, 14 | }; 15 | 16 | use liblumen_alloc::atom; 17 | use liblumen_alloc::erts::exception::{Exception, RuntimeException, SystemException}; 18 | use liblumen_alloc::erts::process::gc::RootSet; 19 | use liblumen_alloc::erts::process::{Process, ProcessFlags}; 20 | use liblumen_alloc::erts::term::prelude::*; 21 | 22 | use crate::module::{ErlangFunction, NativeFunctionKind, ResolvedFunction}; 23 | use crate::vm::VMState; 24 | 25 | mod r#match; 26 | 27 | macro_rules! trace { 28 | ($($t:tt)*) => (crate::runtime::sys::io::puts(&format_args!($($t)*).to_string())) 29 | } 30 | 31 | const VALUE_LIST_MARKER: &str = "eir_value_list_marker_df8gy43h"; 32 | 33 | pub struct CallExecutor { 34 | binds: HashMap, 35 | next_args: Vec, 36 | } 37 | 38 | pub enum OpResult { 39 | Block(Block), 40 | Term(Term), 41 | TermYield(Term), 42 | } 43 | 44 | trait TermCollection { 45 | unsafe fn add(&mut self, root_set: &mut RootSet); 46 | } 47 | impl TermCollection for &mut CallExecutor { 48 | unsafe fn add(&mut self, root_set: &mut RootSet) { 49 | self.binds.add(root_set); 50 | (&mut self.next_args as &mut [Term]).add(root_set); 51 | } 52 | } 53 | impl TermCollection for Term { 54 | unsafe fn add(&mut self, root_set: &mut RootSet) { 55 | root_set.push(self); 56 | } 57 | } 58 | impl TermCollection for &mut Term { 59 | unsafe fn add(&mut self, root_set: &mut RootSet) { 60 | root_set.push(*self); 61 | } 62 | } 63 | impl TermCollection for &mut [Term] { 64 | unsafe fn add(&mut self, root_set: &mut RootSet) { 65 | for term in self.iter_mut() { 66 | root_set.push(term); 67 | } 68 | } 69 | } 70 | impl TermCollection for HashMap { 71 | unsafe fn add(&mut self, root_set: &mut RootSet) { 72 | for term in self.values_mut() { 73 | root_set.push(term); 74 | } 75 | } 76 | } 77 | impl TermCollection for (A, B) 78 | where 79 | A: TermCollection, 80 | B: TermCollection, 81 | { 82 | unsafe fn add(&mut self, root_set: &mut RootSet) { 83 | self.0.add(root_set); 84 | self.1.add(root_set); 85 | } 86 | } 87 | 88 | /// Will keep trying to execute the inner function and performing GC until 89 | /// we succeed without alloc error. 90 | fn try_gc(proc: &Arc, terms: &mut T, fun: &mut F) -> R 91 | where 92 | T: TermCollection, 93 | F: FnMut(&mut T) -> Result, 94 | { 95 | // Loop, keep trying the inner function until we succeed 96 | loop { 97 | match fun(terms) { 98 | Ok(inner) => break inner, 99 | Err(SystemException::Alloc(_)) => { 100 | let mut heap = proc.acquire_heap(); 101 | 102 | let mut rootset = RootSet::new(&mut []); 103 | // Process dictionary/other process related terms 104 | proc.base_root_set(&mut rootset); 105 | // Terms are in root set 106 | unsafe { terms.add(&mut rootset) }; 107 | 108 | crate::runtime::sys::io::puts( 109 | "=================================================== GC", 110 | ); 111 | match heap.garbage_collect(proc, 0, rootset) { 112 | Ok(_) => (), 113 | Err(_) => { 114 | proc.set_flags(ProcessFlags::NeedFullSweep); 115 | 116 | let mut rootset = RootSet::new(&mut []); 117 | // Process dictionary/other process related terms 118 | proc.base_root_set(&mut rootset); 119 | // Terms are in root set 120 | unsafe { terms.add(&mut rootset) }; 121 | 122 | crate::runtime::sys::io::puts( 123 | "=================================================== FULL GC", 124 | ); 125 | match heap.garbage_collect(proc, 0, rootset) { 126 | Ok(_) => (), 127 | Err(_) => panic!(), 128 | } 129 | } 130 | } 131 | } 132 | Err(fatal_err) => { 133 | crate::runtime::sys::io::puts(&format!("FATAL_ERROR!\n\n{:?}", fatal_err)); 134 | abort(); 135 | } 136 | } 137 | } 138 | } 139 | 140 | /// Sets up the current stack frame of `proc` to call `closure` with `args`. 141 | fn call_closure(proc: &Arc, mut closure: Term, args: &mut [Term]) { 142 | try_gc(proc, &mut (&mut closure, args), &mut |( 143 | closure_term, 144 | args, 145 | )| { 146 | call_closure_inner(proc, **closure_term, closure_term.decode().unwrap(), args) 147 | }) 148 | } 149 | 150 | fn call_closure_inner( 151 | proc: &Arc, 152 | closure_term: Term, 153 | closure_typed_term: TypedTerm, 154 | args: &mut [Term], 155 | ) -> Result<(), SystemException> { 156 | match closure_typed_term { 157 | TypedTerm::Closure(closure) => { 158 | let is_closure = closure.env_len() > 0; 159 | 160 | if is_closure { 161 | proc.stack_push(closure_term)?; 162 | } 163 | 164 | let arg_list = proc.list_from_iter(args.iter().cloned())?; 165 | let frame_with_arguments = closure.frame_with_arguments(false, vec![arg_list]); 166 | proc.queue_frame_with_arguments(frame_with_arguments); 167 | 168 | Ok(()) 169 | } 170 | t => panic!("CALL TO: {:?}", t), 171 | } 172 | } 173 | 174 | impl CallExecutor { 175 | pub fn new() -> Self { 176 | CallExecutor { 177 | binds: HashMap::new(), 178 | next_args: Vec::new(), 179 | } 180 | } 181 | 182 | /// Calls the given MFA with args. Will call the entry block. 183 | pub fn call( 184 | &mut self, 185 | vm: &VMState, 186 | proc: &Arc, 187 | module: Atom, 188 | function: Atom, 189 | arity: usize, 190 | args: &mut [Term], 191 | ) { 192 | trace!("======== RUN {} ========", proc.pid()); 193 | let modules = vm.modules.read().unwrap(); 194 | 195 | // Make sure no non-heap terms make it into the process 196 | { 197 | let heap = proc.acquire_heap(); 198 | for arg in args.iter() { 199 | if arg.is_boxed() { 200 | use liblumen_alloc::erts::process::alloc::Heap; 201 | let ptr: *const Term = arg.dyn_cast(); 202 | if !heap.is_owner(ptr) { 203 | println!("NON HEAP BOXED: {:?}", arg); 204 | panic!(); 205 | } 206 | } 207 | } 208 | } 209 | 210 | match modules.lookup_function(module, function, arity) { 211 | None => { 212 | self.fun_not_found(proc, args[1], module, function, arity); 213 | } 214 | Some(ResolvedFunction::Native(native)) => { 215 | assert!(arity + 2 == args.len()); 216 | self.run_native(vm, proc, native, args); 217 | } 218 | Some(ResolvedFunction::Erlang(fun)) => { 219 | let entry = fun.fun.block_entry(); 220 | self.run_erlang(vm, proc, fun, entry, args); 221 | } 222 | } 223 | } 224 | 225 | /// Calls a block in the given MFA with an environment. 226 | pub fn call_block( 227 | &mut self, 228 | vm: &VMState, 229 | proc: &Arc, 230 | module: Atom, 231 | fun_idx: FunctionIndex, 232 | args: &mut [Term], 233 | block: Block, 234 | env: &mut [Term], 235 | ) { 236 | trace!("======== RUN {} ========", proc.pid()); 237 | let modules = vm.modules.read().unwrap(); 238 | match modules.lookup_function_idx(module, fun_idx) { 239 | None => unreachable!(), 240 | Some(fun) => { 241 | let live = &fun.live.live_at(block); 242 | assert_eq!(live.size(), env.len()); 243 | 244 | for (v, t) in live.iter().zip(env.iter()) { 245 | self.binds.insert(v, *t); 246 | } 247 | 248 | self.run_erlang(vm, proc, fun, block, args); 249 | } 250 | } 251 | } 252 | 253 | fn fun_not_found( 254 | &self, 255 | _proc: &Arc, 256 | _throw_cont: Term, 257 | module: Atom, 258 | function: Atom, 259 | arity: usize, 260 | ) -> Term { 261 | panic!("Undef: {} {} {}", module, function, arity); 262 | //let exit_atom = Atom::str_to_term("EXIT"); 263 | //let undef_atom = Atom::str_to_term("undef"); 264 | //let trace_atom = Atom::str_to_term("trace"); 265 | //self.call_closure(proc, throw_cont, &[exit_atom, undef_atom, trace_atom]) 266 | } 267 | 268 | fn run_native( 269 | &mut self, 270 | _vm: &VMState, 271 | proc: &Arc, 272 | native: NativeFunctionKind, 273 | mut args: &mut [Term], 274 | ) { 275 | try_gc(proc, &mut args, &mut |args| match native { 276 | NativeFunctionKind::Simple(ptr) => match ptr(proc, &args[2..]) { 277 | Ok(ret) => Ok(call_closure(proc, args[0], &mut [ret])), 278 | Err(err) => match err { 279 | Exception::Runtime(RuntimeException::Throw(err)) => Ok(call_closure( 280 | proc, 281 | args[1], 282 | &mut [atom!("throw"), err.reason(), atom!("trace")], 283 | )), 284 | Exception::Runtime(RuntimeException::Exit(err)) => Ok(call_closure( 285 | proc, 286 | args[1], 287 | &mut [atom!("EXIT"), err.reason(), atom!("trace")], 288 | )), 289 | Exception::Runtime(RuntimeException::Error(err)) => Ok(call_closure( 290 | proc, 291 | args[1], 292 | &mut [atom!("error"), err.reason(), atom!("trace")], 293 | )), 294 | Exception::System(err) => return Err(err), 295 | }, 296 | }, 297 | NativeFunctionKind::Yielding(ptr) => ptr(proc, args), 298 | }) 299 | } 300 | 301 | fn run_erlang( 302 | &mut self, 303 | vm: &VMState, 304 | proc: &Arc, 305 | fun: &ErlangFunction, 306 | mut block: Block, 307 | args: &mut [Term], 308 | ) { 309 | self.next_args.extend(args.iter().cloned()); 310 | 311 | let mut exec = self; 312 | // Outer loop for optimized execution within the current function 313 | loop { 314 | // Insert block argument into environment 315 | let block_arg_vals = fun.fun.block_args(block); 316 | //trace!("{:?} {:?}", &block_arg_vals, &exec.next_args); 317 | assert!( 318 | block_arg_vals.len() == exec.next_args.len(), 319 | "{} == {}", 320 | block_arg_vals.len(), 321 | exec.next_args.len() 322 | ); 323 | for (v, t) in block_arg_vals.iter().zip(exec.next_args.iter()) { 324 | exec.binds.insert(*v, t.clone()); 325 | } 326 | 327 | match try_gc(proc, &mut exec, &mut |exec| { 328 | exec.next_args.clear(); 329 | exec.run_erlang_op(vm, proc, fun, block) 330 | }) { 331 | OpResult::Block(b) => { 332 | block = b; 333 | continue; 334 | } 335 | OpResult::Term(t) => break call_closure(proc, t, &mut exec.next_args), 336 | OpResult::TermYield(t) => break call_closure(proc, t, &mut exec.next_args), 337 | } 338 | } 339 | } 340 | 341 | fn make_const_term( 342 | &self, 343 | proc: &Arc, 344 | fun: &ErlangFunction, 345 | const_val: Const, 346 | ) -> Result { 347 | let res = match fun.fun.cons().const_kind(const_val) { 348 | ConstKind::Atomic(AtomicTerm::Atom(atom)) => Ok(Atom::str_to_term(&atom.0.as_str())), 349 | ConstKind::Atomic(AtomicTerm::Int(int)) => Ok(proc.integer(int.0)?), 350 | ConstKind::Atomic(AtomicTerm::Binary(bin)) => Ok(proc.binary_from_bytes(&bin.0)?), 351 | ConstKind::Tuple { entries } => { 352 | let vec: Result, _> = entries 353 | .as_slice(&fun.fun.cons().const_pool) 354 | .iter() 355 | .map(|e| self.make_const_term(proc, fun, *e)) 356 | .collect(); 357 | let tup = proc.tuple_from_slice(&vec?)?; 358 | Ok(tup) 359 | } 360 | ConstKind::ListCell { head, tail } => { 361 | let res = proc.cons( 362 | self.make_const_term(proc, fun, *head)?, 363 | self.make_const_term(proc, fun, *tail)?, 364 | )?; 365 | Ok(res) 366 | } 367 | ConstKind::Atomic(AtomicTerm::Nil) => Ok(Term::NIL), 368 | ConstKind::Map { keys, values } => { 369 | let mut map = HashMap::new(); 370 | 371 | for (k, v) in keys 372 | .as_slice(&fun.fun.cons().const_pool) 373 | .iter() 374 | .zip(values.as_slice(&fun.fun.cons().const_pool).iter()) 375 | { 376 | map.insert( 377 | self.make_const_term(proc, fun, *k)?, 378 | self.make_const_term(proc, fun, *v)?, 379 | ); 380 | } 381 | 382 | Ok(proc.map_from_hash_map(map)?) 383 | } 384 | kind => unimplemented!("{:?}", kind), 385 | }; 386 | res 387 | } 388 | 389 | fn make_closure( 390 | &self, 391 | proc: &Arc, 392 | fun: &ErlangFunction, 393 | block: Block, 394 | ) -> Result { 395 | let live = &fun.live.live_at(block); 396 | 397 | // FIXME vec alloc 398 | let mut env = Vec::new(); 399 | for v in live.iter() { 400 | assert!(fun.fun.value_argument(v).is_some()); 401 | env.push(self.make_term(proc, fun, v)?); 402 | } 403 | 404 | let module = Atom::try_from_str(fun.fun.ident().module.as_str()).unwrap(); 405 | let arity = fun.fun.ident().arity as u8; 406 | 407 | let closure = proc.anonymous_closure_with_env_from_slice( 408 | module, 409 | // TODO generate `index` scoped to `module` 410 | block.as_u32(), 411 | // TODO calculate `old_unique` from `code` 412 | fun.index.index() as u32, 413 | // TODO calculate `unique` from `code` 414 | Default::default(), 415 | arity, 416 | Some(crate::code::interpreter_closure::native as *const c_void), 417 | proc.pid().into(), 418 | &env, 419 | )?; 420 | 421 | Ok(closure) 422 | } 423 | 424 | fn make_term( 425 | &self, 426 | proc: &Arc, 427 | fun: &ErlangFunction, 428 | value: Value, 429 | ) -> Result { 430 | match fun.fun.value_kind(value) { 431 | ValueKind::Block(block) => self.make_closure(proc, fun, block), 432 | ValueKind::Argument(_, _) => Ok(self.binds[&value]), 433 | ValueKind::Const(cons) => self.make_const_term(proc, fun, cons), 434 | ValueKind::PrimOp(prim) => { 435 | let reads = fun.fun.primop_reads(prim); 436 | match fun.fun.primop_kind(prim) { 437 | PrimOpKind::ValueList => { 438 | let terms: Result, _> = reads 439 | .iter() 440 | .map(|r| self.make_term(proc, fun, *r)) 441 | .collect(); 442 | let mut vec = terms?; 443 | vec.insert(0, Atom::str_to_term(VALUE_LIST_MARKER)); 444 | let term = proc.tuple_from_slice(&vec)?; 445 | Ok(term) 446 | } 447 | PrimOpKind::Tuple => { 448 | let terms: Result, _> = reads 449 | .iter() 450 | .map(|r| self.make_term(proc, fun, *r)) 451 | .collect(); 452 | let vec = terms?; 453 | Ok(proc.tuple_from_slice(&vec)?) 454 | } 455 | PrimOpKind::ListCell => { 456 | assert!(reads.len() == 2); 457 | let head = self.make_term(proc, fun, reads[0])?; 458 | let tail = self.make_term(proc, fun, reads[1])?; 459 | let res = proc.cons(head, tail)?; 460 | Ok(res) 461 | } 462 | PrimOpKind::LogicOp(LogicOp::And) => { 463 | let mut acc = true; 464 | for read in reads.iter() { 465 | let term = self.make_term(proc, fun, *read).unwrap(); 466 | let res: bool = term.try_into().ok().unwrap(); 467 | acc = acc & res; 468 | } 469 | Ok(acc.into()) 470 | } 471 | PrimOpKind::LogicOp(LogicOp::Or) => { 472 | let mut acc = false; 473 | for read in reads.iter() { 474 | let term = self.make_term(proc, fun, *read).unwrap(); 475 | let res: bool = term.try_into().ok().unwrap(); 476 | acc = acc | res; 477 | } 478 | Ok(acc.into()) 479 | } 480 | PrimOpKind::BinOp(BinOp::Equal) => { 481 | let lhs: Atom = self.make_term(proc, fun, reads[0])?.try_into().unwrap(); 482 | let rhs: Atom = self.make_term(proc, fun, reads[1])?.try_into().unwrap(); 483 | Ok((lhs == rhs).into()) 484 | } 485 | PrimOpKind::CaptureFunction => { 486 | let module: Atom = self.make_term(proc, fun, reads[0])?.try_into().unwrap(); 487 | let function: Atom = 488 | self.make_term(proc, fun, reads[1])?.try_into().unwrap(); 489 | let arity: u8 = self.make_term(proc, fun, reads[2])?.try_into().unwrap(); 490 | 491 | Ok(proc.export_closure( 492 | module, 493 | function, 494 | arity, 495 | Some(crate::code::interpreter_mfa::native as *const c_void), 496 | )?) 497 | } 498 | kind => unimplemented!("{:?}", kind), 499 | } 500 | } 501 | } 502 | } 503 | 504 | fn val_call( 505 | &mut self, 506 | proc: &Arc, 507 | fun: &ErlangFunction, 508 | value: Value, 509 | ) -> Result { 510 | if let ValueKind::Block(block) = fun.fun.value_kind(value) { 511 | Ok(OpResult::Block(block)) 512 | } else { 513 | let term = self.make_term(proc, fun, value)?; 514 | Ok(OpResult::Term(term)) 515 | } 516 | } 517 | 518 | fn run_erlang_op( 519 | &mut self, 520 | _vm: &VMState, 521 | proc: &Arc, 522 | fun: &ErlangFunction, 523 | block: Block, 524 | ) -> Result { 525 | let reads = fun.fun.block_reads(block); 526 | let kind = fun.fun.block_kind(block).unwrap(); 527 | trace!("OP: {:?} {}", kind, block); 528 | println!("{:?}", reads); 529 | 530 | proc.reduce(); 531 | 532 | match kind { 533 | OpKind::Call(_) => { 534 | for read in reads.iter().skip(1) { 535 | let term = self.make_term(proc, fun, *read)?; 536 | self.next_args.push(term); 537 | } 538 | self.val_call(proc, fun, reads[0]) 539 | } 540 | OpKind::UnpackValueList(num) => { 541 | assert!(reads.len() == 2); 542 | let term = self.make_term(proc, fun, reads[1])?; 543 | match term.decode().unwrap() { 544 | TypedTerm::Tuple(items) => match items[0].decode().unwrap() { 545 | TypedTerm::Atom(atom) if atom.name() == VALUE_LIST_MARKER => { 546 | assert!(items.len() - 1 == *num); 547 | for item in items.iter().skip(1) { 548 | self.next_args.push(*item); 549 | } 550 | self.val_call(proc, fun, reads[0]) 551 | } 552 | _ => { 553 | self.next_args.push(term); 554 | self.val_call(proc, fun, reads[0]) 555 | } 556 | }, 557 | _ => { 558 | self.next_args.push(term); 559 | self.val_call(proc, fun, reads[0]) 560 | } 561 | } 562 | } 563 | OpKind::Intrinsic(name) if *name == Symbol::intern("bool_and") => { 564 | let mut res = true; 565 | for val in reads[1..].iter() { 566 | let term = self.make_term(proc, fun, *val)?; 567 | let b: bool = term.try_into().ok().unwrap(); 568 | res = res & b; 569 | } 570 | 571 | self.next_args.push(res.into()); 572 | self.val_call(proc, fun, reads[0]) 573 | } 574 | OpKind::Intrinsic(name) if *name == Symbol::intern("bool_or") => { 575 | let mut res = false; 576 | for val in reads[1..].iter() { 577 | let term = self.make_term(proc, fun, *val)?; 578 | let b: bool = term.try_into().ok().unwrap(); 579 | res = res | b; 580 | } 581 | 582 | self.next_args.push(res.into()); 583 | self.val_call(proc, fun, reads[0]) 584 | } 585 | OpKind::IfBool => { 586 | let call_n = if reads.len() == 4 { 587 | let bool_term = self.make_term(proc, fun, reads[3]).unwrap(); 588 | let b: std::result::Result = bool_term.try_into(); 589 | match b { 590 | Ok(true) => 0, 591 | Ok(false) => 1, 592 | Err(_) => 2, 593 | } 594 | } else if reads.len() == 3 { 595 | let bool_term = self.make_term(proc, fun, reads[2]).unwrap(); 596 | let b: std::result::Result = bool_term.try_into(); 597 | match b { 598 | Ok(true) => 0, 599 | Ok(false) => 1, 600 | Err(_) => unreachable!(), 601 | } 602 | } else { 603 | unreachable!() 604 | }; 605 | 606 | self.val_call(proc, fun, reads[call_n]) 607 | } 608 | OpKind::Match { branches } => self::r#match::match_op(self, proc, fun, branches, block), 609 | OpKind::MapPut { action } => { 610 | let map_term: Boxed = self.make_term(proc, fun, reads[2])?.try_into().unwrap(); 611 | let hashmap_ref: &HashMap = map_term.as_ref().as_ref(); 612 | let mut hashmap = hashmap_ref.clone(); 613 | 614 | let mut idx = 3; 615 | for action in action.iter() { 616 | let key = self.make_term(proc, fun, reads[idx])?; 617 | let val = self.make_term(proc, fun, reads[idx + 1])?; 618 | idx += 2; 619 | 620 | match action { 621 | MapPutUpdate::Put => { 622 | hashmap.insert(key, val); 623 | } 624 | MapPutUpdate::Update => { 625 | if hashmap.contains_key(&key) { 626 | unimplemented!() 627 | } 628 | hashmap.insert(key, val); 629 | } 630 | } 631 | } 632 | 633 | self.next_args.push(proc.map_from_hash_map(hashmap)?); 634 | return self.val_call(proc, fun, reads[0]); 635 | } 636 | OpKind::Intrinsic(name) if *name == Symbol::intern("receive_start") => { 637 | assert!(reads.len() == 2); 638 | 639 | let timeout = self.make_term(proc, fun, reads[1])?; 640 | // Only infinity supported 641 | assert!(timeout == Atom::str_to_term("infinity")); 642 | 643 | proc.mailbox.lock().borrow_mut().recv_start(); 644 | 645 | self.next_args.push(Term::NIL); 646 | self.val_call(proc, fun, reads[0]) 647 | } 648 | OpKind::Intrinsic(name) if *name == Symbol::intern("receive_wait") => { 649 | assert!(reads.len() == 2); 650 | 651 | let curr_cont = self.make_closure(proc, fun, block)?; 652 | 653 | let mailbox_lock = proc.mailbox.lock(); 654 | let mut mailbox = mailbox_lock.borrow_mut(); 655 | if let Some(msg_term) = mailbox.recv_peek() { 656 | mailbox.recv_increment(); 657 | 658 | std::mem::drop(mailbox); 659 | std::mem::drop(mailbox_lock); 660 | 661 | self.next_args.push(msg_term); 662 | self.val_call(proc, fun, reads[1]) 663 | } else { 664 | // If there are no messages, schedule a call 665 | // to the current block for later. 666 | self.next_args.push(Term::NIL); 667 | proc.wait(); 668 | Ok(OpResult::TermYield(curr_cont)) 669 | } 670 | } 671 | OpKind::Intrinsic(name) if *name == Symbol::intern("receive_done") => { 672 | assert!(reads.len() >= 1); 673 | 674 | let mailbox_lock = proc.mailbox.lock(); 675 | let mut mailbox = mailbox_lock.borrow_mut(); 676 | 677 | if mailbox.recv_last_off_heap() { 678 | // Copy to process heap 679 | unimplemented!() 680 | } else { 681 | for n in 0..(reads.len() - 1) { 682 | let term = self.make_term(proc, fun, reads[n + 1]).unwrap(); 683 | self.next_args.push(term); 684 | } 685 | } 686 | 687 | mailbox.recv_finish(proc); 688 | 689 | self.val_call(proc, fun, reads[0]) 690 | } 691 | OpKind::BinaryPush { 692 | specifier: BinaryEntrySpecifier::Bytes { .. }, 693 | } => { 694 | assert!(reads.len() == 4); 695 | let head = self.make_term(proc, fun, reads[2])?; 696 | let tail = self.make_term(proc, fun, reads[3])?; 697 | 698 | let mut head_bin: Vec = head.try_into().unwrap(); 699 | let tail_bin: Vec = tail.try_into().unwrap(); 700 | head_bin.extend(tail_bin.iter()); 701 | 702 | self.next_args.push(proc.binary_from_bytes(&head_bin)?); 703 | self.val_call(proc, fun, reads[0]) 704 | } 705 | OpKind::Unreachable => { 706 | println!("==== Reached OpKind::Unreachable! ===="); 707 | println!("Fun: {} Block: {}", fun.fun.ident(), block); 708 | unreachable!(); 709 | } 710 | kind => unimplemented!("{:?}", kind), 711 | } 712 | } 713 | } 714 | -------------------------------------------------------------------------------- /examples/simple_server/proc_lib.erl: -------------------------------------------------------------------------------- 1 | -file("proc_lib.erl", 1). 2 | 3 | -module(proc_lib). 4 | 5 | -export([spawn/1, 6 | spawn_link/1, 7 | spawn/2, 8 | spawn_link/2, 9 | spawn/3, 10 | spawn_link/3, 11 | spawn/4, 12 | spawn_link/4, 13 | spawn_opt/2, 14 | spawn_opt/3, 15 | spawn_opt/4, 16 | spawn_opt/5, 17 | start/3, 18 | start/4, 19 | start/5, 20 | start_link/3, 21 | start_link/4, 22 | start_link/5, 23 | hibernate/3, 24 | init_ack/1, 25 | init_ack/2, 26 | init_p/3, 27 | init_p/5, 28 | format/1, 29 | format/2, 30 | format/3, 31 | report_cb/2, 32 | initial_call/1, 33 | translate_initial_call/1, 34 | stop/1, 35 | stop/3]). 36 | 37 | -export([wake_up/3]). 38 | 39 | -export_type([spawn_option/0]). 40 | 41 | -file("/home/hansihe/.asdf/plugins/erlang/kerl-home/builds/asdf_21.2.4/" 42 | "otp_src_21.2.4/lib/stdlib/src/../../kernel/include/logger.hrl", 43 | 1). 44 | 45 | -file("proc_lib.erl", 44). 46 | 47 | -type priority_level() :: high | low | max | normal. 48 | 49 | -type max_heap_size() :: 50 | non_neg_integer() | 51 | #{size => non_neg_integer(), 52 | kill => true, 53 | error_logger => true}. 54 | 55 | -type spawn_option() :: 56 | link | 57 | monitor | 58 | {priority, priority_level()} | 59 | {max_heap_size, max_heap_size()} | 60 | {min_heap_size, non_neg_integer()} | 61 | {min_bin_vheap_size, non_neg_integer()} | 62 | {fullsweep_after, non_neg_integer()} | 63 | {message_queue_data, off_heap | on_heap | mixed}. 64 | 65 | -type dict_or_pid() :: 66 | pid() | 67 | (ProcInfo :: [_]) | 68 | {X :: integer(), Y :: integer(), Z :: integer()}. 69 | 70 | -spec spawn(Fun) -> pid() when Fun :: function(). 71 | 72 | spawn(F) when is_function(F) -> 73 | Parent = get_my_name(), 74 | Ancestors = get_ancestors(), 75 | erlang:spawn(proc_lib, init_p, [Parent,Ancestors,F]). 76 | 77 | -spec spawn(Module, Function, Args) -> pid() 78 | when 79 | Module :: module(), 80 | Function :: atom(), 81 | Args :: [term()]. 82 | 83 | spawn(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> 84 | Parent = get_my_name(), 85 | Ancestors = get_ancestors(), 86 | erlang:spawn(proc_lib, init_p, [Parent,Ancestors,M,F,A]). 87 | 88 | -spec spawn_link(Fun) -> pid() when Fun :: function(). 89 | 90 | spawn_link(F) when is_function(F) -> 91 | Parent = get_my_name(), 92 | Ancestors = get_ancestors(), 93 | spawn_link(proc_lib, init_p, [Parent,Ancestors,F]). 94 | 95 | -spec spawn_link(Module, Function, Args) -> pid() 96 | when 97 | Module :: module(), 98 | Function :: atom(), 99 | Args :: [term()]. 100 | 101 | spawn_link(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> 102 | Parent = get_my_name(), 103 | Ancestors = get_ancestors(), 104 | erlang:spawn_link(proc_lib, init_p, [Parent,Ancestors,M,F,A]). 105 | 106 | -spec spawn(Node, Fun) -> pid() when Node :: node(), Fun :: function(). 107 | 108 | spawn(Node, F) when is_function(F) -> 109 | Parent = get_my_name(), 110 | Ancestors = get_ancestors(), 111 | erlang:spawn(Node, proc_lib, init_p, [Parent,Ancestors,F]). 112 | 113 | -spec spawn(Node, Module, Function, Args) -> pid() 114 | when 115 | Node :: node(), 116 | Module :: module(), 117 | Function :: atom(), 118 | Args :: [term()]. 119 | 120 | spawn(Node, M, F, A) when is_atom(M), is_atom(F), is_list(A) -> 121 | Parent = get_my_name(), 122 | Ancestors = get_ancestors(), 123 | erlang:spawn(Node, proc_lib, init_p, [Parent,Ancestors,M,F,A]). 124 | 125 | -spec spawn_link(Node, Fun) -> pid() 126 | when Node :: node(), Fun :: function(). 127 | 128 | spawn_link(Node, F) when is_function(F) -> 129 | Parent = get_my_name(), 130 | Ancestors = get_ancestors(), 131 | erlang:spawn_link(Node, proc_lib, init_p, [Parent,Ancestors,F]). 132 | 133 | -spec spawn_link(Node, Module, Function, Args) -> pid() 134 | when 135 | Node :: node(), 136 | Module :: module(), 137 | Function :: atom(), 138 | Args :: [term()]. 139 | 140 | spawn_link(Node, M, F, A) when is_atom(M), is_atom(F), is_list(A) -> 141 | Parent = get_my_name(), 142 | Ancestors = get_ancestors(), 143 | erlang:spawn_link(Node, proc_lib, init_p, [Parent,Ancestors,M,F,A]). 144 | 145 | -spec spawn_opt(Fun, SpawnOpts) -> pid() 146 | when Fun :: function(), SpawnOpts :: [spawn_option()]. 147 | 148 | spawn_opt(F, Opts) when is_function(F) -> 149 | Parent = get_my_name(), 150 | Ancestors = get_ancestors(), 151 | check_for_monitor(Opts), 152 | erlang:spawn_opt(proc_lib, init_p, [Parent,Ancestors,F], Opts). 153 | 154 | -spec spawn_opt(Node, Function, SpawnOpts) -> pid() 155 | when 156 | Node :: node(), 157 | Function :: function(), 158 | SpawnOpts :: [spawn_option()]. 159 | 160 | spawn_opt(Node, F, Opts) when is_function(F) -> 161 | Parent = get_my_name(), 162 | Ancestors = get_ancestors(), 163 | check_for_monitor(Opts), 164 | erlang:spawn_opt(Node, proc_lib, init_p, [Parent,Ancestors,F], Opts). 165 | 166 | -spec spawn_opt(Module, Function, Args, SpawnOpts) -> pid() 167 | when 168 | Module :: module(), 169 | Function :: atom(), 170 | Args :: [term()], 171 | SpawnOpts :: [spawn_option()]. 172 | 173 | spawn_opt(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) -> 174 | Parent = get_my_name(), 175 | Ancestors = get_ancestors(), 176 | check_for_monitor(Opts), 177 | erlang:spawn_opt(proc_lib, init_p, [Parent,Ancestors,M,F,A], Opts). 178 | 179 | -spec spawn_opt(Node, Module, Function, Args, SpawnOpts) -> pid() 180 | when 181 | Node :: node(), 182 | Module :: module(), 183 | Function :: atom(), 184 | Args :: [term()], 185 | SpawnOpts :: [spawn_option()]. 186 | 187 | spawn_opt(Node, M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) -> 188 | Parent = get_my_name(), 189 | Ancestors = get_ancestors(), 190 | check_for_monitor(Opts), 191 | erlang:spawn_opt(Node, proc_lib, init_p, [Parent,Ancestors,M,F,A], Opts). 192 | 193 | check_for_monitor(SpawnOpts) -> 194 | case lists:member(monitor, SpawnOpts) of 195 | true -> 196 | error(badarg); 197 | false -> 198 | false 199 | end. 200 | 201 | spawn_mon(M, F, A) -> 202 | Parent = get_my_name(), 203 | Ancestors = get_ancestors(), 204 | spawn_monitor(proc_lib, init_p, [Parent,Ancestors,M,F,A]). 205 | 206 | spawn_opt_mon(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) -> 207 | Parent = get_my_name(), 208 | Ancestors = get_ancestors(), 209 | check_for_monitor(Opts), 210 | spawn_opt(proc_lib, 211 | init_p, 212 | [Parent,Ancestors,M,F,A], 213 | [monitor|Opts]). 214 | 215 | -spec hibernate(Module, Function, Args) -> no_return() 216 | when 217 | Module :: module(), 218 | Function :: atom(), 219 | Args :: [term()]. 220 | 221 | hibernate(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> 222 | erlang:hibernate(proc_lib, wake_up, [M,F,A]). 223 | 224 | ensure_link(SpawnOpts) -> 225 | case lists:member(link, SpawnOpts) of 226 | true -> 227 | SpawnOpts; 228 | false -> 229 | [link|SpawnOpts] 230 | end. 231 | 232 | -spec init_p(pid(), [pid()], function()) -> term(). 233 | 234 | init_p(Parent, Ancestors, Fun) when is_function(Fun) -> 235 | put('$ancestors', [Parent|Ancestors]), 236 | Mfa = erlang:fun_info_mfa(Fun), 237 | put('$initial_call', Mfa), 238 | try 239 | Fun() 240 | catch 241 | Class:Reason:Stacktrace -> 242 | exit_p(Class, Reason, Stacktrace) 243 | end. 244 | 245 | -spec init_p(pid(), [pid()], atom(), atom(), [term()]) -> term(). 246 | 247 | init_p(Parent, Ancestors, M, F, A) 248 | when is_atom(M), is_atom(F), is_list(A) -> 249 | put('$ancestors', [Parent|Ancestors]), 250 | put('$initial_call', trans_init(M, F, A)), 251 | init_p_do_apply(M, F, A). 252 | 253 | init_p_do_apply(M, F, A) -> 254 | try 255 | apply(M, F, A) 256 | catch 257 | Class:Reason:Stacktrace -> 258 | exit_p(Class, Reason, Stacktrace) 259 | end. 260 | 261 | -spec wake_up(atom(), atom(), [term()]) -> term(). 262 | 263 | wake_up(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> 264 | try 265 | apply(M, F, A) 266 | catch 267 | Class:Reason:Stacktrace -> 268 | exit_p(Class, Reason, Stacktrace) 269 | end. 270 | 271 | exit_p(Class, Reason, Stacktrace) -> 272 | case get('$initial_call') of 273 | {M,F,A} when is_atom(M), is_atom(F), is_integer(A) -> 274 | MFA = {M,F,make_dummy_args(A, [])}, 275 | crash_report(Class, Reason, MFA, Stacktrace), 276 | erlang:raise(exit, 277 | exit_reason(Class, Reason, Stacktrace), 278 | Stacktrace); 279 | _ -> 280 | crash_report(Class, Reason, [], Stacktrace), 281 | erlang:raise(exit, 282 | exit_reason(Class, Reason, Stacktrace), 283 | Stacktrace) 284 | end. 285 | 286 | exit_reason(error, Reason, Stacktrace) -> 287 | {Reason,Stacktrace}; 288 | exit_reason(exit, Reason, _Stacktrace) -> 289 | Reason; 290 | exit_reason(throw, Reason, Stacktrace) -> 291 | {{nocatch,Reason},Stacktrace}. 292 | 293 | -spec start(Module, Function, Args) -> Ret 294 | when 295 | Module :: module(), 296 | Function :: atom(), 297 | Args :: [term()], 298 | Ret :: term() | {error, Reason :: term()}. 299 | 300 | start(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> 301 | start(M, F, A, infinity). 302 | 303 | -spec start(Module, Function, Args, Time) -> Ret 304 | when 305 | Module :: module(), 306 | Function :: atom(), 307 | Args :: [term()], 308 | Time :: timeout(), 309 | Ret :: term() | {error, Reason :: term()}. 310 | 311 | start(M, F, A, Timeout) when is_atom(M), is_atom(F), is_list(A) -> 312 | PidRef = spawn_mon(M, F, A), 313 | sync_wait_mon(PidRef, Timeout). 314 | 315 | -spec start(Module, Function, Args, Time, SpawnOpts) -> Ret 316 | when 317 | Module :: module(), 318 | Function :: atom(), 319 | Args :: [term()], 320 | Time :: timeout(), 321 | SpawnOpts :: [spawn_option()], 322 | Ret :: term() | {error, Reason :: term()}. 323 | 324 | start(M, F, A, Timeout, SpawnOpts) 325 | when is_atom(M), is_atom(F), is_list(A) -> 326 | PidRef = spawn_opt_mon(M, F, A, SpawnOpts), 327 | sync_wait_mon(PidRef, Timeout). 328 | 329 | -spec start_link(Module, Function, Args) -> Ret 330 | when 331 | Module :: module(), 332 | Function :: atom(), 333 | Args :: [term()], 334 | Ret :: term() | {error, Reason :: term()}. 335 | 336 | start_link(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> 337 | start_link(M, F, A, infinity). 338 | 339 | -spec start_link(Module, Function, Args, Time) -> Ret 340 | when 341 | Module :: module(), 342 | Function :: atom(), 343 | Args :: [term()], 344 | Time :: timeout(), 345 | Ret :: term() | {error, Reason :: term()}. 346 | 347 | start_link(M, F, A, Timeout) when is_atom(M), is_atom(F), is_list(A) -> 348 | Pid = proc_lib:spawn_link(M, F, A), 349 | sync_wait(Pid, Timeout). 350 | 351 | -spec start_link(Module, Function, Args, Time, SpawnOpts) -> Ret 352 | when 353 | Module :: module(), 354 | Function :: atom(), 355 | Args :: [term()], 356 | Time :: timeout(), 357 | SpawnOpts :: [spawn_option()], 358 | Ret :: term() | {error, Reason :: term()}. 359 | 360 | start_link(M, F, A, Timeout, SpawnOpts) 361 | when is_atom(M), is_atom(F), is_list(A) -> 362 | Pid = proc_lib:spawn_opt(M, F, A, ensure_link(SpawnOpts)), 363 | sync_wait(Pid, Timeout). 364 | 365 | sync_wait(Pid, Timeout) -> 366 | receive 367 | {ack,Pid,Return} -> 368 | Return; 369 | {'EXIT',Pid,Reason} -> 370 | {error,Reason} 371 | after 372 | Timeout -> 373 | unlink(Pid), 374 | exit(Pid, kill), 375 | flush(Pid), 376 | {error,timeout} 377 | end. 378 | 379 | sync_wait_mon({Pid,Ref}, Timeout) -> 380 | receive 381 | {ack,Pid,Return} -> 382 | demonitor(Ref, [flush]), 383 | Return; 384 | {'DOWN',Ref,_Type,Pid,Reason} -> 385 | {error,Reason}; 386 | {'EXIT',Pid,Reason} -> 387 | demonitor(Ref, [flush]), 388 | {error,Reason} 389 | after 390 | Timeout -> 391 | demonitor(Ref, [flush]), 392 | exit(Pid, kill), 393 | flush(Pid), 394 | {error,timeout} 395 | end. 396 | 397 | -spec flush(pid()) -> true. 398 | 399 | flush(Pid) -> 400 | receive 401 | {'EXIT',Pid,_} -> 402 | true 403 | after 404 | 0 -> true 405 | end. 406 | 407 | -spec init_ack(Parent, Ret) -> ok when Parent :: pid(), Ret :: term(). 408 | 409 | init_ack(Parent, Return) -> 410 | Parent ! {ack,self(),Return}, 411 | ok. 412 | 413 | -spec init_ack(Ret) -> ok when Ret :: term(). 414 | 415 | init_ack(Return) -> 416 | [Parent|_] = get('$ancestors'), 417 | init_ack(Parent, Return). 418 | 419 | -spec initial_call(Process) -> {Module, Function, Args} | false 420 | when 421 | Process :: dict_or_pid(), 422 | Module :: module(), 423 | Function :: atom(), 424 | Args :: [atom()]. 425 | 426 | initial_call(DictOrPid) -> 427 | case raw_initial_call(DictOrPid) of 428 | {M,F,A} -> 429 | {M,F,make_dummy_args(A, [])}; 430 | false -> 431 | false 432 | end. 433 | 434 | make_dummy_args(0, Acc) -> 435 | Acc; 436 | make_dummy_args(N, Acc) -> 437 | Arg = list_to_atom("Argument__" ++ integer_to_list(N)), 438 | make_dummy_args(N - 1, [Arg|Acc]). 439 | 440 | -spec translate_initial_call(Process) -> {Module, Function, Arity} 441 | when 442 | Process :: dict_or_pid(), 443 | Module :: module(), 444 | Function :: atom(), 445 | Arity :: byte(). 446 | 447 | translate_initial_call(DictOrPid) -> 448 | case raw_initial_call(DictOrPid) of 449 | {_,_,_} = MFA -> 450 | MFA; 451 | false -> 452 | {proc_lib,init_p,5} 453 | end. 454 | 455 | raw_initial_call({X,Y,Z}) 456 | when is_integer(X), is_integer(Y), is_integer(Z) -> 457 | raw_initial_call(c:pid(X, Y, Z)); 458 | raw_initial_call(Pid) when is_pid(Pid) -> 459 | case get_process_info(Pid, dictionary) of 460 | {dictionary,Dict} -> 461 | raw_init_call(Dict); 462 | _ -> 463 | false 464 | end; 465 | raw_initial_call(ProcInfo) when is_list(ProcInfo) -> 466 | case lists:keyfind(dictionary, 1, ProcInfo) of 467 | {dictionary,Dict} -> 468 | raw_init_call(Dict); 469 | _ -> 470 | false 471 | end. 472 | 473 | raw_init_call(Dict) -> 474 | case lists:keyfind('$initial_call', 1, Dict) of 475 | {_,{_,_,_} = MFA} -> 476 | MFA; 477 | _ -> 478 | false 479 | end. 480 | 481 | trans_init(gen, init_it, [gen_server,_,_,supervisor,{_,Module,_},_]) -> 482 | {supervisor,Module,1}; 483 | trans_init(gen, init_it, [gen_server,_,_,_,supervisor,{_,Module,_},_]) -> 484 | {supervisor,Module,1}; 485 | trans_init(gen, 486 | init_it, 487 | [gen_server,_,_,supervisor_bridge,[Module|_],_]) -> 488 | {supervisor_bridge,Module,1}; 489 | trans_init(gen, 490 | init_it, 491 | [gen_server,_,_,_,supervisor_bridge,[Module|_],_]) -> 492 | {supervisor_bridge,Module,1}; 493 | trans_init(gen, init_it, [gen_event|_]) -> 494 | {gen_event,init_it,6}; 495 | trans_init(gen, init_it, [_GenMod,_,_,Module,_,_]) when is_atom(Module) -> 496 | {Module,init,1}; 497 | trans_init(gen, init_it, [_GenMod,_,_,_,Module|_]) when is_atom(Module) -> 498 | {Module,init,1}; 499 | trans_init(M, F, A) when is_atom(M), is_atom(F) -> 500 | {M,F,length(A)}. 501 | 502 | crash_report(exit, normal, _, _) -> 503 | ok; 504 | crash_report(exit, shutdown, _, _) -> 505 | ok; 506 | crash_report(exit, {shutdown,_}, _, _) -> 507 | ok; 508 | crash_report(Class, Reason, StartF, Stacktrace) -> 509 | case logger:allow(error, proc_lib) of 510 | true -> 511 | apply(logger, 512 | macro_log, 513 | [#{mfa => {proc_lib,crash_report,4}, 514 | line => 508, 515 | file => "proc_lib.erl"}, 516 | error, 517 | #{label => {proc_lib,crash}, 518 | report => 519 | [my_info(Class, Reason, StartF, Stacktrace), 520 | linked_info(self())]}, 521 | #{domain => [otp,sasl], 522 | report_cb => fun proc_lib:report_cb/2, 523 | logger_formatter => #{title => "CRASH REPORT"}, 524 | error_logger => 525 | #{tag => error_report,type => crash_report}}]); 526 | false -> 527 | ok 528 | end. 529 | 530 | my_info(Class, Reason, [], Stacktrace) -> 531 | my_info_1(Class, Reason, Stacktrace); 532 | my_info(Class, Reason, StartF, Stacktrace) -> 533 | [{initial_call,StartF}|my_info_1(Class, Reason, Stacktrace)]. 534 | 535 | my_info_1(Class, Reason, Stacktrace) -> 536 | [{pid,self()}, 537 | get_process_info(self(), registered_name), 538 | {error_info,{Class,Reason,Stacktrace}}, 539 | get_ancestors(self()), 540 | get_process_info(self(), message_queue_len), 541 | get_messages(self()), 542 | get_process_info(self(), links), 543 | get_cleaned_dictionary(self()), 544 | get_process_info(self(), trap_exit), 545 | get_process_info(self(), status), 546 | get_process_info(self(), heap_size), 547 | get_process_info(self(), stack_size), 548 | get_process_info(self(), reductions)]. 549 | 550 | -spec get_ancestors(pid()) -> {ancestors, [pid()]}. 551 | 552 | get_ancestors(Pid) -> 553 | case get_dictionary(Pid, '$ancestors') of 554 | {'$ancestors',Ancestors} -> 555 | {ancestors,Ancestors}; 556 | _ -> 557 | {ancestors,[]} 558 | end. 559 | 560 | get_messages(Pid) -> 561 | Messages = get_process_messages(Pid), 562 | {messages,error_logger:limit_term(Messages)}. 563 | 564 | get_process_messages(Pid) -> 565 | Depth = error_logger:get_format_depth(), 566 | case 567 | Pid =/= self() 568 | orelse 569 | Depth =:= unlimited 570 | of 571 | true -> 572 | {messages,Messages} = get_process_info(Pid, messages), 573 | Messages; 574 | false -> 575 | receive_messages(Depth) 576 | end. 577 | 578 | receive_messages(0) -> 579 | []; 580 | receive_messages(N) -> 581 | receive 582 | M -> 583 | [M|receive_messages(N - 1)] 584 | after 585 | 0 -> [] 586 | end. 587 | 588 | get_cleaned_dictionary(Pid) -> 589 | case get_process_info(Pid, dictionary) of 590 | {dictionary,Dict} -> 591 | {dictionary,cleaned_dict(Dict)}; 592 | _ -> 593 | {dictionary,[]} 594 | end. 595 | 596 | cleaned_dict(Dict) -> 597 | CleanDict = clean_dict(Dict), 598 | error_logger:limit_term(CleanDict). 599 | 600 | clean_dict([{'$ancestors',_}|Dict]) -> 601 | clean_dict(Dict); 602 | clean_dict([{'$initial_call',_}|Dict]) -> 603 | clean_dict(Dict); 604 | clean_dict([E|Dict]) -> 605 | [E|clean_dict(Dict)]; 606 | clean_dict([]) -> 607 | []. 608 | 609 | get_dictionary(Pid, Tag) -> 610 | case get_process_info(Pid, dictionary) of 611 | {dictionary,Dict} -> 612 | case lists:keysearch(Tag, 1, Dict) of 613 | {value,Value} -> 614 | Value; 615 | _ -> 616 | undefined 617 | end; 618 | _ -> 619 | undefined 620 | end. 621 | 622 | linked_info(Pid) -> 623 | make_neighbour_reports1(neighbours(Pid)). 624 | 625 | make_neighbour_reports1([P|Ps]) -> 626 | ReportBody = make_neighbour_report(P), 627 | case lists:member(undefined, ReportBody) of 628 | true -> 629 | make_neighbour_reports1(Ps); 630 | false -> 631 | [{neighbour,ReportBody}|make_neighbour_reports1(Ps)] 632 | end; 633 | make_neighbour_reports1([]) -> 634 | []. 635 | 636 | make_neighbour_report(Pid) -> 637 | [{pid,Pid}, 638 | get_process_info(Pid, registered_name), 639 | get_initial_call(Pid), 640 | get_process_info(Pid, current_function), 641 | get_ancestors(Pid), 642 | get_process_info(Pid, message_queue_len), 643 | get_process_info(Pid, links), 644 | get_process_info(Pid, trap_exit), 645 | get_process_info(Pid, status), 646 | get_process_info(Pid, heap_size), 647 | get_process_info(Pid, stack_size), 648 | get_process_info(Pid, reductions), 649 | get_process_info(Pid, current_stacktrace)]. 650 | 651 | get_initial_call(Pid) -> 652 | case get_dictionary(Pid, '$initial_call') of 653 | {'$initial_call',{M,F,A}} -> 654 | {initial_call,{M,F,make_dummy_args(A, [])}}; 655 | _ -> 656 | get_process_info(Pid, initial_call) 657 | end. 658 | 659 | -spec neighbours(pid()) -> [pid()]. 660 | 661 | neighbours(Pid) -> 662 | {_,Visited} = visit(adjacents(Pid), {max_neighbours(),[Pid]}), 663 | lists:delete(Pid, Visited). 664 | 665 | max_neighbours() -> 666 | 15. 667 | 668 | visit([P|Ps], {N,Vs} = NVs) when N > 0 -> 669 | case lists:member(P, Vs) of 670 | false -> 671 | visit(adjacents(P), visit(Ps, {N - 1,[P|Vs]})); 672 | true -> 673 | visit(Ps, NVs) 674 | end; 675 | visit(_, {_N,_Vs} = NVs) -> 676 | NVs. 677 | 678 | -spec adjacents(pid()) -> [pid()]. 679 | 680 | adjacents(Pid) -> 681 | case catch proc_info(Pid, links) of 682 | {links,Links} -> 683 | no_trap(Links); 684 | _ -> 685 | [] 686 | end. 687 | 688 | no_trap([P|Ps]) -> 689 | case catch proc_info(P, trap_exit) of 690 | {trap_exit,false} -> 691 | [P|no_trap(Ps)]; 692 | _ -> 693 | no_trap(Ps) 694 | end; 695 | no_trap([]) -> 696 | []. 697 | 698 | get_process_info(Pid, Tag) -> 699 | translate_process_info(Tag, catch proc_info(Pid, Tag)). 700 | 701 | translate_process_info(registered_name, []) -> 702 | {registered_name,[]}; 703 | translate_process_info(_, {'EXIT',_}) -> 704 | undefined; 705 | translate_process_info(_, Result) -> 706 | Result. 707 | 708 | get_my_name() -> 709 | case proc_info(self(), registered_name) of 710 | {registered_name,Name} -> 711 | Name; 712 | _ -> 713 | self() 714 | end. 715 | 716 | -spec get_ancestors() -> [pid()]. 717 | 718 | get_ancestors() -> 719 | case get('$ancestors') of 720 | A when is_list(A) -> 721 | A; 722 | _ -> 723 | [] 724 | end. 725 | 726 | proc_info(Pid, Item) when node(Pid) =:= node() -> 727 | process_info(Pid, Item); 728 | proc_info(Pid, Item) -> 729 | case lists:member(node(Pid), nodes()) of 730 | true -> 731 | check(rpc:call(node(Pid), erlang, process_info, [Pid,Item])); 732 | _ -> 733 | hidden 734 | end. 735 | 736 | check({badrpc,nodedown}) -> 737 | undefined; 738 | check({badrpc,Error}) -> 739 | Error; 740 | check(Res) -> 741 | Res. 742 | 743 | -spec report_cb(CrashReport, FormatOpts) -> unicode:chardata() 744 | when 745 | CrashReport :: 746 | #{label => {proc_lib, crash}, 747 | report => [term()]}, 748 | FormatOpts :: logger:report_cb_config(). 749 | 750 | report_cb(#{label := {proc_lib,crash},report := CrashReport}, Extra) -> 751 | Default = 752 | #{chars_limit => unlimited, 753 | depth => unlimited, 754 | single_line => false, 755 | encoding => utf8}, 756 | do_format(CrashReport, maps:merge(Default, Extra)). 757 | 758 | -spec format(CrashReport) -> string() when CrashReport :: [term()]. 759 | 760 | format(CrashReport) -> 761 | format(CrashReport, latin1). 762 | 763 | -spec format(CrashReport, Encoding) -> string() 764 | when 765 | CrashReport :: [term()], 766 | Encoding :: latin1 | unicode | utf8. 767 | 768 | format(CrashReport, Encoding) -> 769 | format(CrashReport, Encoding, unlimited). 770 | 771 | -spec format(CrashReport, Encoding, Depth) -> string() 772 | when 773 | CrashReport :: [term()], 774 | Encoding :: latin1 | unicode | utf8, 775 | Depth :: unlimited | pos_integer(). 776 | 777 | format(CrashReport, Encoding, Depth) -> 778 | do_format(CrashReport, 779 | #{chars_limit => unlimited, 780 | depth => Depth, 781 | encoding => Encoding, 782 | single_line => false}). 783 | 784 | do_format([OwnReport,LinkReport], #{single_line := Single} = Extra) -> 785 | Indent = 786 | if 787 | Single -> 788 | ""; 789 | true -> 790 | " " 791 | end, 792 | MyIndent = Indent ++ Indent, 793 | Sep = nl(Single, "; "), 794 | OwnFormat = format_report(OwnReport, MyIndent, Extra), 795 | LinkFormat = 796 | lists:join(Sep, format_link_report(LinkReport, MyIndent, Extra)), 797 | Nl = nl(Single, " "), 798 | Str = 799 | io_lib:format("~scrasher:" 800 | ++ 801 | Nl 802 | ++ 803 | "~ts" ++ Sep ++ "~sneighbours:" ++ Nl ++ "~ts", 804 | [Indent,OwnFormat,Indent,LinkFormat]), 805 | lists:flatten(Str). 806 | 807 | format_link_report([Link|Reps], 808 | Indent0, 809 | #{single_line := Single} = Extra) -> 810 | Rep = 811 | case Link of 812 | {neighbour,Rep0} -> 813 | Rep0; 814 | _ -> 815 | Link 816 | end, 817 | Indent = 818 | if 819 | Single -> 820 | ""; 821 | true -> 822 | Indent0 823 | end, 824 | LinkIndent = [" ",Indent], 825 | [[Indent, 826 | "neighbour:", 827 | nl(Single, " "), 828 | format_report(Rep, LinkIndent, Extra)]| 829 | format_link_report(Reps, Indent, Extra)]; 830 | format_link_report(Rep, Indent, Extra) -> 831 | format_report(Rep, Indent, Extra). 832 | 833 | format_report(Rep, Indent, #{single_line := Single} = Extra) 834 | when is_list(Rep) -> 835 | lists:join(nl(Single, ", "), format_rep(Rep, Indent, Extra)); 836 | format_report(Rep, 837 | Indent0, 838 | #{encoding := Enc, 839 | depth := Depth, 840 | chars_limit := Limit, 841 | single_line := Single}) -> 842 | {P,Tl} = p(Enc, Depth), 843 | {Indent,Width} = 844 | if 845 | Single -> 846 | {"","0"}; 847 | true -> 848 | {Indent0,""} 849 | end, 850 | Opts = 851 | if 852 | is_integer(Limit) -> 853 | [{chars_limit,Limit}]; 854 | true -> 855 | [] 856 | end, 857 | io_lib:format("~s~" ++ Width ++ P, [Indent,Rep|Tl], Opts). 858 | 859 | format_rep([{initial_call,InitialCall}|Rep], Indent, Extra) -> 860 | [format_mfa(Indent, InitialCall, Extra)| 861 | format_rep(Rep, Indent, Extra)]; 862 | format_rep([{error_info,{Class,Reason,StackTrace}}|Rep], Indent, Extra) -> 863 | [format_exception(Class, Reason, StackTrace, Extra)| 864 | format_rep(Rep, Indent, Extra)]; 865 | format_rep([{Tag,Data}|Rep], Indent, Extra) -> 866 | [format_tag(Indent, Tag, Data, Extra)| 867 | format_rep(Rep, Indent, Extra)]; 868 | format_rep(_, _, _Extra) -> 869 | []. 870 | 871 | format_exception(Class, 872 | Reason, 873 | StackTrace, 874 | #{encoding := Enc, 875 | depth := Depth, 876 | chars_limit := Limit, 877 | single_line := Single} = 878 | Extra) -> 879 | PF = pp_fun(Extra), 880 | StackFun = 881 | fun(M, _F, _A) -> 882 | (M =:= erl_eval) or (M =:= proc_lib) 883 | end, 884 | if 885 | Single -> 886 | {P,Tl} = p(Enc, Depth), 887 | Opts = 888 | if 889 | is_integer(Limit) -> 890 | [{chars_limit,Limit}]; 891 | true -> 892 | [] 893 | end, 894 | [atom_to_list(Class), 895 | ": ", 896 | io_lib:format("~0" ++ P, [{Reason,StackTrace}|Tl], Opts)]; 897 | true -> 898 | EI = " ", 899 | [EI, 900 | erl_error:format_exception(1 + length(EI), 901 | Class, 902 | Reason, 903 | StackTrace, 904 | StackFun, 905 | PF, 906 | Enc)] 907 | end. 908 | 909 | format_mfa(Indent0, 910 | {M,F,Args} = StartF, 911 | #{encoding := Enc,single_line := Single} = Extra) -> 912 | Indent = 913 | if 914 | Single -> 915 | ""; 916 | true -> 917 | Indent0 918 | end, 919 | try 920 | A = length(Args), 921 | [Indent, 922 | "initial call: ", 923 | atom_to_list(M), 924 | $:, 925 | to_string(F, Enc), 926 | $/, 927 | integer_to_list(A)] 928 | catch 929 | error:_ -> 930 | format_tag(Indent, initial_call, StartF, Extra) 931 | end. 932 | 933 | to_string(A, latin1) -> 934 | io_lib:write_atom_as_latin1(A); 935 | to_string(A, _) -> 936 | io_lib:write_atom(A). 937 | 938 | pp_fun(#{encoding := Enc, 939 | depth := Depth, 940 | chars_limit := Limit, 941 | single_line := Single}) -> 942 | {P,Tl} = p(Enc, Depth), 943 | Width = 944 | if 945 | Single -> 946 | "0"; 947 | true -> 948 | "" 949 | end, 950 | Opts = 951 | if 952 | is_integer(Limit) -> 953 | [{chars_limit,Limit}]; 954 | true -> 955 | [] 956 | end, 957 | fun(Term, I) -> 958 | io_lib:format("~" ++ Width ++ "." ++ integer_to_list(I) ++ P, 959 | [Term|Tl], 960 | Opts) 961 | end. 962 | 963 | format_tag(Indent0, 964 | Tag, 965 | Data, 966 | #{encoding := Enc, 967 | depth := Depth, 968 | chars_limit := Limit, 969 | single_line := Single}) -> 970 | {P,Tl} = p(Enc, Depth), 971 | {Indent,Width} = 972 | if 973 | Single -> 974 | {"","0"}; 975 | true -> 976 | {Indent0,""} 977 | end, 978 | Opts = 979 | if 980 | is_integer(Limit) -> 981 | [{chars_limit,Limit}]; 982 | true -> 983 | [] 984 | end, 985 | io_lib:format("~s~" ++ Width ++ "p: ~" ++ Width ++ ".18" ++ P, 986 | [Indent,Tag,Data|Tl], 987 | Opts). 988 | 989 | p(Encoding, Depth) -> 990 | {Letter,Tl} = 991 | case Depth of 992 | unlimited -> 993 | {"p",[]}; 994 | _ -> 995 | {"P",[Depth]} 996 | end, 997 | P = modifier(Encoding) ++ Letter, 998 | {P,Tl}. 999 | 1000 | modifier(latin1) -> 1001 | ""; 1002 | modifier(_) -> 1003 | "t". 1004 | 1005 | nl(true, Else) -> 1006 | Else; 1007 | nl(false, _) -> 1008 | "\n". 1009 | 1010 | -spec stop(Process) -> ok 1011 | when 1012 | Process :: pid() | RegName | {RegName, node()}, 1013 | RegName :: atom(). 1014 | 1015 | stop(Process) -> 1016 | stop(Process, normal, infinity). 1017 | 1018 | -spec stop(Process, Reason, Timeout) -> ok 1019 | when 1020 | Process :: pid() | RegName | {RegName, node()}, 1021 | RegName :: atom(), 1022 | Reason :: term(), 1023 | Timeout :: timeout(). 1024 | 1025 | stop(Process, Reason, Timeout) -> 1026 | {Pid,Mref} = spawn_monitor(do_stop(Process, Reason)), 1027 | receive 1028 | {'DOWN',Mref,_,_,Reason} -> 1029 | ok; 1030 | {'DOWN',Mref,_,_,{noproc,{sys,terminate,_}}} -> 1031 | exit(noproc); 1032 | {'DOWN',Mref,_,_,CrashReason} -> 1033 | exit(CrashReason) 1034 | after 1035 | Timeout -> 1036 | exit(Pid, kill), 1037 | receive 1038 | {'DOWN',Mref,_,_,_} -> 1039 | exit(timeout) 1040 | end 1041 | end. 1042 | 1043 | -spec do_stop(Process, Reason) -> Fun 1044 | when 1045 | Process :: pid() | RegName | {RegName, node()}, 1046 | RegName :: atom(), 1047 | Reason :: term(), 1048 | Fun :: fun(() -> no_return()). 1049 | 1050 | do_stop(Process, Reason) -> 1051 | fun() -> 1052 | Mref = monitor(process, Process), 1053 | ok = sys:terminate(Process, Reason, infinity), 1054 | receive 1055 | {'DOWN',Mref,_,_,ExitReason} -> 1056 | exit(ExitReason) 1057 | end 1058 | end. 1059 | 1060 | 1061 | 1062 | -------------------------------------------------------------------------------- /examples/simple_server/Elixir.GenServer.erl: -------------------------------------------------------------------------------- 1 | -file("/home/build/elixir/lib/elixir/lib/gen_server.ex", 1). 2 | 3 | -module('Elixir.GenServer'). 4 | 5 | -callback terminate(reason, state :: term()) -> term() 6 | when 7 | reason :: 8 | normal | shutdown | {shutdown, term()}. 9 | 10 | -optional_callbacks([terminate/2]). 11 | 12 | -callback init(init_arg :: term()) -> 13 | {ok, state} | 14 | {ok, 15 | state, 16 | timeout() | hibernate | {continue, term()}} | 17 | ignore | 18 | {stop, reason :: any()} 19 | when state :: any(). 20 | 21 | -callback handle_info(msg :: timeout | term(), state :: term()) -> 22 | {noreply, new_state} | 23 | {noreply, 24 | new_state, 25 | timeout() | hibernate | {continue, term()}} | 26 | {stop, reason :: term(), new_state} 27 | when new_state :: term(). 28 | 29 | -optional_callbacks([handle_info/2]). 30 | 31 | -callback handle_continue(continue :: term(), state :: term()) -> 32 | {noreply, new_state} | 33 | {noreply, 34 | new_state, 35 | timeout() | hibernate | {continue, term()}} | 36 | {stop, reason :: term(), new_state} 37 | when new_state :: term(). 38 | 39 | -optional_callbacks([handle_continue/2]). 40 | 41 | -callback handle_cast(request :: term(), state :: term()) -> 42 | {noreply, new_state} | 43 | {noreply, 44 | new_state, 45 | timeout() | hibernate | {continue, term()}} | 46 | {stop, reason :: term(), new_state} 47 | when new_state :: term(). 48 | 49 | -optional_callbacks([handle_cast/2]). 50 | 51 | -callback handle_call(request :: term(), from(), state :: term()) -> 52 | {reply, reply, new_state} | 53 | {reply, 54 | reply, 55 | new_state, 56 | timeout() | hibernate | {continue, term()}} | 57 | {noreply, new_state} | 58 | {noreply, 59 | new_state, 60 | timeout() | hibernate | {continue, term()}} | 61 | {stop, reason, reply, new_state} | 62 | {stop, reason, new_state} 63 | when 64 | reply :: term(), 65 | new_state :: term(), 66 | reason :: term(). 67 | 68 | -optional_callbacks([handle_call/3]). 69 | 70 | -callback format_status(reason, pdict_and_state :: list()) -> term() 71 | when reason :: normal | terminate. 72 | 73 | -optional_callbacks([format_status/2]). 74 | 75 | -callback code_change(old_vsn, state :: term(), extra :: term()) -> 76 | {ok, new_state :: term()} | 77 | {error, reason :: term()} | 78 | {down, term()} 79 | when old_vsn :: term(). 80 | 81 | -optional_callbacks([code_change/3]). 82 | 83 | -spec whereis(server()) -> pid() | {atom(), node()} | nil. 84 | 85 | -spec stop(server(), reason :: term(), timeout()) -> ok. 86 | 87 | -spec start_link(module(), any(), options()) -> on_start(). 88 | 89 | -spec start(module(), any(), options()) -> on_start(). 90 | 91 | -spec reply(from(), term()) -> ok. 92 | 93 | -spec multi_call([node()], name :: atom(), term(), timeout()) -> 94 | {replies :: [{node(), term()}], 95 | bad_nodes :: [node()]}. 96 | 97 | -spec cast(server(), term()) -> ok. 98 | 99 | -spec call(server(), term(), timeout()) -> term(). 100 | 101 | -spec abcast([node()], name :: atom(), term()) -> abcast. 102 | 103 | -export_type([from/0]). 104 | 105 | -type from() :: {pid(), tag :: term()}. 106 | 107 | -export_type([server/0]). 108 | 109 | -type server() :: pid() | name() | {atom(), node()}. 110 | 111 | -export_type([debug/0]). 112 | 113 | -type debug() :: 114 | [trace | log | statistics | {log_to_file, 'Elixir.Path':t()}]. 115 | 116 | -export_type([option/0]). 117 | 118 | -type option() :: 119 | {debug, debug()} | 120 | {name, name()} | 121 | {timeout, timeout()} | 122 | {spawn_opt, 'Elixir.Process':spawn_opt()} | 123 | {hibernate_after, timeout()}. 124 | 125 | -export_type([options/0]). 126 | 127 | -type options() :: [option()]. 128 | 129 | -export_type([name/0]). 130 | 131 | -type name() :: atom() | {global, term()} | {via, module(), term()}. 132 | 133 | -export_type([on_start/0]). 134 | 135 | -type on_start() :: 136 | {ok, pid()} | 137 | ignore | 138 | {error, {already_started, pid()} | term()}. 139 | 140 | -export(['MACRO-__before_compile__'/2, 141 | 'MACRO-__using__'/2, 142 | '__info__'/1, 143 | abcast/2, 144 | abcast/3, 145 | call/2, 146 | call/3, 147 | cast/2, 148 | multi_call/2, 149 | multi_call/3, 150 | multi_call/4, 151 | reply/2, 152 | start/2, 153 | start/3, 154 | start_link/2, 155 | start_link/3, 156 | stop/1, 157 | stop/2, 158 | stop/3, 159 | whereis/1]). 160 | 161 | -spec '__info__'(attributes | 162 | compile | 163 | functions | 164 | macros | 165 | md5 | 166 | module | 167 | deprecated) -> 168 | any(). 169 | 170 | '__info__'(module) -> 171 | 'Elixir.GenServer'; 172 | '__info__'(functions) -> 173 | [{abcast,2}, 174 | {abcast,3}, 175 | {call,2}, 176 | {call,3}, 177 | {cast,2}, 178 | {multi_call,2}, 179 | {multi_call,3}, 180 | {multi_call,4}, 181 | {reply,2}, 182 | {start,2}, 183 | {start,3}, 184 | {start_link,2}, 185 | {start_link,3}, 186 | {stop,1}, 187 | {stop,2}, 188 | {stop,3}, 189 | {whereis,1}]; 190 | '__info__'(macros) -> 191 | [{'__before_compile__',1},{'__using__',1}]; 192 | '__info__'(Key = attributes) -> 193 | erlang:get_module_info('Elixir.GenServer', Key); 194 | '__info__'(Key = compile) -> 195 | erlang:get_module_info('Elixir.GenServer', Key); 196 | '__info__'(Key = md5) -> 197 | erlang:get_module_info('Elixir.GenServer', Key); 198 | '__info__'(deprecated) -> 199 | []. 200 | 201 | 'MACRO-__before_compile__'(_@CALLER, _env@1) -> 202 | case 203 | 'Elixir.Module':'defines?'(case _env@1 of 204 | #{module := __@1} -> 205 | __@1; 206 | __@1 when is_map(__@1) -> 207 | error({badkey,module,__@1}); 208 | __@1 -> 209 | __@1:module() 210 | end, 211 | {init,1}) 212 | of 213 | __@2 214 | when 215 | __@2 =:= nil 216 | orelse 217 | __@2 =:= false -> 218 | _message@1 = 219 | <<"function init/1 required by behaviour GenServer is n" 220 | "ot implemented (in module ", 221 | ('Elixir.Kernel':inspect(case _env@1 of 222 | #{module := __@3} -> 223 | __@3; 224 | __@3 when is_map(__@3) -> 225 | error({badkey, 226 | module, 227 | __@3}); 228 | __@3 -> 229 | __@3:module() 230 | end))/binary, 231 | ").\n\nWe will inject a default implementation for no" 232 | "w:\n\n def init(init_arg) do\n {:ok, init_ar" 233 | "g}\n end\n\nYou can copy the implementation above" 234 | " or define your own that converts the arguments give" 235 | "n to GenServer.start_link/3 to the server state.\n">>, 236 | elixir_errors:warn(case _env@1 of 237 | #{line := __@4} -> 238 | __@4; 239 | __@4 when is_map(__@4) -> 240 | error({badkey,line,__@4}); 241 | __@4 -> 242 | __@4:line() 243 | end, 244 | case _env@1 of 245 | #{file := __@5} -> 246 | __@5; 247 | __@5 when is_map(__@5) -> 248 | error({badkey,file,__@5}); 249 | __@5 -> 250 | __@5:file() 251 | end, 252 | _message@1), 253 | {'__block__', 254 | [], 255 | [{'@', 256 | [{context,'Elixir.GenServer'},{import,'Elixir.Kernel'}], 257 | [{doc,[{context,'Elixir.GenServer'}],[false]}]}, 258 | {def, 259 | [{context,'Elixir.GenServer'},{import,'Elixir.Kernel'}], 260 | [{init, 261 | [{context,'Elixir.GenServer'}], 262 | [{init_arg,[],'Elixir.GenServer'}]}, 263 | [{do,{ok,{init_arg,[],'Elixir.GenServer'}}}]]}, 264 | {defoverridable, 265 | [{context,'Elixir.GenServer'},{import,'Elixir.Kernel'}], 266 | [[{init,1}]]}]}; 267 | _ -> 268 | nil 269 | end. 270 | 271 | 'MACRO-__using__'(_@CALLER, _opts@1) -> 272 | {'__block__', 273 | [], 274 | [{'=',[],[{opts,[],'Elixir.GenServer'},_opts@1]}, 275 | {'__block__', 276 | [{keep,{<<"lib/gen_server.ex">>,0}}], 277 | [{'@', 278 | [{keep,{<<"lib/gen_server.ex">>,719}}, 279 | {context,'Elixir.GenServer'}, 280 | {import,'Elixir.Kernel'}], 281 | [{behaviour, 282 | [{keep,{<<"lib/gen_server.ex">>,719}}, 283 | {context,'Elixir.GenServer'}], 284 | [{'__aliases__', 285 | [{keep,{<<"lib/gen_server.ex">>,719}},{alias,false}], 286 | ['GenServer']}]}]}, 287 | {'if', 288 | [{keep,{<<"lib/gen_server.ex">>,721}}, 289 | {context,'Elixir.GenServer'}, 290 | {import,'Elixir.Kernel'}], 291 | [{'==', 292 | [{keep,{<<"lib/gen_server.ex">>,721}}, 293 | {context,'Elixir.GenServer'}, 294 | {import,'Elixir.Kernel'}], 295 | [{{'.', 296 | [{keep,{<<"lib/gen_server.ex">>,721}}], 297 | [{'__aliases__', 298 | [{keep,{<<"lib/gen_server.ex">>,721}},{alias,false}], 299 | ['Module']}, 300 | get_attribute]}, 301 | [{keep,{<<"lib/gen_server.ex">>,721}}], 302 | [{'__MODULE__', 303 | [{keep,{<<"lib/gen_server.ex">>,721}}], 304 | 'Elixir.GenServer'}, 305 | doc]}, 306 | nil]}, 307 | [{do, 308 | {'@', 309 | [{keep,{<<"lib/gen_server.ex">>,722}}, 310 | {context,'Elixir.GenServer'}, 311 | {import,'Elixir.Kernel'}], 312 | [{doc, 313 | [{keep,{<<"lib/gen_server.ex">>,722}}, 314 | {context,'Elixir.GenServer'}], 315 | [<<"Returns a specification to start this module under a" 316 | " supervisor.\n\nSee `Supervisor`.\n">>]}]}}]]}, 317 | {def, 318 | [{keep,{<<"lib/gen_server.ex">>,729}}, 319 | {context,'Elixir.GenServer'}, 320 | {import,'Elixir.Kernel'}], 321 | [{child_spec, 322 | [{keep,{<<"lib/gen_server.ex">>,729}}, 323 | {context,'Elixir.GenServer'}], 324 | [{init_arg, 325 | [{keep,{<<"lib/gen_server.ex">>,729}}], 326 | 'Elixir.GenServer'}]}, 327 | [{do, 328 | {'__block__', 329 | [{keep,{<<"lib/gen_server.ex">>,0}}], 330 | [{'=', 331 | [{keep,{<<"lib/gen_server.ex">>,730}}], 332 | [{default, 333 | [{keep,{<<"lib/gen_server.ex">>,730}}], 334 | 'Elixir.GenServer'}, 335 | {'%{}', 336 | [{keep,{<<"lib/gen_server.ex">>,730}}], 337 | [{id, 338 | {'__MODULE__', 339 | [{keep,{<<"lib/gen_server.ex">>,731}}], 340 | 'Elixir.GenServer'}}, 341 | {start, 342 | {'{}', 343 | [{keep,{<<"lib/gen_server.ex">>,732}}], 344 | [{'__MODULE__', 345 | [{keep,{<<"lib/gen_server.ex">>,732}}], 346 | 'Elixir.GenServer'}, 347 | start_link, 348 | [{init_arg, 349 | [{keep,{<<"lib/gen_server.ex">>,732}}], 350 | 'Elixir.GenServer'}]]}}]}]}, 351 | {{'.', 352 | [{keep,{<<"lib/gen_server.ex">>,735}}], 353 | [{'__aliases__', 354 | [{keep,{<<"lib/gen_server.ex">>,735}},{alias,false}], 355 | ['Supervisor']}, 356 | child_spec]}, 357 | [{keep,{<<"lib/gen_server.ex">>,735}}], 358 | [{default, 359 | [{keep,{<<"lib/gen_server.ex">>,735}}], 360 | 'Elixir.GenServer'}, 361 | {unquote, 362 | [{keep,{<<"lib/gen_server.ex">>,735}}], 363 | [{{'.', 364 | [{keep,{<<"lib/gen_server.ex">>,735}}], 365 | [{'__aliases__', 366 | [{keep,{<<"lib/gen_server.ex">>,735}}, 367 | {alias,false}], 368 | ['Macro']}, 369 | escape]}, 370 | [{keep,{<<"lib/gen_server.ex">>,735}}], 371 | [{opts, 372 | [{keep,{<<"lib/gen_server.ex">>,735}}], 373 | 'Elixir.GenServer'}]}]}]}]}}]]}, 374 | {defoverridable, 375 | [{keep,{<<"lib/gen_server.ex">>,738}}, 376 | {context,'Elixir.GenServer'}, 377 | {import,'Elixir.Kernel'}], 378 | [[{child_spec,1}]]}, 379 | {'@', 380 | [{keep,{<<"lib/gen_server.ex">>,741}}, 381 | {context,'Elixir.GenServer'}, 382 | {import,'Elixir.Kernel'}], 383 | [{before_compile, 384 | [{keep,{<<"lib/gen_server.ex">>,741}}, 385 | {context,'Elixir.GenServer'}], 386 | [{'__aliases__', 387 | [{keep,{<<"lib/gen_server.ex">>,741}},{alias,false}], 388 | ['GenServer']}]}]}, 389 | {'@', 390 | [{keep,{<<"lib/gen_server.ex">>,743}}, 391 | {context,'Elixir.GenServer'}, 392 | {import,'Elixir.Kernel'}], 393 | [{doc, 394 | [{keep,{<<"lib/gen_server.ex">>,743}}, 395 | {context,'Elixir.GenServer'}], 396 | [false]}]}, 397 | {def, 398 | [{keep,{<<"lib/gen_server.ex">>,744}}, 399 | {context,'Elixir.GenServer'}, 400 | {import,'Elixir.Kernel'}], 401 | [{handle_call, 402 | [{keep,{<<"lib/gen_server.ex">>,744}}, 403 | {context,'Elixir.GenServer'}], 404 | [{msg, 405 | [{keep,{<<"lib/gen_server.ex">>,744}}], 406 | 'Elixir.GenServer'}, 407 | {'_from', 408 | [{keep,{<<"lib/gen_server.ex">>,744}}], 409 | 'Elixir.GenServer'}, 410 | {state, 411 | [{keep,{<<"lib/gen_server.ex">>,744}}], 412 | 'Elixir.GenServer'}]}, 413 | [{do, 414 | {'__block__', 415 | [{keep,{<<"lib/gen_server.ex">>,0}}], 416 | [{'=', 417 | [{keep,{<<"lib/gen_server.ex">>,745}}], 418 | [{proc, 419 | [{keep,{<<"lib/gen_server.ex">>,745}}], 420 | 'Elixir.GenServer'}, 421 | {'case', 422 | [{keep,{<<"lib/gen_server.ex">>,746}}], 423 | [{{'.', 424 | [{keep,{<<"lib/gen_server.ex">>,746}}], 425 | [{'__aliases__', 426 | [{keep,{<<"lib/gen_server.ex">>,746}}, 427 | {alias,false}], 428 | ['Process']}, 429 | info]}, 430 | [{keep,{<<"lib/gen_server.ex">>,746}}], 431 | [{self, 432 | [{keep,{<<"lib/gen_server.ex">>,746}}, 433 | {context,'Elixir.GenServer'}, 434 | {import,'Elixir.Kernel'}], 435 | []}, 436 | registered_name]}, 437 | [{do, 438 | [{'->', 439 | [{keep,{<<"lib/gen_server.ex">>,747}}], 440 | [[{{'_', 441 | [{keep,{<<"lib/gen_server.ex">>,747}}], 442 | 'Elixir.GenServer'}, 443 | []}], 444 | {self, 445 | [{keep,{<<"lib/gen_server.ex">>,747}}, 446 | {context,'Elixir.GenServer'}, 447 | {import,'Elixir.Kernel'}], 448 | []}]}, 449 | {'->', 450 | [{keep,{<<"lib/gen_server.ex">>,748}}], 451 | [[{{'_', 452 | [{keep,{<<"lib/gen_server.ex">>,748}}], 453 | 'Elixir.GenServer'}, 454 | {name, 455 | [{keep,{<<"lib/gen_server.ex">>,748}}], 456 | 'Elixir.GenServer'}}], 457 | {name, 458 | [{keep,{<<"lib/gen_server.ex">>,748}}], 459 | 'Elixir.GenServer'}]}]}]]}]}, 460 | {'case', 461 | [{keep,{<<"lib/gen_server.ex">>,752}}], 462 | [{{'.', 463 | [{keep,{<<"lib/gen_server.ex">>,752}}], 464 | [erlang,phash2]}, 465 | [{keep,{<<"lib/gen_server.ex">>,752}}], 466 | [1,1]}, 467 | [{do, 468 | [{'->', 469 | [{keep,{<<"lib/gen_server.ex">>,753}}], 470 | [[0], 471 | {raise, 472 | [{keep,{<<"lib/gen_server.ex">>,754}}, 473 | {context,'Elixir.GenServer'}, 474 | {import,'Elixir.Kernel'}], 475 | [{'<<>>', 476 | [{keep,{<<"lib/gen_server.ex">>,754}}], 477 | [<<"attempted to call GenServer ">>, 478 | {'::', 479 | [{keep,{<<"lib/gen_server.ex">>,754}}], 480 | [{{'.', 481 | [{keep,{<<"lib/gen_server.ex">>,754}}], 482 | ['Elixir.Kernel',to_string]}, 483 | [{keep,{<<"lib/gen_server.ex">>,754}}], 484 | [{inspect, 485 | [{keep,{<<"lib/gen_server.ex">>,754}}, 486 | {context,'Elixir.GenServer'}, 487 | {import,'Elixir.Kernel'}], 488 | [{proc, 489 | [{keep,{<<"lib/gen_server.ex">>,754}}], 490 | 'Elixir.GenServer'}]}]}, 491 | {binary, 492 | [{keep,{<<"lib/gen_server.ex">>,754}}], 493 | 'Elixir.GenServer'}]}, 494 | <<" but no handle_call/3 clause was provided">>]}]}]}, 495 | {'->', 496 | [{keep,{<<"lib/gen_server.ex">>,756}}], 497 | [[1], 498 | {'{}', 499 | [{keep,{<<"lib/gen_server.ex">>,757}}], 500 | [stop, 501 | {bad_call, 502 | {msg, 503 | [{keep,{<<"lib/gen_server.ex">>,757}}], 504 | 'Elixir.GenServer'}}, 505 | {state, 506 | [{keep,{<<"lib/gen_server.ex">>,757}}], 507 | 'Elixir.GenServer'}]}]}]}]]}]}}]]}, 508 | {'@', 509 | [{keep,{<<"lib/gen_server.ex">>,761}}, 510 | {context,'Elixir.GenServer'}, 511 | {import,'Elixir.Kernel'}], 512 | [{doc, 513 | [{keep,{<<"lib/gen_server.ex">>,761}}, 514 | {context,'Elixir.GenServer'}], 515 | [false]}]}, 516 | {def, 517 | [{keep,{<<"lib/gen_server.ex">>,762}}, 518 | {context,'Elixir.GenServer'}, 519 | {import,'Elixir.Kernel'}], 520 | [{handle_info, 521 | [{keep,{<<"lib/gen_server.ex">>,762}}, 522 | {context,'Elixir.GenServer'}], 523 | [{msg, 524 | [{keep,{<<"lib/gen_server.ex">>,762}}], 525 | 'Elixir.GenServer'}, 526 | {state, 527 | [{keep,{<<"lib/gen_server.ex">>,762}}], 528 | 'Elixir.GenServer'}]}, 529 | [{do, 530 | {'__block__', 531 | [{keep,{<<"lib/gen_server.ex">>,0}}], 532 | [{'=', 533 | [{keep,{<<"lib/gen_server.ex">>,763}}], 534 | [{proc, 535 | [{keep,{<<"lib/gen_server.ex">>,763}}], 536 | 'Elixir.GenServer'}, 537 | {'case', 538 | [{keep,{<<"lib/gen_server.ex">>,764}}], 539 | [{{'.', 540 | [{keep,{<<"lib/gen_server.ex">>,764}}], 541 | [{'__aliases__', 542 | [{keep,{<<"lib/gen_server.ex">>,764}}, 543 | {alias,false}], 544 | ['Process']}, 545 | info]}, 546 | [{keep,{<<"lib/gen_server.ex">>,764}}], 547 | [{self, 548 | [{keep,{<<"lib/gen_server.ex">>,764}}, 549 | {context,'Elixir.GenServer'}, 550 | {import,'Elixir.Kernel'}], 551 | []}, 552 | registered_name]}, 553 | [{do, 554 | [{'->', 555 | [{keep,{<<"lib/gen_server.ex">>,765}}], 556 | [[{{'_', 557 | [{keep,{<<"lib/gen_server.ex">>,765}}], 558 | 'Elixir.GenServer'}, 559 | []}], 560 | {self, 561 | [{keep,{<<"lib/gen_server.ex">>,765}}, 562 | {context,'Elixir.GenServer'}, 563 | {import,'Elixir.Kernel'}], 564 | []}]}, 565 | {'->', 566 | [{keep,{<<"lib/gen_server.ex">>,766}}], 567 | [[{{'_', 568 | [{keep,{<<"lib/gen_server.ex">>,766}}], 569 | 'Elixir.GenServer'}, 570 | {name, 571 | [{keep,{<<"lib/gen_server.ex">>,766}}], 572 | 'Elixir.GenServer'}}], 573 | {name, 574 | [{keep,{<<"lib/gen_server.ex">>,766}}], 575 | 'Elixir.GenServer'}]}]}]]}]}, 576 | {'=', 577 | [{keep,{<<"lib/gen_server.ex">>,769}}], 578 | [{pattern, 579 | [{keep,{<<"lib/gen_server.ex">>,769}}], 580 | 'Elixir.GenServer'}, 581 | [126, 582 | 112, 583 | 32, 584 | 126, 585 | 112, 586 | 32, 587 | 114, 588 | 101, 589 | 99, 590 | 101, 591 | 105, 592 | 118, 593 | 101, 594 | 100, 595 | 32, 596 | 117, 597 | 110, 598 | 101, 599 | 120, 600 | 112, 601 | 101, 602 | 99, 603 | 116, 604 | 101, 605 | 100, 606 | 32, 607 | 109, 608 | 101, 609 | 115, 610 | 115, 611 | 97, 612 | 103, 613 | 101, 614 | 32, 615 | 105, 616 | 110, 617 | 32, 618 | 104, 619 | 97, 620 | 110, 621 | 100, 622 | 108, 623 | 101, 624 | 95, 625 | 105, 626 | 110, 627 | 102, 628 | 111, 629 | 47, 630 | 50, 631 | 58, 632 | 32, 633 | 126, 634 | 112, 635 | 126, 636 | 110]]}, 637 | {{'.', 638 | [{keep,{<<"lib/gen_server.ex">>,770}}], 639 | [error_logger,error_msg]}, 640 | [{keep,{<<"lib/gen_server.ex">>,770}}], 641 | [{pattern, 642 | [{keep,{<<"lib/gen_server.ex">>,770}}], 643 | 'Elixir.GenServer'}, 644 | [{'__MODULE__', 645 | [{keep,{<<"lib/gen_server.ex">>,770}}], 646 | 'Elixir.GenServer'}, 647 | {proc, 648 | [{keep,{<<"lib/gen_server.ex">>,770}}], 649 | 'Elixir.GenServer'}, 650 | {msg, 651 | [{keep,{<<"lib/gen_server.ex">>,770}}], 652 | 'Elixir.GenServer'}]]}, 653 | {noreply, 654 | {state, 655 | [{keep,{<<"lib/gen_server.ex">>,771}}], 656 | 'Elixir.GenServer'}}]}}]]}, 657 | {'@', 658 | [{keep,{<<"lib/gen_server.ex">>,774}}, 659 | {context,'Elixir.GenServer'}, 660 | {import,'Elixir.Kernel'}], 661 | [{doc, 662 | [{keep,{<<"lib/gen_server.ex">>,774}}, 663 | {context,'Elixir.GenServer'}], 664 | [false]}]}, 665 | {def, 666 | [{keep,{<<"lib/gen_server.ex">>,775}}, 667 | {context,'Elixir.GenServer'}, 668 | {import,'Elixir.Kernel'}], 669 | [{handle_cast, 670 | [{keep,{<<"lib/gen_server.ex">>,775}}, 671 | {context,'Elixir.GenServer'}], 672 | [{msg, 673 | [{keep,{<<"lib/gen_server.ex">>,775}}], 674 | 'Elixir.GenServer'}, 675 | {state, 676 | [{keep,{<<"lib/gen_server.ex">>,775}}], 677 | 'Elixir.GenServer'}]}, 678 | [{do, 679 | {'__block__', 680 | [{keep,{<<"lib/gen_server.ex">>,0}}], 681 | [{'=', 682 | [{keep,{<<"lib/gen_server.ex">>,776}}], 683 | [{proc, 684 | [{keep,{<<"lib/gen_server.ex">>,776}}], 685 | 'Elixir.GenServer'}, 686 | {'case', 687 | [{keep,{<<"lib/gen_server.ex">>,777}}], 688 | [{{'.', 689 | [{keep,{<<"lib/gen_server.ex">>,777}}], 690 | [{'__aliases__', 691 | [{keep,{<<"lib/gen_server.ex">>,777}}, 692 | {alias,false}], 693 | ['Process']}, 694 | info]}, 695 | [{keep,{<<"lib/gen_server.ex">>,777}}], 696 | [{self, 697 | [{keep,{<<"lib/gen_server.ex">>,777}}, 698 | {context,'Elixir.GenServer'}, 699 | {import,'Elixir.Kernel'}], 700 | []}, 701 | registered_name]}, 702 | [{do, 703 | [{'->', 704 | [{keep,{<<"lib/gen_server.ex">>,778}}], 705 | [[{{'_', 706 | [{keep,{<<"lib/gen_server.ex">>,778}}], 707 | 'Elixir.GenServer'}, 708 | []}], 709 | {self, 710 | [{keep,{<<"lib/gen_server.ex">>,778}}, 711 | {context,'Elixir.GenServer'}, 712 | {import,'Elixir.Kernel'}], 713 | []}]}, 714 | {'->', 715 | [{keep,{<<"lib/gen_server.ex">>,779}}], 716 | [[{{'_', 717 | [{keep,{<<"lib/gen_server.ex">>,779}}], 718 | 'Elixir.GenServer'}, 719 | {name, 720 | [{keep,{<<"lib/gen_server.ex">>,779}}], 721 | 'Elixir.GenServer'}}], 722 | {name, 723 | [{keep,{<<"lib/gen_server.ex">>,779}}], 724 | 'Elixir.GenServer'}]}]}]]}]}, 725 | {'case', 726 | [{keep,{<<"lib/gen_server.ex">>,783}}], 727 | [{{'.', 728 | [{keep,{<<"lib/gen_server.ex">>,783}}], 729 | [erlang,phash2]}, 730 | [{keep,{<<"lib/gen_server.ex">>,783}}], 731 | [1,1]}, 732 | [{do, 733 | [{'->', 734 | [{keep,{<<"lib/gen_server.ex">>,784}}], 735 | [[0], 736 | {raise, 737 | [{keep,{<<"lib/gen_server.ex">>,785}}, 738 | {context,'Elixir.GenServer'}, 739 | {import,'Elixir.Kernel'}], 740 | [{'<<>>', 741 | [{keep,{<<"lib/gen_server.ex">>,785}}], 742 | [<<"attempted to cast GenServer ">>, 743 | {'::', 744 | [{keep,{<<"lib/gen_server.ex">>,785}}], 745 | [{{'.', 746 | [{keep,{<<"lib/gen_server.ex">>,785}}], 747 | ['Elixir.Kernel',to_string]}, 748 | [{keep,{<<"lib/gen_server.ex">>,785}}], 749 | [{inspect, 750 | [{keep,{<<"lib/gen_server.ex">>,785}}, 751 | {context,'Elixir.GenServer'}, 752 | {import,'Elixir.Kernel'}], 753 | [{proc, 754 | [{keep,{<<"lib/gen_server.ex">>,785}}], 755 | 'Elixir.GenServer'}]}]}, 756 | {binary, 757 | [{keep,{<<"lib/gen_server.ex">>,785}}], 758 | 'Elixir.GenServer'}]}, 759 | <<" but no handle_cast/2 clause was provided">>]}]}]}, 760 | {'->', 761 | [{keep,{<<"lib/gen_server.ex">>,787}}], 762 | [[1], 763 | {'{}', 764 | [{keep,{<<"lib/gen_server.ex">>,788}}], 765 | [stop, 766 | {bad_cast, 767 | {msg, 768 | [{keep,{<<"lib/gen_server.ex">>,788}}], 769 | 'Elixir.GenServer'}}, 770 | {state, 771 | [{keep,{<<"lib/gen_server.ex">>,788}}], 772 | 'Elixir.GenServer'}]}]}]}]]}]}}]]}, 773 | {'@', 774 | [{keep,{<<"lib/gen_server.ex">>,792}}, 775 | {context,'Elixir.GenServer'}, 776 | {import,'Elixir.Kernel'}], 777 | [{doc, 778 | [{keep,{<<"lib/gen_server.ex">>,792}}, 779 | {context,'Elixir.GenServer'}], 780 | [false]}]}, 781 | {def, 782 | [{keep,{<<"lib/gen_server.ex">>,793}}, 783 | {context,'Elixir.GenServer'}, 784 | {import,'Elixir.Kernel'}], 785 | [{terminate, 786 | [{keep,{<<"lib/gen_server.ex">>,793}}, 787 | {context,'Elixir.GenServer'}], 788 | [{'_reason', 789 | [{keep,{<<"lib/gen_server.ex">>,793}}], 790 | 'Elixir.GenServer'}, 791 | {'_state', 792 | [{keep,{<<"lib/gen_server.ex">>,793}}], 793 | 'Elixir.GenServer'}]}, 794 | [{do,ok}]]}, 795 | {'@', 796 | [{keep,{<<"lib/gen_server.ex">>,797}}, 797 | {context,'Elixir.GenServer'}, 798 | {import,'Elixir.Kernel'}], 799 | [{doc, 800 | [{keep,{<<"lib/gen_server.ex">>,797}}, 801 | {context,'Elixir.GenServer'}], 802 | [false]}]}, 803 | {def, 804 | [{keep,{<<"lib/gen_server.ex">>,798}}, 805 | {context,'Elixir.GenServer'}, 806 | {import,'Elixir.Kernel'}], 807 | [{code_change, 808 | [{keep,{<<"lib/gen_server.ex">>,798}}, 809 | {context,'Elixir.GenServer'}], 810 | [{'_old', 811 | [{keep,{<<"lib/gen_server.ex">>,798}}], 812 | 'Elixir.GenServer'}, 813 | {state, 814 | [{keep,{<<"lib/gen_server.ex">>,798}}], 815 | 'Elixir.GenServer'}, 816 | {'_extra', 817 | [{keep,{<<"lib/gen_server.ex">>,798}}], 818 | 'Elixir.GenServer'}]}, 819 | [{do, 820 | {ok, 821 | {state, 822 | [{keep,{<<"lib/gen_server.ex">>,799}}], 823 | 'Elixir.GenServer'}}}]]}, 824 | {defoverridable, 825 | [{keep,{<<"lib/gen_server.ex">>,802}}, 826 | {context,'Elixir.GenServer'}, 827 | {import,'Elixir.Kernel'}], 828 | [[{code_change,3}, 829 | {terminate,2}, 830 | {handle_info,2}, 831 | {handle_cast,2}, 832 | {handle_call,3}]]}]}]}. 833 | 834 | abcast(__@1, __@2) -> 835 | abcast([node()|nodes()], __@1, __@2). 836 | 837 | abcast(_nodes@1, _name@1, _request@1) 838 | when 839 | is_list(_nodes@1) 840 | andalso 841 | is_atom(_name@1) -> 842 | _msg@1 = cast_msg(_request@1), 843 | 'Elixir.Enum':reduce(_nodes@1, 844 | [], 845 | fun(_node@1, __@1) -> 846 | begin 847 | do_send({_name@1,_node@1}, _msg@1), 848 | nil 849 | end 850 | end), 851 | abcast. 852 | 853 | call(__@1, __@2) -> 854 | call(__@1, __@2, 5000). 855 | 856 | call(_server@1, _request@1, _timeout@1) -> 857 | case whereis(_server@1) of 858 | nil -> 859 | exit({noproc, 860 | {'Elixir.GenServer', 861 | call, 862 | [_server@1,_request@1,_timeout@1]}}); 863 | _pid@1 when _pid@1 == self() -> 864 | exit({calling_self, 865 | {'Elixir.GenServer', 866 | call, 867 | [_server@1,_request@1,_timeout@1]}}); 868 | _pid@2 -> 869 | try gen:call(_pid@2, '$gen_call', _request@1, _timeout@1) of 870 | {ok,_res@1} -> 871 | _res@1 872 | catch 873 | exit:_reason@1 -> 874 | exit({_reason@1, 875 | {'Elixir.GenServer', 876 | call, 877 | [_server@1,_request@1,_timeout@1]}}) 878 | end 879 | end. 880 | 881 | cast({global,_name@1}, _request@1) -> 882 | try 883 | global:send(_name@1, cast_msg(_request@1)), 884 | ok 885 | catch 886 | _:_ -> 887 | ok 888 | end; 889 | cast({via,_mod@1,_name@1}, _request@1) -> 890 | try 891 | _mod@1:send(_name@1, cast_msg(_request@1)), 892 | ok 893 | catch 894 | _:_ -> 895 | ok 896 | end; 897 | cast({_name@1,_node@1}, _request@1) 898 | when 899 | is_atom(_name@1) 900 | andalso 901 | is_atom(_node@1) -> 902 | do_send({_name@1,_node@1}, cast_msg(_request@1)); 903 | cast(_dest@1, _request@1) 904 | when 905 | is_atom(_dest@1) 906 | orelse 907 | is_pid(_dest@1) -> 908 | do_send(_dest@1, cast_msg(_request@1)). 909 | 910 | cast_msg(_req@1) -> 911 | {'$gen_cast',_req@1}. 912 | 913 | do_send(_dest@1, _msg@1) -> 914 | try 915 | erlang:send(_dest@1, _msg@1), 916 | ok 917 | catch 918 | _:_ -> 919 | ok 920 | end. 921 | 922 | do_start(_link@1, _module@1, _init_arg@1, _options@1) -> 923 | case 'Elixir.Keyword':pop(_options@1, name) of 924 | {nil,_opts@1} -> 925 | gen:start(gen_server, 926 | _link@1, 927 | _module@1, 928 | _init_arg@1, 929 | _opts@1); 930 | {_atom@1,_opts@2} when is_atom(_atom@1) -> 931 | gen:start(gen_server, 932 | _link@1, 933 | {local,_atom@1}, 934 | _module@1, 935 | _init_arg@1, 936 | _opts@2); 937 | {{global,__term@1} = _tuple@1,_opts@3} -> 938 | gen:start(gen_server, 939 | _link@1, 940 | _tuple@1, 941 | _module@1, 942 | _init_arg@1, 943 | _opts@3); 944 | {{via,_via_module@1,__term@2} = _tuple@2,_opts@4} 945 | when is_atom(_via_module@1) -> 946 | gen:start(gen_server, 947 | _link@1, 948 | _tuple@2, 949 | _module@1, 950 | _init_arg@1, 951 | _opts@4); 952 | {_other@1,_} -> 953 | error('Elixir.ArgumentError':exception(<<"expected :name op" 954 | "tion to be one of" 955 | " the following:\n" 956 | "\n * nil\n * at" 957 | "om\n * {:global," 958 | " term}\n * {:via" 959 | ", module, term}\n" 960 | "\nGot: ", 961 | ('Elixir.Kernel':inspect(_other@1))/binary, 962 | "\n">>)) 963 | end. 964 | 965 | multi_call(__@1, __@2) -> 966 | multi_call([node()|nodes()], __@1, __@2, infinity). 967 | 968 | multi_call(__@1, __@2, __@3) -> 969 | multi_call(__@1, __@2, __@3, infinity). 970 | 971 | multi_call(_nodes@1, _name@1, _request@1, _timeout@1) -> 972 | gen_server:multi_call(_nodes@1, _name@1, _request@1, _timeout@1). 973 | 974 | reply({_to@1,_tag@1}, _reply@1) when is_pid(_to@1) -> 975 | erlang:send(_to@1, {_tag@1,_reply@1}), 976 | ok. 977 | 978 | start(__@1, __@2) -> 979 | start(__@1, __@2, []). 980 | 981 | start(_module@1, _init_arg@1, _options@1) 982 | when 983 | is_atom(_module@1) 984 | andalso 985 | is_list(_options@1) -> 986 | do_start(nolink, _module@1, _init_arg@1, _options@1). 987 | 988 | start_link(__@1, __@2) -> 989 | start_link(__@1, __@2, []). 990 | 991 | start_link(_module@1, _init_arg@1, _options@1) 992 | when 993 | is_atom(_module@1) 994 | andalso 995 | is_list(_options@1) -> 996 | do_start(link, _module@1, _init_arg@1, _options@1). 997 | 998 | stop(__@1) -> 999 | stop(__@1, normal, infinity). 1000 | 1001 | stop(__@1, __@2) -> 1002 | stop(__@1, __@2, infinity). 1003 | 1004 | stop(_server@1, _reason@1, _timeout@1) -> 1005 | case whereis(_server@1) of 1006 | nil -> 1007 | exit({noproc, 1008 | {'Elixir.GenServer', 1009 | stop, 1010 | [_server@1,_reason@1,_timeout@1]}}); 1011 | _pid@1 when _pid@1 == self() -> 1012 | exit({calling_self, 1013 | {'Elixir.GenServer', 1014 | stop, 1015 | [_server@1,_reason@1,_timeout@1]}}); 1016 | _pid@2 -> 1017 | try 1018 | proc_lib:stop(_pid@2, _reason@1, _timeout@1) 1019 | catch 1020 | exit:_err@1 -> 1021 | exit({_err@1, 1022 | {'Elixir.GenServer', 1023 | stop, 1024 | [_server@1,_reason@1,_timeout@1]}}) 1025 | end 1026 | end. 1027 | 1028 | whereis(_pid@1) when is_pid(_pid@1) -> 1029 | _pid@1; 1030 | whereis(_name@1) when is_atom(_name@1) -> 1031 | 'Elixir.Process':whereis(_name@1); 1032 | whereis({global,_name@1}) -> 1033 | case global:whereis_name(_name@1) of 1034 | _pid@1 when is_pid(_pid@1) -> 1035 | _pid@1; 1036 | undefined -> 1037 | nil 1038 | end; 1039 | whereis({via,_mod@1,_name@1}) -> 1040 | case apply(_mod@1, whereis_name, [_name@1]) of 1041 | _pid@1 when is_pid(_pid@1) -> 1042 | _pid@1; 1043 | undefined -> 1044 | nil 1045 | end; 1046 | whereis({_name@1,_local@1}) 1047 | when 1048 | is_atom(_name@1) 1049 | andalso 1050 | _local@1 == node() -> 1051 | 'Elixir.Process':whereis(_name@1); 1052 | whereis({_name@1,_node@1} = _server@1) 1053 | when 1054 | is_atom(_name@1) 1055 | andalso 1056 | is_atom(_node@1) -> 1057 | _server@1. 1058 | 1059 | --------------------------------------------------------------------------------