Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vectors and buffers #202

Closed
wants to merge 10 commits into from
7 changes: 7 additions & 0 deletions .travis.sh
Expand Up @@ -20,6 +20,13 @@ cargo fmt -- --write-mode=diff
cd "$DIR/rust_src/remacs-sys"
cargo fmt -- --write-mode=diff

cd "$DIR/rust_src/remacs-lib"
cargo fmt -- --write-mode=diff

cd "$DIR/rust_src/remacs-macros"
# TODO: not yet - cargo fmt seems to enter an infinite loop here.
# cargo fmt -- --write-mode=diff

cd "$DIR/rust_src/alloc_unexecmacosx"
cargo fmt -- --write-mode=diff

Expand Down
2 changes: 1 addition & 1 deletion Makefile.in
Expand Up @@ -896,7 +896,7 @@ top_distclean=\
${top_bootclean}; \
rm -f config.status config.log~ Makefile stamp-h1 ${SUBDIR_MAKEFILES}

distclean_dirs = $(clean_dirs) leim lisp
distclean_dirs = $(clean_dirs) leim

$(foreach dir,$(distclean_dirs),$(eval $(call submake_template,$(dir),distclean)))

Expand Down
60 changes: 31 additions & 29 deletions README.md
Expand Up @@ -143,21 +143,21 @@ more Emacs-y.

