diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index baaa6b3..ac97037 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -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 @@ -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 diff --git a/generate_tests.py b/generate_tests.py index 5494bc7..9618941 100755 --- a/generate_tests.py +++ b/generate_tests.py @@ -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): @@ -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): diff --git a/src/02-basics/project-setup.md b/src/02-basics/project-setup.md index 4c11948..1d32134 100644 --- a/src/02-basics/project-setup.md +++ b/src/02-basics/project-setup.md @@ -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: diff --git a/src/03-memory-management/local-targets.md b/src/03-memory-management/local-targets.md index 5a65da9..c56ded7 100644 --- a/src/03-memory-management/local-targets.md +++ b/src/03-memory-management/local-targets.md @@ -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::().expect("cannot unbox as u8"); - assert_eq!(unboxed, 3); - } - - { - let result = add(output, 1, 2).expect("could not add numbers"); - let unboxed = result.unbox::().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::().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::().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::().expect("cannot unbox as u8"); + assert_eq!(unboxed, 3); + } + + { + let result = add(output, 1, 2).expect("could not add numbers"); + let unboxed = result.unbox::().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::().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::().expect("cannot unbox as u8"); + assert_eq!(unboxed, 3); + } + }) + } } ``` diff --git a/src/06-exception-handling/exception-handling.md b/src/06-exception-handling/exception-handling.md index 3348ee0..86a2abd 100644 --- a/src/06-exception-handling/exception-handling.md +++ b/src/06-exception-handling/exception-handling.md @@ -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. diff --git a/src/11-julia-module/functions/typed-layouts.md b/src/11-julia-module/functions/typed-layouts.md index fb27672..7fa463d 100644 --- a/src/11-julia-module/functions/typed-layouts.md +++ b/src/11-julia-module/functions/typed-layouts.md @@ -5,7 +5,7 @@ If the layout of an immutable type has one or more elided type parameters, the l ```rust,ignore use jlrs::{ - data::{layout::typed_layout::TypedLayout, types::construct_type::ConstantBool}, + data::{layout::typed_layout::TypedLayout, types::construct_type::constants::ConstantBool}, prelude::*, }; diff --git a/src/11-julia-module/julia-module.md b/src/11-julia-module/julia-module.md index c3aa528..aa15d70 100644 --- a/src/11-julia-module/julia-module.md +++ b/src/11-julia-module/julia-module.md @@ -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. @@ -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 @@ -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 +``` diff --git a/src/11-julia-module/yggdrasil-and-jlrs/yggdrasil-and-jlrs.md b/src/11-julia-module/yggdrasil-and-jlrs/yggdrasil-and-jlrs.md index 4034834..29632bb 100644 --- a/src/11-julia-module/yggdrasil-and-jlrs/yggdrasil-and-jlrs.md +++ b/src/11-julia-module/yggdrasil-and-jlrs/yggdrasil-and-jlrs.md @@ -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