Skip to content

Commit 7b095da

Browse files
committed
Check MATLAB exceptions
1 parent ddf3968 commit 7b095da

File tree

4 files changed

+94
-8
lines changed

4 files changed

+94
-8
lines changed

src/MATLAB.jl

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,7 @@ else
4343
)
4444
end
4545

46-
# exceptions
47-
struct MEngineError <: Exception
48-
message::String
49-
end
50-
46+
include("exceptions.jl")
5147
include("init.jl") # initialize Refs
5248
include("mxarray.jl")
5349
include("matfile.jl")

src/engine.jl

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,13 @@ mutable struct MSession
3535
ptr::Ptr{Cvoid}
3636
buffer::Vector{UInt8}
3737
bufptr::Ptr{UInt8}
38+
check_exceptions::Bool
3839

39-
function MSession(bufsize::Integer=default_output_buffer_size; flags=default_startflag)
40+
function MSession(
41+
bufsize::Integer=default_output_buffer_size;
42+
flags=default_startflag,
43+
check_exceptions::Bool=true,
44+
)
4045
if Sys.iswindows()
4146
assign_persistent_msession()
4247
end
@@ -72,7 +77,7 @@ mutable struct MSession
7277
bufptr = convert(Ptr{UInt8}, C_NULL)
7378
end
7479

75-
self = new(ep, buf, bufptr)
80+
self = new(ep, buf, bufptr, check_exceptions)
7681
finalizer(release, self)
7782
return self
7883
end
@@ -101,6 +106,13 @@ function close(session::MSession)
101106
return nothing
102107
end
103108

109+
has_exception_check_enabled(session::MSession=get_default_msession()) =
110+
session.check_exceptions
111+
disable_exception_check!(session::MSession=get_default_msession()) =
112+
(session.check_exceptions = false; nothing)
113+
enable_exception_check!(session::MSession=get_default_msession()) =
114+
(session.check_exceptions = true; nothing)
115+
104116
# default session
105117

106118
const default_msession_ref = Ref{MSession}()
@@ -153,7 +165,7 @@ end
153165
#
154166
###########################################################
155167

156-
function eval_string(session::MSession, stmt::String)
168+
function _eval_string(session::MSession, stmt::String)
157169
# evaluate a MATLAB statement in a given MATLAB session
158170
ret = ccall(eng_eval_string[], Cint, (Ptr{Cvoid}, Ptr{UInt8}), session, stmt)
159171
ret != 0 && throw(MEngineError("invalid engine session (err = $ret)"))
@@ -168,6 +180,13 @@ function eval_string(session::MSession, stmt::String)
168180
return nothing
169181
end
170182

183+
function eval_string(session::MSession, stmt::String)
184+
_eval_string(session, stmt)
185+
if session.check_exceptions
186+
check_and_clear_last_exception(session)
187+
end
188+
end
189+
171190
eval_string(stmt::String) = eval_string(get_default_msession(), stmt)
172191

173192
function put_variable(session::MSession, name::Symbol, v::MxArray)
@@ -208,6 +227,33 @@ get_mvariable(name::Symbol) = get_mvariable(get_default_msession(), name)
208227
get_variable(name::Symbol) = jvalue(get_mvariable(name))
209228
get_variable(name::Symbol, kind) = jvalue(get_mvariable(name), kind)
210229

230+
"""
231+
check_and_clear_last_exception(session::MSession)
232+
233+
Checks if an exception has been thrown in the MATLAB session by checking the `MException.last` variable.
234+
If it is not empty, it throws a `MatlabException` with the message and identifier of the last exception.
235+
In any case, it clears the `MException.last` variable.
236+
"""
237+
function check_and_clear_last_exception(session::MSession)
238+
exception_check_code = """
239+
matlab_exception_jl_message = MException.last.message;
240+
matlab_exception_jl_identifier = MException.last.identifier;
241+
MException.last('reset');
242+
"""
243+
_eval_string(session, exception_check_code)
244+
message = jvalue(get_mvariable(session, :matlab_exception_jl_message))
245+
identifier = jvalue(get_mvariable(session, :matlab_exception_jl_identifier))
246+
247+
if !isempty(identifier)
248+
throw(MatlabException(identifier, message))
249+
end
250+
251+
_eval_string(
252+
session,
253+
"clear matlab_exception_jl_message matlab_exception_jl_identifier;",
254+
)
255+
end
256+
211257
###########################################################
212258
#
213259
# macro to simplify syntax

src/exceptions.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
struct MEngineError <: Exception
2+
message::String
3+
end
4+
5+
"""
6+
MEngineError(message::String)
7+
8+
Exception thrown by MATLAB, e.g. due to syntax errors in the code
9+
passed to `eval_string` or `mat"..."`.
10+
"""
11+
struct MatlabException <: Exception
12+
identifier::String
13+
message::String
14+
end

test/matstr.jl

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,33 @@ $d ...
7070
# Test strings with =
7171
text = "hello = world"
7272
@test mat"strfind($text, 'o = w')" == 5
73+
74+
@testset "Propagate Matlab Exceptions" begin
75+
76+
# Checks should be enabled by default
77+
@test MATLAB.has_exception_check_enabled() == true
78+
79+
# Test invalid command
80+
@test_throws MATLAB.MatlabException mat"invalid_command"
81+
82+
# Test invalid assignment
83+
@test_throws MATLAB.MatlabException mat"1 = 2"
84+
85+
# Test invalid command within a block
86+
@test_throws MATLAB.MatlabException mat"""
87+
xyz = 1 + 2;
88+
invalid_command;
89+
abc = 2 * xyz;
90+
"""
91+
92+
# Disable Checks
93+
MATLAB.disable_exception_check!()
94+
@test MATLAB.has_exception_check_enabled() == false
95+
96+
# Test invalid command
97+
try
98+
mat"invalid_command"
99+
catch ex
100+
@test false # should not throw an exception
101+
end
102+
end

0 commit comments

Comments
 (0)