Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
strategy:
matrix:
rust: [stable]
julia: ['1.10', '1.11', '1.12']
julia: ['1.10', '1.11', '1.12', '1.13']
allow-fail: [false]
steps:
- uses: actions/checkout@v4
Expand All @@ -32,7 +32,7 @@ jobs:

- name: Run tests
run: |
julia -e "import Pkg; Pkg.add(url=\"https://github.com/Taaitaaiger/JlrsCore.jl\", rev=\"3fc603a1cf8d4e0c16a57027f15d00232f47ac4a\")"
julia -e "import Pkg; Pkg.add(url=\"https://github.com/Taaitaaiger/JlrsCore.jl\", rev=\"0f1b7cba4eeedae260cb48374716f3cd551fc8c0\")"
export JLRS_JULIA_DIR="$(dirname $(dirname $(which julia)))"
export LD_LIBRARY_PATH="${JLRS_JULIA_DIR}/lib:${LD_LIBRARY_PATH}"
./generate_tests.py
Expand Down
4 changes: 2 additions & 2 deletions generate_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def cargo_toml_bin_template(jlrs_path):
panic = "abort"

[dependencies]
jlrs = {{version = "0.23", {jlrs_path}features = ["full", "ccall"]}}"""
jlrs = {{version = "0.24", {jlrs_path}features = ["full", "ccall"]}}"""


def cargo_toml_lib_template(name, jlrs_path):
Expand All @@ -63,7 +63,7 @@ def cargo_toml_lib_template(name, jlrs_path):
crate-type = ["cdylib"]

[dependencies]
jlrs = {{ {jlrs_path}version = "0.23", features = ["jlrs-derive", "ccall", "complex"] }}"""
jlrs = {{ {jlrs_path}version = "0.24", features = ["jlrs-derive", "ccall", "complex"] }}"""


def bin_fragment(bin_name):
Expand Down
2 changes: 1 addition & 1 deletion src/02-basics/project-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ panic = "abort"
panic = "abort"

[dependencies]
jlrs = {version = "0.23", features = ["local-rt"]}
jlrs = {version = "0.24", features = ["local-rt"]}
```

If Julia has been installed and we've configured our environment according to the steps in the [dependency chapter], building and running should succeed:
Expand Down
63 changes: 33 additions & 30 deletions src/03-memory-management/local-targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,36 +92,39 @@ where
fn main() {
let handle = Builder::new().start_local().expect("cannot init Julia");

handle.unsized_local_scope(2, |mut frame| {
let mut output = frame.output();
let mut reusable_slot = frame.reusable_slot();

{
let result = add(&mut output, 1, 2).expect("could not add numbers");
let unboxed = result.unbox::<u8>().expect("cannot unbox as u8");
assert_eq!(unboxed, 3);
}

{
let result = add(output, 1, 2).expect("could not add numbers");
let unboxed = result.unbox::<u8>().expect("cannot unbox as u8");
assert_eq!(unboxed, 3);
}

{
let result = add(&mut reusable_slot, 1, 2).expect("could not add numbers");

// Safety: result is rooted until we use `reusable_slot` again
let unboxed = unsafe { result.as_value() }.unbox::<u8>().expect("cannot unbox as u8");
assert_eq!(unboxed, 3);
}

{
let result = add(reusable_slot, 1, 2).expect("could not add numbers");
let unboxed = result.unbox::<u8>().expect("cannot unbox as u8");
assert_eq!(unboxed, 3);
}
})
// Safety: we don't call any functions which can throw exceptions
unsafe {
handle.unsized_local_scope(2, |mut frame| {
let mut output = frame.output();
let mut reusable_slot = frame.reusable_slot();

{
let result = add(&mut output, 1, 2).expect("could not add numbers");
let unboxed = result.unbox::<u8>().expect("cannot unbox as u8");
assert_eq!(unboxed, 3);
}

{
let result = add(output, 1, 2).expect("could not add numbers");
let unboxed = result.unbox::<u8>().expect("cannot unbox as u8");
assert_eq!(unboxed, 3);
}

{
let result = add(&mut reusable_slot, 1, 2).expect("could not add numbers");

// Safety: result is rooted until we use `reusable_slot` again
let unboxed = unsafe { result.as_value() }.unbox::<u8>().expect("cannot unbox as u8");
assert_eq!(unboxed, 3);
}

{
let result = add(reusable_slot, 1, 2).expect("could not add numbers");
let unboxed = result.unbox::<u8>().expect("cannot unbox as u8");
assert_eq!(unboxed, 3);
}
})
}
}
```
<!-- DOCTEST END -->
2 changes: 1 addition & 1 deletion src/06-exception-handling/exception-handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Many functions in Julia can throw exceptions, including low-level functions exposed by jlrs. Examples include calling a Julia function with incorrect arguments and trying to allocate a ridiculously-sized array. These functions are typically exposed twice: as a function that catches exceptions, and one that doesn't.

