Explaining exception handling on Zend VM




❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
<?php
function mydiv($a, $b) {
return $b ? $a / $b : 0;
}
$ php -dopcache.enable_cli=1 -dopcache.opt_debug_level=0x10000 mydiv.php
L0 (2): CV0($a) = RECV 1
L1 (2): CV1($b) = RECV 2
L2 (3): JMPZ CV1($b) L6
L3 (3): T2 = DIV CV0($a) CV1($b)
L4 (3): T3 = QM_ASSIGN T2
L5 (3): JMP L7
L6 (3): T3 = QM_ASSIGN int(0)
L7 (3): RETURN T3
L8 (4): RETURN null
❖
❖
❖
❖
JMPZ CV1($b) L6
T2 = DIV CV0($a) CV1($b)
❖
❖
❖
❖
❖
❖
❖
❖
❖
<?php
function bar() {
$e = new Exception();
throw $e;
try {
throw $e;
} catch (FooException $e) {
throw $e;
} catch (BarException $e) {
return 0;
}
}
❖
L0 (4): V1 = NEW 0 string("Exception")
L1 (4): DO_FCALL
L2 (3): ASSIGN CV0($e) V1
L3 (4): THROW CV0($e)
L4 (6): THROW CV0($e)
L5 (6): JMP L11
L6 (7): CV0($e) = CATCH string("FooException") L9
L7 (8): THROW CV0($e)
L8 (8): JMP L11
L9 (9): CV0($e) = CATCH string("BarException")
L10 (10): RETURN int(0)
L11 (12): RETURN null
EXCEPTION TABLE:
L4, L6, -, -
❖
L0 (4): V1 = NEW 0 string("Exception")
L1 (4): DO_FCALL
L2 (3): ASSIGN CV0($e) V1
L3 (4): THROW CV0($e)
L4 (6): THROW CV0($e)
L5 (6): JMP L11
L6 (7): CV0($e) = CATCH string("FooException") L9
L7 (8): THROW CV0($e)
L8 (8): JMP L11
L9 (9): CV0($e) = CATCH string("BarException")
L10 (10): RETURN int(0)
L11 (12): RETURN null
EXCEPTION TABLE:
L4, L6, -, -
❖
❖
THROW CV0($e)
❖
CV0($e) = CATCH string("FooException") L9
❖
❖
❖
❖
THROW CV0($e)
ZEND_API ZEND_COLD void zend_throw_exception_internal 
(zval *exception) /* {{{ */
{
if (exception != NULL) {
EG(exception) = Z_OBJ_P(exception);
❖
❖
❖
EG(current_execute_data)->opline = EG(exception_op);
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
THROW CV0($e)
❖
❖
❖
❖
L0 (4): V1 = NEW 0 string("Exception")
L1 (4): DO_FCALL
L2 (3): ASSIGN CV0($e) V1
L3 (4): THROW CV0($e)
L4 (6): THROW CV0($e)
L5 (6): JMP L11
L6 (7): CV0($e) = CATCH string("FooException") L9
L7 (8): THROW CV0($e)
L8 (8): JMP L11
L9 (9): CV0($e) = CATCH string("BarException")
L10 (10): RETURN int(0)
L11 (12): RETURN null
EXCEPTION TABLE:
L4, L6, -, -
❖
if (ce != catch_ce) {
if (!catch_ce || !instanceof_function(ce, catch_ce)) {
if (opline->extended_value & ZEND_LAST_CATCH) {
zend_rethrow_exception(execute_data);
HANDLE_EXCEPTION();
}
ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0);
}
}
❖
❖
❖
❖
static zend_always_inline void 
zend_rethrow_exception(zend_execute_data *execute_data)
{
if (EX(opline)->opcode != ZEND_HANDLE_EXCEPTION) {
EG(opline_before_exception) = EX(opline);
EX(opline) = EG(exception_op);
}
}
❖
❖
❖
❖
❖
THROW CV0($e)
❖
HANDLE_EXEPTION
CV0($e) = CATCH string("FooException") L9
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
<?php
function baz() {
try {
throw new Exception();
} finally {
return 0;
}
}
❖
<?php
function baz() {
try {
throw new Exception();
} finally {
return 0;
}
}
❖
❖
❖
L0 (4): V1 = NEW 0 string("Exception")
L1 (4): DO_FCALL
L2 (4): THROW V1
L3 (5): T0 = FAST_CALL L5
L4 (5): JMP L8
L5 (6): DISCARD_EXCEPTION T0
L6 (6): RETURN int(0)
L7 (6): FAST_RET T0
L8 (8): RETURN null
EXCEPTION TABLE:
L0, -, L5, L7
❖
❖ 

❖
❖
❖
❖
<?php
function baz() {
try {
throw new Exception(1);
} finally {
throw new Exception(2);
}
}
❖
<?php
function baz() {
try {
throw new Exception(1);
} finally {
throw new Exception(2);
}
}
❖
❖
try {
baz();
} catch (Exception $e) {
echo $e->getMessage(); // 2
echo $e->getPrevious()->getMessage(); // 1
}
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
❖
Zend VMにおける例外の実装

Zend VMにおける例外の実装