diff --git a/src/MATLAB.jl b/src/MATLAB.jl index 40408cb..f3323f4 100644 --- a/src/MATLAB.jl +++ b/src/MATLAB.jl @@ -43,11 +43,7 @@ else ) end -# exceptions -struct MEngineError <: Exception - message::String -end - +include("exceptions.jl") include("init.jl") # initialize Refs include("mxarray.jl") include("matfile.jl") diff --git a/src/engine.jl b/src/engine.jl index f42b6a1..c92c32b 100644 --- a/src/engine.jl +++ b/src/engine.jl @@ -20,8 +20,13 @@ mutable struct MSession ptr::Ptr{Cvoid} buffer::Vector{UInt8} bufptr::Ptr{UInt8} + check_exceptions::Bool - function MSession(bufsize::Integer=default_output_buffer_size; flags=default_startflag) + function MSession( + bufsize::Integer=default_output_buffer_size; + flags=default_startflag, + check_exceptions::Bool=true, + ) if Sys.iswindows() assign_persistent_msession() end @@ -56,7 +61,7 @@ mutable struct MSession bufptr = convert(Ptr{UInt8}, C_NULL) end - self = new(ep, buf, bufptr) + self = new(ep, buf, bufptr, check_exceptions) finalizer(release, self) return self end @@ -85,6 +90,13 @@ function close(session::MSession) return nothing end +has_exception_check_enabled(session::MSession=get_default_msession()) = + session.check_exceptions +disable_exception_check!(session::MSession=get_default_msession()) = + (session.check_exceptions = false; nothing) +enable_exception_check!(session::MSession=get_default_msession()) = + (session.check_exceptions = true; nothing) + # default session const default_msession_ref = Ref{MSession}() @@ -137,7 +149,7 @@ end # ########################################################### -function eval_string(session::MSession, stmt::String) +function _eval_string(session::MSession, stmt::String) # evaluate a MATLAB statement in a given MATLAB session ret = ccall(eng_eval_string[], Cint, (Ptr{Cvoid}, Ptr{UInt8}), session, stmt) ret != 0 && throw(MEngineError("invalid engine session (err = $ret)")) @@ -152,6 +164,13 @@ function eval_string(session::MSession, stmt::String) return nothing end +function eval_string(session::MSession, stmt::String) + _eval_string(session, stmt) + if session.check_exceptions + check_and_clear_last_exception(session) + end +end + eval_string(stmt::String) = eval_string(get_default_msession(), stmt) function put_variable(session::MSession, name::Symbol, v::MxArray) @@ -192,6 +211,33 @@ get_mvariable(name::Symbol) = get_mvariable(get_default_msession(), name) get_variable(name::Symbol) = jvalue(get_mvariable(name)) get_variable(name::Symbol, kind) = jvalue(get_mvariable(name), kind) +""" + check_and_clear_last_exception(session::MSession) + +Checks if an exception has been thrown in the MATLAB session by checking the `MException.last` variable. +If it is not empty, it throws a `MatlabException` with the message and identifier of the last exception. +In any case, it clears the `MException.last` variable. +""" +function check_and_clear_last_exception(session::MSession) + exception_check_code = """ + matlab_exception_jl_message = MException.last.message; + matlab_exception_jl_identifier = MException.last.identifier; + MException.last('reset'); + """ + _eval_string(session, exception_check_code) + message = jvalue(get_mvariable(session, :matlab_exception_jl_message)) + identifier = jvalue(get_mvariable(session, :matlab_exception_jl_identifier)) + + if !isempty(identifier) + throw(MatlabException(identifier, message)) + end + + _eval_string( + session, + "clear matlab_exception_jl_message matlab_exception_jl_identifier;", + ) +end + ########################################################### # # macro to simplify syntax diff --git a/src/exceptions.jl b/src/exceptions.jl new file mode 100644 index 0000000..84013fe --- /dev/null +++ b/src/exceptions.jl @@ -0,0 +1,14 @@ +struct MEngineError <: Exception + message::String +end + +""" + MEngineError(message::String) + +Exception thrown by MATLAB, e.g. due to syntax errors in the code +passed to `eval_string` or `mat"..."`. +""" +struct MatlabException <: Exception + identifier::String + message::String +end diff --git a/test/matstr.jl b/test/matstr.jl index 9733445..79451e2 100644 --- a/test/matstr.jl +++ b/test/matstr.jl @@ -70,3 +70,33 @@ $d ... # Test strings with = text = "hello = world" @test mat"strfind($text, 'o = w')" == 5 + +@testset "Propagate Matlab Exceptions" begin + + # Checks should be enabled by default + @test MATLAB.has_exception_check_enabled() == true + + # Test invalid command + @test_throws MATLAB.MatlabException mat"invalid_command" + + # Test invalid assignment + @test_throws MATLAB.MatlabException mat"1 = 2" + + # Test invalid command within a block + @test_throws MATLAB.MatlabException mat""" + xyz = 1 + 2; + invalid_command; + abc = 2 * xyz; + """ + + # Disable Checks + MATLAB.disable_exception_check!() + @test MATLAB.has_exception_check_enabled() == false + + # Test invalid command + try + mat"invalid_command" + catch ex + @test false # should not throw an exception + end +end