From a5863c485228d2509959def96697e85f57fb8987 Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Thu, 14 Oct 2021 14:21:43 +0200 Subject: [PATCH 01/10] ffi: Fix error message --- src/ffi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ffi.rs b/src/ffi.rs index 79b72bd6..abf1de61 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -120,7 +120,7 @@ pub fn execute( ctx.error( Error::new(ErrKind::ExternFunc) - .with_msg(format!("cannot call external function {}", call.name())), + .with_msg(format!("cannot call external function `{}`", call.name())), ); None From b7159333bb8fc791fb4f79dd4887a384cd2972db Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Thu, 14 Oct 2021 14:46:49 +0200 Subject: [PATCH 02/10] ffi: Add ability to call fn(string) -> () and fn(string) -> int --- src/ffi.rs | 73 ++++++++++++++++++++++---------- src/instruction/function_call.rs | 8 +++- 2 files changed, 58 insertions(+), 23 deletions(-) diff --git a/src/ffi.rs b/src/ffi.rs index abf1de61..1073b900 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -5,7 +5,12 @@ use std::path::{Path, PathBuf}; use crate::instruction::{FunctionCall, FunctionDec}; -use crate::{Context, ErrKind, Error, JkInt, ObjectInstance, ToObjectInstance}; +use crate::{ + Context, ErrKind, Error, FromObjectInstance, JkInt, JkString, ObjectInstance, ToObjectInstance, +}; + +use std::ffi::CString; +use std::os::raw::c_char; fn find_lib(paths: &[PathBuf], lib_path: &Path) -> Result { for dir_base in paths.iter() { @@ -85,45 +90,69 @@ pub fn execute( dec: &FunctionDec, call: &FunctionCall, ctx: &mut Context, -) -> Option { +) -> Result, Error> { ctx.debug("EXT CALL", call.name()); let sym = call.name().as_bytes(); + // FIXME: Don't unwrap + let args: Vec = call + .args() + .iter() + .map(|arg| arg.execute(ctx).unwrap()) + .collect(); + for lib in ctx.libs().iter() { unsafe { if lib.get::>(sym).is_ok() { match call.args().len() { - 0 => { - match dec.ty() { + 0 => match dec.ty() { + None => { + let f = lib.get::>(sym)?; + f(); + return Ok(None); + } + Some(ty) => match ty.id() { + "int" => { + let f = lib.get:: i64>>(sym)?; + let res = f(); + return Ok(Some(JkInt::from(res).to_instance())); + } + _ => unreachable!(), + }, + }, + 1 => match dec.args()[0].get_type().id() { + "string" => match dec.ty() { None => { - if let Ok(f) = lib.get::>(sym) { - f(); - return None; - } + let f = lib.get::>(sym)?; + let arg = JkString::from_instance(&args[0]).0; + let arg = CString::new(arg.as_str()).unwrap(); + let arg = arg.as_ptr(); + f(arg); + return Ok(None); } Some(ty) => match ty.id() { "int" => { - if let Ok(f) = lib.get:: i64>>(sym) { - let res = f(); - return Some(JkInt::from(res).to_instance()); - } + let f = lib + .get:: i64>>(sym)?; + let arg = JkString::from_instance(&args[0]).0; + let arg = CString::new(arg.as_str()).unwrap(); + let arg = arg.as_ptr(); + let res = f(arg); + return Ok(Some(JkInt::from(res).to_instance())); } _ => unreachable!(), }, - }; - } + }, + _ => unreachable!(), + }, _ => unreachable!(), } } } } - ctx.error( - Error::new(ErrKind::ExternFunc) - .with_msg(format!("cannot call external function `{}`", call.name())), - ); - - None + Err(Error::new(ErrKind::ExternFunc) + .with_msg(format!("cannot call external function `{}`", call.name()))) } #[cfg(test)] @@ -161,7 +190,7 @@ mod tests { assert_eq!( execute(&dec, &call, &mut i), - Some(JkInt::from(15).to_instance()) + Ok(Some(JkInt::from(15).to_instance())) ); } @@ -176,7 +205,7 @@ mod tests { let call = Construct::instruction("print_something()").unwrap().1; let call = call.downcast_ref::().unwrap(); - assert_eq!(execute(&dec, &call, &mut i), None); + assert_eq!(execute(&dec, &call, &mut i), Ok(None)); } #[test] diff --git a/src/instruction/function_call.rs b/src/instruction/function_call.rs index 0eddecab..d5e1b7ba 100644 --- a/src/instruction/function_call.rs +++ b/src/instruction/function_call.rs @@ -131,7 +131,13 @@ impl FunctionCall { } } } else { - crate::ffi::execute(dec, self, ctx) + match crate::ffi::execute(dec, self, ctx) { + Ok(value) => value, + Err(e) => { + ctx.error(e.into()); + None + } + } } } } From dc3cfcf450ba1088c92b082b85892280ae8ba42e Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Thu, 14 Oct 2021 14:52:31 +0200 Subject: [PATCH 03/10] shell: Add basic shell wrapper --- stdlib/lib.jk | 1 + stdlib/shell.jk | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 stdlib/shell.jk diff --git a/stdlib/lib.jk b/stdlib/lib.jk index ffdf08ce..8e11ea63 100644 --- a/stdlib/lib.jk +++ b/stdlib/lib.jk @@ -2,3 +2,4 @@ incl pair incl string incl maybe incl ffi +incl shell diff --git a/stdlib/shell.jk b/stdlib/shell.jk new file mode 100644 index 00000000..9cd4ff2f --- /dev/null +++ b/stdlib/shell.jk @@ -0,0 +1,27 @@ +link_with("/usr/lib/libc.so.6"); + +ext func system(s: string) -> int; + +// FIXME: Should return a Result +func shell(s: string) -> int { + system(s) +} + +type Cmd(s: string); + +func cmd(command: string) -> Cmd { + // FIXME: Make sure `command` does not contain any spaces + Cmd { s = command } +} + +func with_arg(cmd: Cmd, arg: string) -> Cmd { + mut new_s = concat(cmd.s, " "); + new_s = new_s.concat(arg); + + Cmd { s = new_s } +} + +// FIXME: Should return a Result +func execute(cmd: Cmd) -> int { + shell(cmd.s) +} From 636953126c2a09fdbb906ff96221d64f52025d0e Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Thu, 14 Oct 2021 15:00:01 +0200 Subject: [PATCH 04/10] stdlib: Add functional tests for shell module --- tests/ft/stdlib/shell.jk | 7 +++++++ tests/ft/stdlib/stdlib.yml | 5 +++++ 2 files changed, 12 insertions(+) create mode 100644 tests/ft/stdlib/shell.jk diff --git a/tests/ft/stdlib/shell.jk b/tests/ft/stdlib/shell.jk new file mode 100644 index 00000000..3382c4f4 --- /dev/null +++ b/tests/ft/stdlib/shell.jk @@ -0,0 +1,7 @@ +shell("echo 'from shell'") + +mut c = cmd("echo").with_arg("from"); +c = c.with_arg("cmd"); +c = c.with_arg("builder"); + +c.execute() diff --git a/tests/ft/stdlib/stdlib.yml b/tests/ft/stdlib/stdlib.yml index 03c25aef..1681b3ad 100644 --- a/tests/ft/stdlib/stdlib.yml +++ b/tests/ft/stdlib/stdlib.yml @@ -18,3 +18,8 @@ tests: args: - "tests/ft/stdlib/ffi.jk" stdout: "jinko called\n" + - name: "Test shell module" + binary: "target/debug/jinko" + args: + - "tests/ft/stdlib/shell.jk" + stdout: "from shell\nfrom cmd builder\n" From fb1a2dcff08083702b4a07fb34cbe0d1110730f8 Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Thu, 14 Oct 2021 16:09:23 +0200 Subject: [PATCH 05/10] ffi: Use transmute to avoid fetching symbol continuously --- src/ffi.rs | 105 +++++++++++++++++++++++++++-------------------------- 1 file changed, 53 insertions(+), 52 deletions(-) diff --git a/src/ffi.rs b/src/ffi.rs index 1073b900..f16d8e33 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -2,22 +2,23 @@ //! Primitive types are converted to their C counterparts. //! FIXME -use std::path::{Path, PathBuf}; - use crate::instruction::{FunctionCall, FunctionDec}; use crate::{ Context, ErrKind, Error, FromObjectInstance, JkInt, JkString, ObjectInstance, ToObjectInstance, }; +use libloading::{Library, Symbol}; use std::ffi::CString; +use std::mem::transmute; use std::os::raw::c_char; +use std::path::{Path, PathBuf}; -fn find_lib(paths: &[PathBuf], lib_path: &Path) -> Result { +fn find_lib(paths: &[PathBuf], lib_path: &Path) -> Result { for dir_base in paths.iter() { let lib_path = PathBuf::from(dir_base).join(lib_path); dbg!(&lib_path); if lib_path.exists() { - if let Ok(lib) = unsafe { libloading::Library::new(lib_path) } { + if let Ok(lib) = unsafe { Library::new(lib_path) } { return Ok(lib); } } @@ -26,7 +27,7 @@ fn find_lib(paths: &[PathBuf], lib_path: &Path) -> Result Result { +fn lib_from_root(base: &Path, lib_path: &Path) -> Result { // We search through all entries in the `base` directory as well as all the entries // in the directories contained in `base`. let mut dirs = vec![PathBuf::from(base)]; @@ -44,7 +45,7 @@ fn lib_from_root(base: &Path, lib_path: &Path) -> Result Result { +fn lib_from_path(lib_path: PathBuf) -> Result { if let Ok(lib) = lib_from_ld_library_path(&lib_path) { return Ok(lib); } @@ -61,13 +62,13 @@ fn lib_from_path(lib_path: PathBuf) -> Result { } /// Look through the paths specified in the LD_LIBRARY_PATH environment variable (if any) -fn lib_from_ld_library_path(lib_path: &Path) -> Result { +fn lib_from_ld_library_path(lib_path: &Path) -> Result { let paths = std::env::var("LD_LIBRARY_PATH")?; for dir in paths.split(':') { let path = PathBuf::from(dir).join(&lib_path); if path.exists() { - return unsafe { Ok(libloading::Library::new(path)?) }; + return unsafe { Ok(Library::new(path)?) }; } } @@ -76,7 +77,7 @@ fn lib_from_ld_library_path(lib_path: &Path) -> Result Result<(), Error> { let lib = if std::path::Path::new(&lib_path).exists() { - unsafe { libloading::Library::new(lib_path)? } + unsafe { Library::new(lib_path)? } } else { lib_from_path(lib_path)? }; @@ -102,52 +103,52 @@ pub fn execute( .collect(); for lib in ctx.libs().iter() { - unsafe { - if lib.get::>(sym).is_ok() { - match call.args().len() { - 0 => match dec.ty() { - None => { - let f = lib.get::>(sym)?; - f(); - return Ok(None); + let func = unsafe { lib.get::>(sym) }; + let func = match func { + Ok(func) => func, + Err(_) => continue, + }; + match call.args().len() { + 0 => match dec.ty() { + None => { + func(); + return Ok(None); + } + Some(ty) => match ty.id() { + "int" => { + let func = unsafe { transmute:: i64>(**func) }; + let res = func(); + return Ok(Some(JkInt::from(res).to_instance())); + } + _ => unreachable!(), + }, + }, + 1 => match dec.args()[0].get_type().id() { + "string" => match dec.ty() { + None => { + let func = unsafe { transmute::(**func) }; + let arg = JkString::from_instance(&args[0]).0; + let arg = CString::new(arg.as_str()).unwrap(); + let arg = arg.as_ptr(); + func(arg); + return Ok(None); + } + Some(ty) => match ty.id() { + "int" => { + let func = + unsafe { transmute:: i64>(**func) }; + let arg = JkString::from_instance(&args[0]).0; + let arg = CString::new(arg.as_str()).unwrap(); + let arg = arg.as_ptr(); + let res = func(arg); + return Ok(Some(JkInt::from(res).to_instance())); } - Some(ty) => match ty.id() { - "int" => { - let f = lib.get:: i64>>(sym)?; - let res = f(); - return Ok(Some(JkInt::from(res).to_instance())); - } - _ => unreachable!(), - }, - }, - 1 => match dec.args()[0].get_type().id() { - "string" => match dec.ty() { - None => { - let f = lib.get::>(sym)?; - let arg = JkString::from_instance(&args[0]).0; - let arg = CString::new(arg.as_str()).unwrap(); - let arg = arg.as_ptr(); - f(arg); - return Ok(None); - } - Some(ty) => match ty.id() { - "int" => { - let f = lib - .get:: i64>>(sym)?; - let arg = JkString::from_instance(&args[0]).0; - let arg = CString::new(arg.as_str()).unwrap(); - let arg = arg.as_ptr(); - let res = f(arg); - return Ok(Some(JkInt::from(res).to_instance())); - } - _ => unreachable!(), - }, - }, _ => unreachable!(), }, - _ => unreachable!(), - } - } + }, + _ => unreachable!(), + }, + _ => unreachable!(), } } From f8855533c4e363437e22462f7ad6613458ed8efc Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Thu, 14 Oct 2021 16:36:45 +0200 Subject: [PATCH 06/10] function_call: Fix clippy warning --- src/instruction/function_call.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/instruction/function_call.rs b/src/instruction/function_call.rs index d5e1b7ba..c9535386 100644 --- a/src/instruction/function_call.rs +++ b/src/instruction/function_call.rs @@ -134,7 +134,7 @@ impl FunctionCall { match crate::ffi::execute(dec, self, ctx) { Ok(value) => value, Err(e) => { - ctx.error(e.into()); + ctx.error(e); None } } From 4bd9062d404a94ebbb0856c93bd3dbbd07ce44f6 Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Fri, 15 Oct 2021 16:27:11 +0200 Subject: [PATCH 07/10] tests: Add base for functional tests in jinko --- tests/func_tests.jk | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/func_tests.jk diff --git a/tests/func_tests.jk b/tests/func_tests.jk new file mode 100644 index 00000000..9afd2dc2 --- /dev/null +++ b/tests/func_tests.jk @@ -0,0 +1,38 @@ +#!jinko + +ft_exists_exit_code = shell("ft --version"); + +ft_exists = if ft_exists_exit_code == 0 { + true +} else { + false +}; + +if !ft_exists { // ft_exists.not() + display_err("\n`ft` not found. Install it using the following command\n\n"); + display_err("\tcargo install --git https://github.com/cohenarthur/ft\n\n"); + + exit(1); +} + +shell("cargo build") + +if !args().empty() { + display("Test files:\n"); + for arg in args() { + display(arg); + + shell("ft -f {arg}"); + } +} else { + // Find all files somehow + files = shell("find tests -name '*.yml'").output(); + + display("Test files:\n{files}\n\n"); + + shell("ft -f {files}"); +} + +// FIXME: Parse arguments correctly +// FIXME: Add split method on string +// FIXME: Add string vector From 7b06c99c35aa765df4bcd28558cbda8e7adc009b Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Fri, 15 Oct 2021 16:36:57 +0200 Subject: [PATCH 08/10] ffi: Remove test removing LD_LIBRARY_PATH variable --- src/ffi.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/ffi.rs b/src/ffi.rs index f16d8e33..f7193738 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -226,12 +226,5 @@ mod tests { jinko! { link_with("libc.so.6"); }; - - std::env::remove_var("LD_LIBRARY_PATH"); - - // Load libc without LD_LIBRARY_PATH - jinko! { - link_with("libc.so.6"); - }; } } From 5d8cb5a63e3493cd67f136c092f2bb9bdb02f3ac Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Fri, 15 Oct 2021 16:47:15 +0200 Subject: [PATCH 09/10] ffi: Remove dbg print --- src/ffi.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ffi.rs b/src/ffi.rs index f7193738..99e8be73 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -16,7 +16,6 @@ use std::path::{Path, PathBuf}; fn find_lib(paths: &[PathBuf], lib_path: &Path) -> Result { for dir_base in paths.iter() { let lib_path = PathBuf::from(dir_base).join(lib_path); - dbg!(&lib_path); if lib_path.exists() { if let Ok(lib) = unsafe { Library::new(lib_path) } { return Ok(lib); From f49c1b90dfe31a140d73ec64017cedce7be2a2d1 Mon Sep 17 00:00:00 2001 From: CohenArthur Date: Fri, 15 Oct 2021 16:48:17 +0200 Subject: [PATCH 10/10] stdlib/shell: Remove absolute path --- stdlib/shell.jk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/shell.jk b/stdlib/shell.jk index 9cd4ff2f..48ec3654 100644 --- a/stdlib/shell.jk +++ b/stdlib/shell.jk @@ -1,4 +1,4 @@ -link_with("/usr/lib/libc.so.6"); +link_with("libc.so.6"); ext func system(s: string) -> int;