1. You will need [Rust installed](https://www.rust-lang.org/en-US/install.html). If you're on macOS, you will need Rust
nightly.

2. You will need a C compiler and toolchain. On Linux, you can do
something like `apt-get install build-essential automake`. On
macOS, you'll need Xcode.

3. You will need some C libraries. On Linux, you can install
everything you need with:

apt-get install texinfo libjpeg-dev libtiff-dev \
libgif-dev libxpm-dev libgtk-3-dev libgnutls-dev \
libncurses5-dev libxml2-dev

On macOS, you'll need libxml2 (via `xcode-select --install`) and
gnutls (via `brew install gnutls`).

### Building Remacs

```
Expand Down Expand Up @@ -246,9 +246,9 @@ $ gcc -Ilib -E src/dummy.c > dummy_exp.c
This gives us a file that ends with:

``` c
static struct Lisp_Subr
static struct Lisp_Subr
# 3 "src/dummy.c" 3 4
_Alignas
_Alignas
# 3 "src/dummy.c"
(8) Snumberp = { { PVEC_SUBR << PSEUDOVECTOR_AREA_BITS }, { .a1 = Fnumberp }, 1, 1, "numberp", 0, 0}; Lisp_Object Fnumberp

Expand All @@ -263,36 +263,38 @@ _Alignas
```

We can see we need to define a `Snumberp` and a `Fnumberp`. We define
a `numberp` function that does the actual work, then `defun!` handles
a `numberp` function that does the actual work, then use an attribute
(implemented as a procedural macro) named `lisp_fn` that handles
these definitions for us:

``` rust
// This is the function that gets called when
// This is the function that gets called when
// we call numberp in elisp.
//
// `lisp_fn` defines a wrapper function that calls numberp with
// LispObject values. It also declares a struct that we can pass to
// defsubr so the elisp interpreter knows about this function.

/// Return t if OBJECT is a number.
#[lisp_fn]
fn numberp(object: LispObject) -> LispObject {
if lisp::NUMBERP(object) {
LispObject::constant_t()
} else {
LispObject::constant_nil()
}
LispObject::from_bool(object.is_number())
}

// defun! defines a wrapper function that calls numberp with
// LispObject values. It also declares a struct that we can pass to
// defsubr so the elisp interpreter knows about our function.
defun!("numberp", // the name of our primitive function inside elisp
Fnumberp(object), // the signature of the wrapper function
Snumberp, // the name of the struct that describes our function
numberp, // the rust function we want to call
1, 1, // min and max number of arguments
ptr::null(), // our function is not interactive
// docstring, the last line ensures that *Help* shows the
// correct calling convention
"Return t if OBJECT is a number (floating point or integer).

(fn OBJECT)");
```

The elisp name of the function is derived from the Rust name, with
underscores replaced by hyphens. If that is not possible (like for
the function `+`), you can give an elisp name as an argument to
`lisp\_fn`, like `#[lisp_fn(name = "+")]`.

Optional arguments are also possible: to make the minimum number of
arguments from elisp different from the number of Rust arguments,
pass a `min = "n"` argument.

The docstring of the function should be the same as the docstring
in the C code. (Don't wonder about it being a comment there, Emacs
has some magic that extracts it into a separate file.)

Finally, we need to delete the old C definition and call `defsubr`
inside `rust_init_syms`:

Expand Down
39 changes: 15 additions & 24 deletions rust_src/remacs-lib/files.rs
Expand Up @@ -10,7 +10,10 @@ use libc::{O_CLOEXEC, O_EXCL, O_RDWR, O_CREAT, open};

#[cfg(windows)]
extern "C" {
fn sys_open (filename: *const libc::c_char, flags: libc::c_int, mode: libc::c_int) -> libc::c_int;
fn sys_open(filename: *const libc::c_char,
flags: libc::c_int,
mode: libc::c_int)
-> libc::c_int;
}

use libc::{EEXIST, EINVAL};
Expand All @@ -23,15 +26,9 @@ use self::rand::Rng;
const NUM_RETRIES: usize = 50;

#[no_mangle]
pub extern "C" fn rust_make_temp(template: *mut libc::c_char,
flags: libc::c_int)
-> libc::c_int {
pub extern "C" fn rust_make_temp(template: *mut libc::c_char, flags: libc::c_int) -> libc::c_int {
let save_errno = errno::errno();
let template_string = unsafe {
CStr::from_ptr(template)
.to_string_lossy()
.into_owned()
};
let template_string = unsafe { CStr::from_ptr(template).to_string_lossy().into_owned() };

match make_temporary_file(template_string, flags) {
Ok(result) => {
Expand All @@ -40,23 +37,20 @@ pub extern "C" fn rust_make_temp(template: *mut libc::c_char,
unsafe { libc::strcpy(template, name.as_ptr()) };
result.0
}

Err(error_code) => {
errno::set_errno(errno::Errno(error_code));
-1
}
}

}

pub fn make_temporary_file(template: String,
flags: i32)
-> Result<(i32, String), i32> {
pub fn make_temporary_file(template: String, flags: i32) -> Result<(i32, String), i32> {
let mut validated_template = try!(validate_template(template));
for _ in 0..NUM_RETRIES {
generate_temporary_filename(&mut validated_template);
let attempt = try!(CString::new(validated_template.clone())
.map_err(|_| EEXIST));
let attempt = try!(CString::new(validated_template.clone()).map_err(|_| EEXIST));
let file_handle = match open_temporary_file(&attempt, flags) {
Ok(file) => file,
Err(_) => continue,
Expand All @@ -79,9 +73,7 @@ fn validate_template(template: String) -> Result<String, i32> {
fn generate_temporary_filename(name: &mut String) {
let len = name.len();
assert!(len >= 6);
let mut name_vec = unsafe {
&mut name.as_mut_vec()
};
let mut name_vec = unsafe { &mut name.as_mut_vec() };

let mut bytes = &mut name_vec[len - 6..len];
rand::thread_rng().fill_bytes(bytes);
Expand All @@ -99,7 +91,7 @@ fn generate_temporary_filename(name: &mut String) {
fn open_temporary_file(name: &CString, flags: libc::c_int) -> io::Result<libc::c_int> {
unsafe {
match open(name.as_ptr(),
O_CLOEXEC | O_EXCL | O_RDWR | O_CREAT | flags,
O_CLOEXEC | O_EXCL | O_RDWR | O_CREAT | flags,
0o600) {
-1 => Err(io::Error::last_os_error()),
fd => Ok(fd),
Expand All @@ -113,9 +105,7 @@ fn open_temporary_file(name: &CString, flags: libc::c_int) -> io::Result<libc::c
let _O_EXCL = 0x0400;
let _O_RDWR = 0x0002;
unsafe {
match sys_open(name.as_ptr(),
_O_CREAT | _O_EXCL | _O_RDWR | flags,
0o600) {
match sys_open(name.as_ptr(), _O_CREAT | _O_EXCL | _O_RDWR | flags, 0o600) {
-1 => Err(io::Error::last_os_error()),
fd => Ok(fd),
}
Expand Down Expand Up @@ -151,7 +141,8 @@ fn test_generate_temporary_filename() {
let mut name = String::from(".emacs-XXXXXX");
let name_copy = name.clone();
generate_temporary_filename(&mut name);
assert!(name != name_copy, "Temporary filename should always mutate the name string");
assert!(name != name_copy,
"Temporary filename should always mutate the name string");
}

#[test]
Expand Down
42 changes: 42 additions & 0 deletions rust_src/remacs-macros/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 36 additions & 16 deletions rust_src/remacs-macros/README.md
Expand Up @@ -4,37 +4,57 @@ remacs-macros
# Attributes

## `lisp_fn`
This macro creates the necessary FFI functions and symbols automatically.
It handles normal functions and functions that take an arbitrary number of arguments (functions with `MANY` as the
maximum number of arguments on the C side)

This macro creates the necessary FFI functions and symbols
automatically. It handles normal functions and functions that take an
arbitrary number of arguments (functions with `MANY` as the maximum
number of arguments on the C side)

It is used like this:

```rust
/// Return the same argument
#[lisp_fn(name = "same", min = "1")]
#[lisp_fn(name = "same", c_name = "same", min = "1"]
fn same(obj: LispObject) -> LispObject {
obj
}
```

Here the `name` argument specifies the **symbol name** that is going to be use in Emacs Lisp, `min` specifies the **minimum** number of arguments that can be passed to this function, the **maximum** number of arguments is calculated automatically from the function signature.

In this example the attribute generates the `Fsame` function that is going to be called in C, and the `Ssame` structure that holds the function information. You still need to register the function with `defsubr` to make it visible in Emacs Lisp. To make a function visible to C you need to export it in the crate root (lib.rs) as follows:
Here the `name` argument specifies the **symbol name** that is going
to be use in Emacs Lisp, `c_name` specifies the name for the `Fsame`
and `Ssame` statics used in C, and `min` specifies the **minimum**
number of arguments that can be passed to this function, the
**maximum** number of arguments is calculated automatically from the
function signature.

All three of these arguments are optional, and have sane defaults.
Default for `name` is the Rust function name with `_` replaced by `-`.
Default for `c_name` is the Rust function name. Default for `min` is
the number of Rust arguments, giving a function without optional
arguments.

In this example the attribute generates the `Fsame` function that is
going to be called in C, and the `Ssame` structure that holds the
function information. You still need to register the function with
`defsubr` to make it visible in Emacs Lisp. To make a function visible
to C you need to export it in the crate root (lib.rs) as follows:

```rust
use somemodule::Fsome;
```

### Functions with a dynamic number of arguments (`MANY`)

This attribute handles too the definition of functions that take an arbitrary number of arguments, these functions can take an arbitrary number of arguments, but you *still can* specify a `min` number of arguments.
This attribute handles too the definition of functions that take an
arbitrary number of arguments, these functions can take an arbitrary
number of arguments, but you *still can* specify a `min` number of
arguments.

They are created as follows:

```rust
/// Returns the first argument.
#[lisp_fn(name = "first", min = "1")]
#[lisp_fn(min = "1")]
fn first(args: &mut [LispObject]) -> LispObject {
args[0]
}
Expand All @@ -57,16 +77,16 @@ DEFUN ("numberp", Fnumberp, Snumberp, 1, 1, 0,
}
```

Looking at it when can gather some information of it, for example it's name is "numberp" and defines a `Fnumberp` function and a static structure `Snumberp`, plus the function only takes one argument `object`. With that in mind we can rewrite now in Rust. With that in mind we can rewrite it now in Rust:
Looking at it when can gather some information of it, for example it's
name is "numberp" and defines a `Fnumberp` function and a static
structure `Snumberp`, plus the function only takes one argument
`object`. With that in mind we can rewrite now in Rust. With that in
mind we can rewrite it now in Rust:

```rust
/// Return t if OBJECT is a number (floating point or integer).
#[lisp_fn(name = "numberp", min = "1")]
#[lisp_fn]
fn numberp(object: LispObject) -> LispObject {
if lisp::deprecated::NUMBERP(object) {
LispObject::constant_t()
} else {
LispObject::constant_nil()
}
LispObject::from_bool(object.is_number())
}
```