diff --git a/src/ephp_class.erl b/src/ephp_class.erl index 4c85767d..eeadd6a6 100644 --- a/src/ephp_class.erl +++ b/src/ephp_class.erl @@ -28,6 +28,8 @@ init_static_value/5, set_static/4, + instance_of/2, + register_class/4, set_alias/3, instance/5, @@ -116,6 +118,14 @@ instance(Ref, LocalCtx, GlobalCtx, RawClassName, Line) -> {RawClassName}}) end. +instance_of(#reg_instance{class = #class{name = Name}}, Name) -> + true; +instance_of(#reg_instance{class = #class{extends = Extends, + implements = Impl}}, Name) -> + lists:member(Name, Extends) orelse lists:member(Name, Impl); +instance_of(_, _) -> + false. + initialize_class(#class{static_context=Ctx, attrs=Attrs}) -> lists:foreach(fun (#class_attr{type=static, name=Name, init_value=RawVal}) -> diff --git a/src/ephp_interpr.erl b/src/ephp_interpr.erl index 4abe9a28..9e7bb75e 100644 --- a/src/ephp_interpr.erl +++ b/src/ephp_interpr.erl @@ -295,7 +295,7 @@ run_depth(Context, #constant{type=define,name=Name,value=Expr,line=Line}, ephp_context:register_const(Context, Name, Value), false; -run_depth(Context, #constant{line=Line}, false, Cover) -> +run_depth(Context, #constant{line = Line}, false, Cover) -> ok = ephp_cover:store(Cover, constant, Context, Line), false; @@ -307,6 +307,29 @@ run_depth(Context, {silent, Statement}, false, Cover) -> run_depth(_Context, die, false, _Cover) -> throw(die); +run_depth(Context, #try_catch{code_block = Code, catches = Catches, + finally = Finally, line = Line}, false, Cover) -> + ok = ephp_cover:store(Cover, try_catch, Context, Line), + try + run(Context, #eval{statements = Code}, Cover) + catch + throw:Exception when ?IS_OBJECT(Exception) -> + Ret = lists:foldl(fun + (#catch_block{} = CatchBlock, throw) -> + run_catch(Context, CatchBlock, Exception, Finally, Cover); + (#catch_block{}, Ret) -> + Ret + end, throw, Catches), + case Ret of + throw -> + run_finally(Context, Finally, Cover), + throw(Exception); + _ -> + Ret + end + end, + run_finally(Context, Finally, Cover); + run_depth(_Context, Statement, false, _Cover) -> ephp_error:error({error, eunknownst, undefined, ?E_CORE_ERROR, Statement}), break; @@ -314,11 +337,41 @@ run_depth(_Context, Statement, false, _Cover) -> run_depth(_Context, _Statement, Break, _Cover) -> Break. +-spec exit_cond(flow_status()) -> flow_status(). + exit_cond({return, Ret}) -> {return, Ret}; exit_cond({break, 0}) -> false; exit_cond({break, N}) -> {break, N-1}; exit_cond(false) -> false. +-spec run_finally(context(), statements(), Cover :: boolean()) -> flow_status(). + +run_finally(_Context, [], _Cover) -> + false; +run_finally(Context, Finally, Cover) -> + run(Context, #eval{statements = Finally}, Cover). + +-spec run_catch(context(), catch_block(), Exception :: binary(), + Finally :: statements(), Cover :: boolean()) -> throw | + flow_status(). + +run_catch(Context, + #catch_block{exception = #variable{data_type = Catch} = Var, + code_block = CatchCode}, + Exception, Finally, Cover) -> + case ephp_class:instance_of(Exception, Catch) of + true -> + ephp_context:set(Context, Var, Exception), + try + run(Context, #eval{statements = CatchCode}, Cover) + catch throw:NewException when ?IS_OBJECT(NewException) -> + run_finally(Context, Finally, Cover), + throw(NewException) + end; + false -> + throw + end. + -spec run_loop( PrePost :: (pre | post), Context :: context(), diff --git a/test/code/test_try_catch.out b/test/code/test_try_catch.out new file mode 100644 index 00000000..dafe04f7 --- /dev/null +++ b/test/code/test_try_catch.out @@ -0,0 +1,2 @@ +hey! +Exception: it's broken diff --git a/test/code/test_try_catch.php b/test/code/test_try_catch.php new file mode 100644 index 00000000..6871aa23 --- /dev/null +++ b/test/code/test_try_catch.php @@ -0,0 +1,10 @@ +getMessage() . "\n"; +} + diff --git a/test/code/test_try_catch_finally.out b/test/code/test_try_catch_finally.out new file mode 100644 index 00000000..0db5e787 --- /dev/null +++ b/test/code/test_try_catch_finally.out @@ -0,0 +1,10 @@ +hey! +Exception: it's broken +and ends first one +hey again! +should this appears? + +Fatal error: Uncaught exception 'Exception' with message 'Another exception in chain!' in {{CWD}}/test/code/test_try_catch_finally.php:18 +Stack trace: +#0 {main} + thrown in {{CWD}}/test/code/test_try_catch_finally.php on line 18 diff --git a/test/code/test_try_catch_finally.php b/test/code/test_try_catch_finally.php new file mode 100644 index 00000000..81137139 --- /dev/null +++ b/test/code/test_try_catch_finally.php @@ -0,0 +1,21 @@ +getMessage() . "\n"; +} finally { + print "and ends first one\n"; +} + +try { + print "hey again!\n"; + throw new Exception("it's broken... again"); + print "no way!\n"; +} catch (Exception $e) { + throw new Exception("Another exception in chain!"); +} finally { + print "should this appears?\n"; +} diff --git a/test/code/test_try_finally.out b/test/code/test_try_finally.out new file mode 100644 index 00000000..912f2593 --- /dev/null +++ b/test/code/test_try_finally.out @@ -0,0 +1,2 @@ +hello world! +ok diff --git a/test/code/test_try_finally.php b/test/code/test_try_finally.php new file mode 100644 index 00000000..e971b856 --- /dev/null +++ b/test/code/test_try_finally.php @@ -0,0 +1,7 @@ +