The function that doesn't catch the exception is always unsafe. Julia exceptions are implemented with `longjmp`, when an exception is thrown control flow jumps to the nearest enclosing catch block, so we must guarantee we don't jump over any pending drops. We can call arbitrary functions in a try-block with a custom exception handler with the `catch_exceptions` function, but this remains unsafe because we still have to guarantee we don't jump over any drops. It's fine to jump out of some deeply nested scope as long as any frame that is jumped over is a ["Plain Old Frame"].
The function that doesn't catch the exception is always unsafe. Julia exceptions are implemented with `longjmp`, when an exception is thrown control flow jumps to the nearest enclosing catch block, so we must guarantee we don't jump over any pending drops. We can call arbitrary functions in a try-block with a custom exception handler with the `catch_exceptions` function, but this remains unsafe because we still have to guarantee we don't jump over any drops. It's fine to jump out of some deeply nested scopes as long as any frame that is jumped over is a ["Plain Old Frame"]. The local_scope methods are _not_ exception-safe; they use drop internally. If you want to create a new scope in the try-callback, use `exception_safe_scope` or `unsized_local_scope`.

If an exception is thrown and there is no handler available, Julia aborts the process.

Expand Down
2 changes: 1 addition & 1 deletion src/11-julia-module/functions/typed-layouts.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ If the layout of an immutable type has one or more elided type parameters, the l
<!-- LIBTEST START -->
```rust,ignore
use jlrs::{
data::{layout::typed_layout::TypedLayout, types::construct_type::ConstantBool},
data::{layout::typed_layout::TypedLayout, types::construct_type::constants::ConstantBool},
prelude::*,
};

Expand Down
19 changes: 17 additions & 2 deletions src/11-julia-module/julia-module.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ panic = "abort"
crate-type = ["cdylib"]

[dependencies]
jlrs = { version = "0.23", features = ["jlrs-derive", "ccall"] }
jlrs = { version = "0.24", features = ["jlrs-derive", "ccall"] }
```

It's important that we don't enable any runtime features like `local-rt` when we build a dynamic library.
Expand All @@ -45,7 +45,7 @@ The macro is transformed into a single function, `julia_module_tutorial_init_fn`
module JuliaModuleTutorial
using JlrsCore.Wrap

@wrapmodule("/path/to/libjulia_module_tutorial", :julia_module_tutorial_init_fn)
@wrapmodule(() -> "/path/to/libjulia_module_tutorial", :julia_module_tutorial_init_fn)

function __init__()
@initjlrs
Expand All @@ -54,3 +54,18 @@ end
```

This is all the Julia code we'll need to write, the `@wrapmodule` macro generates the content of the module. For the sake of brevity, code samples in the following sections will write `module JuliaModuleTutorial ... end` as a shorthand for this module definition.

If the wrapped code uses data from other packages, e.g. types from `StaticArrays`, these packages must be specified in `@initjlrs`:

```julia
module JuliaModuleTutorial
using JlrsCore.Wrap
using StaticArrays

@wrapmodule(() -> "/path/to/libjulia_module_tutorial", :julia_module_tutorial_init_fn)

function __init__()
@initjlrs [StaticArrays]
end
end
```
2 changes: 0 additions & 2 deletions src/11-julia-module/yggdrasil-and-jlrs/yggdrasil-and-jlrs.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,3 @@ The main differences with the recipe for a crate that doesn't depend on jlrs are
- The supported versions of Julia are set.
- Supported platforms are acquired via `libjulia_platforms`, not `supported_platforms`.
- `libjulia_jll` is added to the dependencies as a build dependency.

[#2942]: https://github.com/JuliaLang/Pkg.jl/issues/2942
Loading