CG(handle_op_arrays) = 1;
CG(in_compilation) = 0;
init_compiler_declarables(TSRMLS_C);
+ CG(throw_list) = NULL;
}
zend_stack_push(&CG(foreach_copy_stack), (void *) &switch_entry.cond, sizeof(znode));
}
+ function_token->throw_list = CG(throw_list);
+ CG(throw_list) = NULL;
}
/* 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;
}
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;
}
}
+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) {
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));
typedef struct _znode {
int op_type;
+ zend_llist *throw_list; // Try and save this space later on
union {
zval constant;
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);
#define ZEND_SEND_VAR_NO_REF 106
+#define ZEND_CATCH 107
+#define ZEND_THROW 108
+
/* end of block */
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;
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;
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;
(*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)) {
case ZEND_NOP:
NEXT_OPCODE();
EMPTY_SWITCH_DEFAULT_CASE()
-
}
}
zend_error(E_ERROR, "Arrived at end of main loop which shouldn't happen");
#ifdef ZEND_WIN32
EG(timed_out) = 0;
#endif
+
+ EG(exception) = NULL;
}
void *ini_parser;
#endif
+ zend_llist *throw_list;
+
struct _zend_ini_parser_param *ini_parser_param;
int interactive;
HashTable ini_directives;
zend_objects objects;
+ zval *exception;
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
};
%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
| 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); }
;
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;
}