mirror of
https://github.com/php/php-src.git
synced 2026-03-26 01:02:25 +01:00
- Initial support for exceptions.
This commit is contained in:
@@ -81,6 +81,7 @@ void zend_init_compiler_data_structures(TSRMLS_D)
|
||||
CG(handle_op_arrays) = 1;
|
||||
CG(in_compilation) = 0;
|
||||
init_compiler_declarables(TSRMLS_C);
|
||||
CG(throw_list) = NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -758,6 +759,8 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n
|
||||
|
||||
zend_stack_push(&CG(foreach_copy_stack), (void *) &switch_entry.cond, sizeof(znode));
|
||||
}
|
||||
function_token->throw_list = CG(throw_list);
|
||||
CG(throw_list) = NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -771,6 +774,8 @@ void zend_do_end_function_declaration(znode *function_token TSRMLS_DC)
|
||||
/* Pop the switch and foreach seperators */
|
||||
zend_stack_del_top(&CG(switch_cond_stack));
|
||||
zend_stack_del_top(&CG(foreach_copy_stack));
|
||||
|
||||
CG(throw_list) = function_token->throw_list;
|
||||
}
|
||||
|
||||
|
||||
@@ -919,7 +924,16 @@ void zend_do_end_function_call(znode *function_name, znode *result, znode *argum
|
||||
opline->result.op_type = IS_VAR;
|
||||
*result = opline->result;
|
||||
SET_UNUSED(opline->op2);
|
||||
opline->op2.u.constant.value.lval = is_method;
|
||||
|
||||
// Check how much this is really needed
|
||||
//opline->op2.u.constant.value.lval = is_method;
|
||||
if (CG(throw_list) != NULL) {
|
||||
long op_number = get_next_op_number(CG(active_op_array))-1;
|
||||
zend_llist_add_element(CG(throw_list), &op_number);
|
||||
} else {
|
||||
opline->op2.u.opline_num = -1;
|
||||
}
|
||||
|
||||
zend_stack_del_top(&CG(function_call_stack));
|
||||
opline->extended_value = argument_list->u.constant.value.lval;
|
||||
}
|
||||
@@ -1089,6 +1103,74 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC)
|
||||
}
|
||||
|
||||
|
||||
void zend_do_try(znode *try_token CLS_DC)
|
||||
{
|
||||
try_token->throw_list = (void *) CG(throw_list);
|
||||
CG(throw_list) = (zend_llist *) emalloc(sizeof(zend_llist));
|
||||
zend_llist_init(CG(throw_list), sizeof(long), NULL, 0);
|
||||
// Initialize try backpatch list used to backpatch throw, do_fcall
|
||||
}
|
||||
|
||||
static void throw_list_applier(long *opline_num, long *catch_opline)
|
||||
{
|
||||
zend_op *opline;
|
||||
CLS_FETCH(); // Pass this by argument
|
||||
|
||||
opline = &CG(active_op_array)->opcodes[*opline_num];
|
||||
|
||||
// Backpatch the opline of the catch statement
|
||||
switch (opline->opcode) {
|
||||
case ZEND_DO_FCALL:
|
||||
case ZEND_DO_FCALL_BY_NAME:
|
||||
case ZEND_THROW:
|
||||
opline->op2.u.opline_num = *catch_opline;
|
||||
break;
|
||||
default:
|
||||
zend_error(E_ERROR, "Bad opcode in throw list");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void zend_do_begin_catch(znode *try_token, znode *catch_var CLS_DC)
|
||||
{
|
||||
long catch_op_number = get_next_op_number(CG(active_op_array));
|
||||
zend_op *opline;
|
||||
|
||||
opline = get_next_op(CG(active_op_array) CLS_CC);
|
||||
opline->opcode = ZEND_CATCH;
|
||||
opline->op1 = *catch_var;
|
||||
SET_UNUSED(opline->op2);
|
||||
|
||||
zend_llist_apply_with_argument(CG(throw_list), throw_list_applier, &catch_op_number);
|
||||
zend_llist_destroy(CG(throw_list));
|
||||
efree(CG(throw_list));
|
||||
CG(throw_list) = (void *) try_token->throw_list;
|
||||
|
||||
try_token->u.opline_num = catch_op_number;
|
||||
}
|
||||
|
||||
void zend_do_end_catch(znode *try_token CLS_DC)
|
||||
{
|
||||
CG(active_op_array)->opcodes[try_token->u.opline_num].op2.u.opline_num = get_next_op_number(CG(active_op_array));
|
||||
}
|
||||
|
||||
void zend_do_throw(znode *expr CLS_DC)
|
||||
{
|
||||
zend_op *opline;
|
||||
long throw_op_number = get_next_op_number(CG(active_op_array));
|
||||
|
||||
opline = get_next_op(CG(active_op_array) CLS_CC);
|
||||
opline->opcode = ZEND_THROW;
|
||||
opline->op1 = *expr;
|
||||
SET_UNUSED(opline->op2);
|
||||
|
||||
if (CG(throw_list) != NULL) {
|
||||
zend_llist_add_element(CG(throw_list), &throw_op_number);
|
||||
} else {
|
||||
opline->op2.u.opline_num = -1;
|
||||
}
|
||||
}
|
||||
|
||||
ZEND_API void function_add_ref(zend_function *function)
|
||||
{
|
||||
if (function->type == ZEND_USER_FUNCTION) {
|
||||
@@ -1778,6 +1860,7 @@ void zend_do_shell_exec(znode *result, znode *cmd TSRMLS_DC)
|
||||
opline->extended_value = ZEND_DO_FCALL;
|
||||
SET_UNUSED(opline->op2);
|
||||
|
||||
// FIXME: exception support not added to this op2
|
||||
opline = get_next_op(CG(active_op_array) TSRMLS_CC);
|
||||
opline->opcode = ZEND_DO_FCALL;
|
||||
opline->result.u.var = get_temporary_variable(CG(active_op_array));
|
||||
|
||||
@@ -48,6 +48,7 @@ typedef struct _zend_op_array zend_op_array;
|
||||
|
||||
typedef struct _znode {
|
||||
int op_type;
|
||||
zend_llist *throw_list; // Try and save this space later on
|
||||
union {
|
||||
zval constant;
|
||||
|
||||
@@ -280,6 +281,12 @@ void zend_do_begin_dynamic_function_call(znode *function_name TSRMLS_DC);
|
||||
void zend_do_begin_class_member_function_call(znode *class_name, znode *function_name TSRMLS_DC);
|
||||
void zend_do_end_function_call(znode *function_name, znode *result, znode *argument_list, int is_method, int is_dynamic_fcall TSRMLS_DC);
|
||||
void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC);
|
||||
|
||||
void zend_do_try(znode *try_token CLS_DC);
|
||||
void zend_do_begin_catch(znode *try_token, znode *catch_var CLS_DC);
|
||||
void zend_do_end_catch(znode *try_token CLS_DC);
|
||||
void zend_do_throw(znode *expr CLS_DC);
|
||||
|
||||
ZEND_API int do_bind_function_or_class(zend_op *opline, HashTable *function_table, HashTable *class_table, int compile_time);
|
||||
void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce);
|
||||
void zend_do_early_binding(TSRMLS_D);
|
||||
@@ -530,6 +537,9 @@ int zendlex(znode *zendlval TSRMLS_DC);
|
||||
|
||||
#define ZEND_SEND_VAR_NO_REF 106
|
||||
|
||||
#define ZEND_CATCH 107
|
||||
#define ZEND_THROW 108
|
||||
|
||||
/* end of block */
|
||||
|
||||
|
||||
|
||||
@@ -989,6 +989,11 @@ static int zend_check_symbol(zval **pz TSRMLS_DC)
|
||||
opline++; \
|
||||
continue;
|
||||
|
||||
#define RETURN_FROM_EXECUTE_LOOP() \
|
||||
free_alloca(Ts); \
|
||||
EG(in_execution) = original_in_execution; \
|
||||
return;
|
||||
|
||||
typedef struct _object_info {
|
||||
zval *ptr;
|
||||
} object_info;
|
||||
@@ -1633,11 +1638,14 @@ do_fcall_common:
|
||||
zend_execute(EG(active_op_array) TSRMLS_CC);
|
||||
|
||||
if (return_value_used && !Ts[opline->result.u.var].var.ptr) {
|
||||
ALLOC_ZVAL(Ts[opline->result.u.var].var.ptr);
|
||||
INIT_ZVAL(*Ts[opline->result.u.var].var.ptr);
|
||||
if (!EG(exception)) {
|
||||
ALLOC_ZVAL(Ts[opline->result.u.var].var.ptr);
|
||||
INIT_ZVAL(*Ts[opline->result.u.var].var.ptr);
|
||||
}
|
||||
} else if (!return_value_used && Ts[opline->result.u.var].var.ptr) {
|
||||
zval_ptr_dtor(&Ts[opline->result.u.var].var.ptr);
|
||||
}
|
||||
|
||||
EG(opline_ptr) = &opline;
|
||||
EG(active_op_array) = op_array;
|
||||
EG(return_value_ptr_ptr)=original_return_value;
|
||||
@@ -1666,7 +1674,16 @@ do_fcall_common:
|
||||
function_state.function = (zend_function *) op_array;
|
||||
EG(function_state_ptr) = &function_state;
|
||||
zend_ptr_stack_clear_multiple(TSRMLS_C);
|
||||
}
|
||||
|
||||
if (EG(exception)) {
|
||||
if (opline->op2.u.opline_num == -1) {
|
||||
RETURN_FROM_EXECUTE_LOOP();
|
||||
} else {
|
||||
opline = &op_array->opcodes[opline->op2.u.opline_num];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
NEXT_OPCODE();
|
||||
case ZEND_RETURN: {
|
||||
zval *retval_ptr;
|
||||
@@ -1703,11 +1720,43 @@ do_fcall_common:
|
||||
(*EG(return_value_ptr_ptr))->is_ref = 0;
|
||||
}
|
||||
}
|
||||
free_alloca(Ts);
|
||||
EG(in_execution) = original_in_execution;
|
||||
return;
|
||||
RETURN_FROM_EXECUTE_LOOP();
|
||||
}
|
||||
break;
|
||||
case ZEND_THROW:
|
||||
{
|
||||
zval *value;
|
||||
zval *exception;
|
||||
|
||||
value = get_zval_ptr(&opline->op1, Ts, &EG(free_op1), BP_VAR_R);
|
||||
|
||||
// Not sure if a complete copy is what we want here
|
||||
MAKE_STD_ZVAL(exception);
|
||||
*exception = *value;
|
||||
if (!EG(free_op1)) {
|
||||
zval_copy_ctor(exception);
|
||||
}
|
||||
INIT_PZVAL(exception);
|
||||
EG(exception) = exception;
|
||||
|
||||
if (opline->op2.u.opline_num == -1) {
|
||||
RETURN_FROM_EXECUTE_LOOP();
|
||||
} else {
|
||||
opline = &op_array->opcodes[opline->op2.u.opline_num];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
NEXT_OPCODE();
|
||||
case ZEND_CATCH:
|
||||
// Check if this is really an exception, if not, jump over code
|
||||
if (EG(exception) == NULL) {
|
||||
opline = &op_array->opcodes[opline->op2.u.opline_num];
|
||||
continue;
|
||||
}
|
||||
zend_hash_update(EG(active_symbol_table), opline->op1.u.constant.value.str.val,
|
||||
opline->op1.u.constant.value.str.len+1, &EG(exception), sizeof(zval *), (void **) NULL);
|
||||
EG(exception) = NULL;
|
||||
NEXT_OPCODE();
|
||||
case ZEND_SEND_VAL:
|
||||
if (opline->extended_value==ZEND_DO_FCALL_BY_NAME
|
||||
&& ARG_SHOULD_BE_SENT_BY_REF(opline->op2.u.opline_num, fbc, fbc->common.arg_types)) {
|
||||
@@ -2490,7 +2539,6 @@ send_by_ref:
|
||||
case ZEND_NOP:
|
||||
NEXT_OPCODE();
|
||||
EMPTY_SWITCH_DEFAULT_CASE()
|
||||
|
||||
}
|
||||
}
|
||||
zend_error(E_ERROR, "Arrived at end of main loop which shouldn't happen");
|
||||
|
||||
@@ -155,6 +155,8 @@ void init_executor(TSRMLS_D)
|
||||
#ifdef ZEND_WIN32
|
||||
EG(timed_out) = 0;
|
||||
#endif
|
||||
|
||||
EG(exception) = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -121,6 +121,8 @@ struct _zend_compiler_globals {
|
||||
void *ini_parser;
|
||||
#endif
|
||||
|
||||
zend_llist *throw_list;
|
||||
|
||||
struct _zend_ini_parser_param *ini_parser_param;
|
||||
|
||||
int interactive;
|
||||
@@ -203,6 +205,7 @@ struct _zend_executor_globals {
|
||||
|
||||
HashTable ini_directives;
|
||||
zend_objects objects;
|
||||
zval *exception;
|
||||
|
||||
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
|
||||
};
|
||||
|
||||
@@ -105,6 +105,9 @@
|
||||
%token T_FUNCTION
|
||||
%token T_CONST
|
||||
%token T_RETURN
|
||||
%token T_TRY
|
||||
%token T_CATCH
|
||||
%token T_THROW
|
||||
%token T_USE
|
||||
%token T_GLOBAL
|
||||
%token T_STATIC
|
||||
@@ -201,6 +204,9 @@ unticked_statement:
|
||||
| T_FOREACH '(' expr_without_variable T_AS { zend_do_foreach_begin(&$1, &$3, &$2, &$4, 0 TSRMLS_CC); } w_cvar foreach_optional_arg ')' { zend_do_foreach_cont(&$6, &$7, &$4 TSRMLS_CC); } foreach_statement { zend_do_foreach_end(&$1, &$2 TSRMLS_CC); }
|
||||
| T_DECLARE { zend_do_declare_begin(TSRMLS_C); } '(' declare_list ')' declare_statement { zend_do_declare_end(TSRMLS_C); }
|
||||
| ';' /* empty statement */
|
||||
| T_TRY { zend_do_try(&$1 CLS_CC); } '{' inner_statement_list '}'
|
||||
T_CATCH '(' T_VARIABLE ')' { zend_do_begin_catch(&$1, &$8 CLS_CC); } '{' inner_statement_list '}' { zend_do_end_catch(&$1 CLS_CC); }
|
||||
| T_THROW expr ';' { zend_do_throw(&$2 CLS_CC); }
|
||||
| T_DELETE cvar ';' { zend_do_end_variable_parse(BP_VAR_UNSET, 0 TSRMLS_CC); zend_do_unset(&$1, ZEND_UNSET_OBJ TSRMLS_CC); }
|
||||
;
|
||||
|
||||
|
||||
@@ -648,6 +648,18 @@ NEWLINE ("\r"|"\n"|"\r\n")
|
||||
return T_RETURN;
|
||||
}
|
||||
|
||||
<ST_IN_SCRIPTING>"try" {
|
||||
return T_TRY;
|
||||
}
|
||||
|
||||
<ST_IN_SCRIPTING>"catch" {
|
||||
return T_CATCH;
|
||||
}
|
||||
|
||||
<ST_IN_SCRIPTING>"throw" {
|
||||
return T_THROW;
|
||||
}
|
||||
|
||||
<ST_IN_SCRIPTING>"if" {
|
||||
return T_IF;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user