? b ? fileGetContentsVar7.dir ? run-tests.sh ? traits.patch ? Zend/.DS_Store ? Zend/tests/.DS_Store ? Zend/tests/traits Index: Zend/zend.h =================================================================== RCS file: /repository/ZendEngine2/zend.h,v retrieving revision 1.364 diff -u -r1.364 zend.h --- Zend/zend.h 31 Dec 2008 11:12:28 -0000 1.364 +++ Zend/zend.h 18 Jan 2009 13:48:34 -0000 @@ -22,7 +22,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "3.0.0-dev" +#define ZEND_VERSION "3.0.0-dev-with-traits" #define ZEND_ENGINE_2 @@ -449,6 +449,44 @@ typedef struct _zend_serialize_data zend_serialize_data; typedef struct _zend_unserialize_data zend_unserialize_data; +struct _zend_trait_method_reference { + zstr method_name; + unsigned int mname_len; + + zend_class_entry *ce; + + zstr class_name; + unsigned int cname_len; +}; +typedef struct _zend_trait_method_reference zend_trait_method_reference; + +struct _zend_trait_precedence { + zend_trait_method_reference *trait_method; + + zend_class_entry** exclude_from_classes; + + union _zend_function* function; +}; +typedef struct _zend_trait_precedence zend_trait_precedence; + +struct _zend_trait_alias { + zend_trait_method_reference *trait_method; + + /** + * name for method to be added + */ + zstr alias; + unsigned int alias_len; + + /** + * modifiers to be set on trait method + */ + zend_uint modifiers; + + union _zend_function* function; +}; +typedef struct _zend_trait_alias zend_trait_alias; + struct _zend_class_entry { char type; zstr name; @@ -493,6 +531,11 @@ zend_class_entry **interfaces; zend_uint num_interfaces; + + zend_class_entry **traits; + zend_uint num_traits; + zend_trait_alias **trait_aliases; + zend_trait_precedence **trait_precedences; char *filename; zend_uint line_start; Index: Zend/zend_compile.c =================================================================== RCS file: /repository/ZendEngine2/zend_compile.c,v retrieving revision 1.848 diff -u -r1.848 zend_compile.c --- Zend/zend_compile.c 14 Jan 2009 13:57:59 -0000 1.848 +++ Zend/zend_compile.c 18 Jan 2009 13:48:35 -0000 @@ -2988,7 +2988,7 @@ if (ce->ce_flags & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS && ce->type == ZEND_INTERNAL_CLASS) { ce->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; - } else if (!(ce->ce_flags & ZEND_ACC_IMPLEMENT_INTERFACES)) { + } else if (!(ce->ce_flags & ZEND_ACC_IMPLEMENT_INTERFACES) && !(ce->ce_flags & ZEND_ACC_IMPLEMENT_TRAITS)) { /* The verification will be done in runtime by ZEND_VERIFY_ABSTRACT_CLASS */ zend_verify_abstract_class(ce TSRMLS_CC); } @@ -3046,6 +3046,497 @@ } /* }}} */ +ZEND_API void zend_do_implement_trait(zend_class_entry *ce, const zend_class_entry *trait TSRMLS_DC) /* {{{ */ +{ + zend_uint i, ignore = 0; + zend_uint current_trait_num = ce->num_traits; + zend_uint parent_trait_num = ce->parent ? ce->parent->num_traits : 0; + + for (i = 0; i < ce->num_traits; i++) { + if (ce->traits[i] == NULL) { + memmove(ce->traits + i, ce->traits + i + 1, sizeof(zend_class_entry*) * (--ce->num_traits - i)); + i--; + } else if (ce->traits[i] == trait) { + if (i < parent_trait_num) { + ignore = 1; + } + } + } + if (!ignore) { + if (ce->num_traits >= current_trait_num) { + if (ce->type == ZEND_INTERNAL_CLASS) { + ce->traits = (zend_class_entry **) realloc(ce->traits, sizeof(zend_class_entry *) * (++current_trait_num)); + } else { + ce->traits = (zend_class_entry **) erealloc(ce->traits, sizeof(zend_class_entry *) * (++current_trait_num)); + } + } + ce->traits[ce->num_traits++] = trait; + } +} +/* }}} */ + +static int _merge_functions(zend_function *fn, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ +{ + size_t current; + size_t i; + size_t count; + HashTable* resulting_table; + HashTable** function_tables; + zend_class_entry *ce; + //zstr lcname; + size_t collision = 0; + size_t abstract_solved = 0; + //unsigned int name_len; + zend_function* other_trait_fn; + + current = va_arg(args, size_t); // index of current trait + count = va_arg(args, size_t); + resulting_table = va_arg(args, HashTable*); + function_tables = va_arg(args, HashTable**); + ce = va_arg(args, zend_class_entry*); + + for (i = 0; i < count; i++) { + if (i == current) { + continue; // just skip this, cause its the table this function is applied on + } + + if (zend_u_hash_find(function_tables[i], UG(unicode)?IS_UNICODE:IS_STRING, hash_key->arKey, hash_key->nKeyLength, &other_trait_fn) == SUCCESS) { + // if it is an abstract method, there is no collision + if (other_trait_fn->common.fn_flags & ZEND_ACC_ABSTRACT) { + // we can savely free and remove it from other table + zend_function_dtor(other_trait_fn); + zend_u_hash_del(function_tables[i], UG(unicode)?IS_UNICODE:IS_STRING, hash_key->arKey, hash_key->nKeyLength); + } else { + // if it is not an abstract method, there is still no collision + // iff fn is an abstract method + if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { + // just mark as solved, will be added if its own trait is processed + abstract_solved = 1; + } else { + // but else, we have a collision of non-abstract methods + collision++; + zend_function_dtor(other_trait_fn); + zend_u_hash_del(function_tables[i], UG(unicode)?IS_UNICODE:IS_STRING, hash_key->arKey, hash_key->nKeyLength); + } + } + } + } + + if (collision) { + zend_function* class_fn; + // make sure method is not already overridden in class + + if (zend_u_hash_find(&ce->function_table, UG(unicode)?IS_UNICODE:IS_STRING, hash_key->arKey, hash_key->nKeyLength, &class_fn) == FAILURE + || class_fn->common.scope != ce) { + zend_error(E_WARNING, "Trait method %v has not been applied, because there are collisions with other trait methods on %v", fn->common.function_name, ce->name); + } + + zend_function_dtor(fn); + } else if (abstract_solved) { + zend_function_dtor(fn); + } else { + // Add it to result function table + if (zend_u_hash_add(resulting_table, ZEND_STR_TYPE, hash_key->arKey, hash_key->nKeyLength, fn, sizeof(zend_function), NULL)==FAILURE) { + zend_error(E_ERROR, "Trait method %v has not been applied, because failure occured during updating resulting trait method table.", fn->common.function_name); + } + } + + //efree(lcname.v); + return ZEND_HASH_APPLY_REMOVE; +} +/* }}} */ + +#define IS_EQUAL(mname, mname_len, str) \ + mname_len == sizeof(str)-1 && ZEND_U_EQUAL(UG(unicode)?IS_UNICODE:IS_STRING, mname, mname_len, str, sizeof(str)-1) + +#define _ADD_MAGIC_METHOD(ce, mname, mname_len, fe) { \ + if (IS_EQUAL(mname, mname_len, "__clone")) { (ce)->clone = (fe); (fe)->common.fn_flags = ZEND_ACC_CLONE; } \ + else if (IS_EQUAL(mname, mname_len, "__get")) (ce)->__get = (fe); \ + else if (IS_EQUAL(mname, mname_len, "__set")) (ce)->__set = (fe); \ + else if (IS_EQUAL(mname, mname_len, "__call")) (ce)->__call = (fe); \ + else if (IS_EQUAL(mname, mname_len, "__unset")) (ce)->__unset = (fe); \ + else if (IS_EQUAL(mname, mname_len, "__isset")) (ce)->__isset = (fe); \ + else if (IS_EQUAL(mname, mname_len, "__callstatic"))(ce)->__callstatic = (fe); \ + else if (IS_EQUAL(mname, mname_len, "__tostring")) (ce)->__tostring = (fe); \ + else if (IS_EQUAL(mname, mname_len, "serialize_func")) (ce)->serialize_func = (fe); \ + else if (IS_EQUAL(mname, mname_len, "unserialize_func"))(ce)->unserialize_func = (fe); \ +} + +/* {{{ php_runkit_function_copy_ctor + Duplicate structures in an op_array where necessary to make an outright duplicate */ +void php_runkit_function_copy_ctor(zend_function *fe, zstr newname) +{ + zend_compiled_variable *dupvars; + zend_op *opcode_copy; + int i; + + if (fe->op_array.static_variables) { + HashTable *tmpHash; + zval tmpZval; + + ALLOC_HASHTABLE(tmpHash); + zend_hash_init(tmpHash, 2, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(tmpHash, fe->op_array.static_variables, ZVAL_COPY_CTOR, &tmpZval, sizeof(zval)); + fe->op_array.static_variables = tmpHash; + } + + fe->op_array.refcount = emalloc(sizeof(zend_uint)); + *(fe->op_array.refcount) = 1; + + i = fe->op_array.last_var; + dupvars = safe_emalloc(fe->op_array.last_var, sizeof(zend_compiled_variable), 0); + while (i > 0) { + i--; + dupvars[i].name = ezstrndup(UG(unicode)?IS_UNICODE:IS_STRING, fe->op_array.vars[i].name, fe->op_array.vars[i].name_len); + dupvars[i].name_len = fe->op_array.vars[i].name_len; + dupvars[i].hash_value = fe->op_array.vars[i].hash_value; + } + fe->op_array.vars = dupvars; + + opcode_copy = safe_emalloc(sizeof(zend_op), fe->op_array.last, 0); + for(i = 0; i < fe->op_array.last; i++) { + opcode_copy[i] = fe->op_array.opcodes[i]; + if (opcode_copy[i].op1.op_type == IS_CONST) { + zval_copy_ctor(&opcode_copy[i].op1.u.constant); + } else { + if (opcode_copy[i].op1.u.jmp_addr >= fe->op_array.opcodes && + opcode_copy[i].op1.u.jmp_addr < fe->op_array.opcodes + fe->op_array.last) { + opcode_copy[i].op1.u.jmp_addr = opcode_copy + (fe->op_array.opcodes[i].op1.u.jmp_addr - fe->op_array.opcodes); + } + } + + if (opcode_copy[i].op2.op_type == IS_CONST) { + zval_copy_ctor(&opcode_copy[i].op2.u.constant); + } else { + if (opcode_copy[i].op2.u.jmp_addr >= fe->op_array.opcodes && + opcode_copy[i].op2.u.jmp_addr < fe->op_array.opcodes + fe->op_array.last) { + opcode_copy[i].op2.u.jmp_addr = opcode_copy + (fe->op_array.opcodes[i].op2.u.jmp_addr - fe->op_array.opcodes); + } + } + } + fe->op_array.opcodes = opcode_copy; + fe->op_array.start_op = fe->op_array.opcodes; + fe->op_array.function_name = newname; + + fe->op_array.prototype = fe; + + if (fe->op_array.arg_info) { + zend_arg_info *tmpArginfo; + + tmpArginfo = safe_emalloc(sizeof(zend_arg_info), fe->op_array.num_args, 0); + for(i = 0; i < fe->op_array.num_args; i++) { + tmpArginfo[i] = fe->op_array.arg_info[i]; + + tmpArginfo[i].name = ezstrndup(UG(unicode)?IS_UNICODE:IS_STRING, tmpArginfo[i].name, tmpArginfo[i].name_len); + if (tmpArginfo[i].class_name.v) { + tmpArginfo[i].class_name = ezstrndup(UG(unicode)?IS_UNICODE:IS_STRING, tmpArginfo[i].class_name, tmpArginfo[i].class_name_len); + } + } + fe->op_array.arg_info = tmpArginfo; + } + + fe->op_array.doc_comment = ezstrndup(UG(unicode)?IS_UNICODE:IS_STRING, fe->op_array.doc_comment, fe->op_array.doc_comment_len); + fe->op_array.try_catch_array = (zend_try_catch_element*)estrndup((char*)fe->op_array.try_catch_array, sizeof(zend_try_catch_element) * fe->op_array.last_try_catch); + + fe->op_array.brk_cont_array = (zend_brk_cont_element*)estrndup((char*)fe->op_array.brk_cont_array, sizeof(zend_brk_cont_element) * fe->op_array.last_brk_cont); +} +/* }}}} */ + +static int _merge_functions_to_class(zend_function *fn, int num_args, va_list args, zend_hash_key *hash_key TSRMLS_DC) +{ + zend_class_entry *ce = va_arg(args, zend_class_entry*); + int add = 0; + zend_function* existing_fn; + + if (zend_u_hash_find(&ce->function_table, ZEND_STR_TYPE, hash_key->arKey, hash_key->nKeyLength, (void**) &existing_fn) == FAILURE) { + add = 1; // not found + } else if (existing_fn->common.scope != ce) { + add = 1; // or inherited from other class or interface + zend_u_hash_del(&ce->function_table, ZEND_STR_TYPE, hash_key->arKey, hash_key->nKeyLength); + } + + if (add) { + fn->common.scope = ce; + + // remove ZEND_ACC_IMPLEMENTED_ABSTRACT flag, think it shouldn't be copied to class + if (fn->common.fn_flags & ZEND_ACC_IMPLEMENTED_ABSTRACT) { + fn->common.fn_flags = fn->common.fn_flags - ZEND_ACC_IMPLEMENTED_ABSTRACT; + } + + if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) { + ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS; + } + + if (zend_u_hash_add(&ce->function_table, UG(unicode)?IS_UNICODE:IS_STRING, hash_key->arKey, hash_key->nKeyLength, fn, sizeof(zend_function), NULL)==FAILURE) { + zend_error(E_ERROR, "Trait method %s has not been applied, because failure occured during updating class method table.", hash_key->arKey); + } + + _ADD_MAGIC_METHOD(ce, hash_key->arKey, hash_key->nKeyLength, fn); + //it could be necessary to update child classes as well + //zend_hash_apply_with_arguments(EG(class_table), (apply_func_args_t)php_runkit_update_children_methods, 5, dce, dce, &dfe, dfunc, dfunc_len); + } else { + zend_function_dtor(fn); + //efree(fn); + } + + //efree(lcname.v); + return ZEND_HASH_APPLY_REMOVE; +} + +static int _copy_functions(zend_function *fn, int num_args, va_list args, zend_hash_key *hash_key) +{ + HashTable* target; + zend_trait_alias** aliases; + HashTable* exclude_table; + zstr lcname; + unsigned int lcname_len; + unsigned int fnname_len; + zend_function fn_copy; + + size_t i = 0; + target = va_arg(args, HashTable*); + aliases = va_arg(args, zend_trait_alias**); + exclude_table = va_arg(args, HashTable*); + + fnname_len = USTR_LEN(fn->common.function_name); + + // apply aliases which are qualified with a class name, there should not be any ambiguatty + if (aliases) { + while (aliases[i]) { + if (fn->common.scope == aliases[i]->trait_method->ce && + ((UG(unicode) && zend_u_binary_strcasecmp(aliases[i]->trait_method->method_name.u, aliases[i]->trait_method->mname_len, fn->common.function_name.u, fnname_len) == 0) + || zend_binary_strcasecmp(aliases[i]->trait_method->method_name.s, aliases[i]->trait_method->mname_len, fn->common.function_name.s, fnname_len) == 0)) { + if (aliases[i]->alias.v) { + fn_copy = *fn; + php_runkit_function_copy_ctor(&fn_copy, ezstrndup(ZEND_STR_TYPE, aliases[i]->alias, aliases[i]->alias_len)); + + if (aliases[i]->modifiers) { // if it is 0, no modifieres has been changed + fn_copy.common.fn_flags = aliases[i]->modifiers; + if (!(aliases[i]->modifiers & ZEND_ACC_PPP_MASK)) { + fn_copy.common.fn_flags |= ZEND_ACC_PUBLIC; + } + } + + lcname_len = 0; + lcname = zend_u_str_case_fold(ZEND_STR_TYPE, aliases[i]->alias, aliases[i]->alias_len, 1, &lcname_len); + + if (zend_u_hash_add(target, ZEND_STR_TYPE, lcname, lcname_len+1, &fn_copy, sizeof(zend_function), NULL)==FAILURE) { + zend_error(E_ERROR, "Failed to added aliased trait method (%v) to trait table. Propably there is already a trait method with same name\n", fn_copy.common.function_name); + } + //aliases[i]->function = fn_copy; + efree(lcname.v); + } + } + i++; + } + } + + lcname_len = 0; + lcname = zend_u_str_case_fold(ZEND_STR_TYPE, fn->common.function_name, fnname_len, 1, &lcname_len); + void* dummy; + if (zend_u_hash_find(exclude_table, ZEND_STR_TYPE, lcname, lcname_len, &dummy) == FAILURE) { + // is not in hashtable, thus, function is not to be excluded + fn_copy = *fn; + php_runkit_function_copy_ctor(&fn_copy, ezstrndup(ZEND_STR_TYPE, fn->common.function_name, fnname_len)); + + // apply aliases which are not qualified by a class name, or which have not alias name, just setting visibility + // TODO: i am still not sure, that there will be no ambigousities... + + if (aliases) { + i = 0; + while (aliases[i]) { + if ((!aliases[i]->trait_method->ce || fn->common.scope == aliases[i]->trait_method->ce) && + ((UG(unicode) && zend_u_binary_strcasecmp(aliases[i]->trait_method->method_name.u, aliases[i]->trait_method->mname_len, fn->common.function_name.u, fnname_len) == 0) + || zend_binary_strcasecmp(aliases[i]->trait_method->method_name.s, aliases[i]->trait_method->mname_len, fn->common.function_name.s, fnname_len) == 0)) { + if (aliases[i]->alias.v) { + zend_function fn_copy2 = *fn; + php_runkit_function_copy_ctor(&fn_copy2, ezstrndup(ZEND_STR_TYPE, aliases[i]->alias, aliases[i]->alias_len)); + + if (aliases[i]->modifiers) { // if it is 0, no modifieres has been changed + fn_copy2.common.fn_flags = aliases[i]->modifiers; + if (!(aliases[i]->modifiers & ZEND_ACC_PPP_MASK)) { + fn_copy2.common.fn_flags |= ZEND_ACC_PUBLIC; + } + } + + unsigned int lcname2_len = 0; + zstr lcname2 = zend_u_str_case_fold(ZEND_STR_TYPE, aliases[i]->alias, aliases[i]->alias_len, 1, &lcname2_len); + + if (zend_u_hash_add(target, ZEND_STR_TYPE, lcname2, lcname2_len+1, &fn_copy2, sizeof(zend_function), NULL)==FAILURE) { + zend_error(E_ERROR, "Failed to added aliased trait method (%v) to trait table. Propably there is already a trait method with same name\n", fn_copy2.common.function_name); + } + efree(lcname2.v); + } else { + if (aliases[i]->modifiers) { // if it is 0, no modifieres has been changed + fn_copy.common.fn_flags = aliases[i]->modifiers; + if (!(aliases[i]->modifiers & ZEND_ACC_PPP_MASK)) { + fn_copy.common.fn_flags |= ZEND_ACC_PUBLIC; + } + } + } + } + i++; + } + } + + + if (zend_u_hash_add(target, ZEND_STR_TYPE, lcname, lcname_len+1, &fn_copy, sizeof(zend_function), NULL)==FAILURE) { + zend_error(E_ERROR, "Failed to added trait method (%v) to trait table. Propably there is already a trait method with same name\n", fn_copy.common.function_name); + } + } + + efree(lcname.v); + + return ZEND_HASH_APPLY_KEEP; +} + +/** +* Copies function table entries to target function table with applied aliasing +*/ +void copy_trait_function_table(HashTable *target, HashTable *source, zend_trait_alias** aliases, HashTable* exclude_table) { + zend_hash_apply_with_arguments(source, (apply_func_args_t)_copy_functions, 3, //3 is number of args for apply_func + target, aliases, exclude_table); +} + +void init_trait_structures(zend_class_entry *ce TSRMLS_DC) /* {{{ */ +{ + size_t i, j = 0; + zend_trait_precedence *cur_precedence; + zend_trait_method_reference *cur_method_ref; + zend_class_entry *cur_ce; + + // resolve class references + + if (ce->trait_precedences) { + i = 0; + while ((cur_precedence = ce->trait_precedences[i])) { + cur_ce = zend_u_fetch_class(ZEND_STR_TYPE, cur_precedence->trait_method->class_name, cur_precedence->trait_method->cname_len, ZEND_FETCH_CLASS_TRAIT TSRMLS_CC); + cur_precedence->trait_method->ce = cur_ce; + + if (cur_precedence->exclude_from_classes) { + j = 0; + while (cur_precedence->exclude_from_classes[j]) { + zstr class_name = ZSTR(cur_precedence->exclude_from_classes[j]); + cur_ce = zend_u_fetch_class(ZEND_STR_TYPE, class_name, USTR_LEN(class_name), ZEND_FETCH_CLASS_TRAIT TSRMLS_CC); + efree(class_name.v); + cur_precedence->exclude_from_classes[j] = cur_ce; + j++; + } + } + i++; + } + } + + if (ce->trait_aliases) { + i = 0; + while (ce->trait_aliases[i]) { + cur_method_ref = ce->trait_aliases[i]->trait_method; + if (cur_method_ref->class_name.v) { + cur_method_ref->ce = zend_u_fetch_class(ZEND_STR_TYPE, cur_method_ref->class_name, cur_method_ref->cname_len, ZEND_FETCH_CLASS_TRAIT TSRMLS_CC); + } + i++; + } + } +} +/* }}} */ + +void compile_exclude_table(HashTable* exclude_table, zend_trait_precedence **precedences, zend_class_entry *trait) { + size_t i, j; + if (precedences) { + i = 0; + while (precedences[i]) { + if (precedences[i]->exclude_from_classes) { + j = 0; + while (precedences[i]->exclude_from_classes[j]) { + if (precedences[i]->exclude_from_classes[j] == trait) { + unsigned int lcname_len; + zstr lcname = zend_u_str_case_fold(ZEND_STR_TYPE, precedences[i]->trait_method->method_name, precedences[i]->trait_method->mname_len, 1, &lcname_len); + if (zend_u_hash_add(exclude_table, ZEND_STR_TYPE, lcname, lcname_len, NULL, 0, NULL)==FAILURE) { + zend_error(E_ERROR, "Failed to evaluate a trait precedence (%v). Method of trait %v was defined to be excluded multiple times.\n", precedences[i]->trait_method->method_name, trait->name); + } + efree(lcname.v); + } + j++; + } + } + i++; + } + } +} + +ZEND_API void zend_do_bind_traits(zend_class_entry *ce TSRMLS_DC) /* {{{ */ +{ + HashTable** function_tables; + HashTable* resulting_table; + HashTable exclude_table; + size_t i; + + if (ce->num_traits <= 0) { return; } + +// zend_error(E_NOTICE, "Do bind Traits on %v with %d traits.\n Class has already %d methods.\n", +// ce->name.s, ce->num_traits, ce->function_table.nNumOfElements); + + // complete initialization of trait strutures in ce + init_trait_structures(ce); + + // prepare copies of trait function tables for combination + function_tables = malloc(sizeof(HashTable*) * ce->num_traits); + resulting_table = (HashTable *) malloc(sizeof(HashTable)); + zend_u_hash_init_ex(resulting_table, 10, // TODO: revisit this start size, may be its not optimal + //NULL, ZEND_FUNCTION_DTOR, 0, 0); + NULL, NULL, 0, UG(unicode), 0); + + for (i = 0; i < ce->num_traits; i++) { + function_tables[i] = (HashTable *) malloc(sizeof(HashTable)); + zend_u_hash_init_ex(function_tables[i], ce->traits[i]->function_table.nNumOfElements, + //NULL, ZEND_FUNCTION_DTOR, 0, 0); + NULL, NULL, 0, UG(unicode), 0); + + zend_u_hash_init_ex(&exclude_table, 2, // TODO: revisit this start size, may be its not optimal + NULL, NULL, 0, UG(unicode), 0); + compile_exclude_table(&exclude_table, ce->trait_precedences, ce->traits[i]); + + // copies functions, applies defined aliasing, and excludes unused trait methods + copy_trait_function_table(function_tables[i], &ce->traits[i]->function_table, ce->trait_aliases, &exclude_table); + zend_hash_graceful_destroy(&exclude_table); + } + + // now merge trait methods + for (i = 0; i < ce->num_traits; i++) { + zend_hash_apply_with_arguments(function_tables[i], (apply_func_args_t)_merge_functions, 5, //5 is number of args for apply_func + i, ce->num_traits, resulting_table, function_tables, ce); + } + + // now the resulting_table contains all trait methods we would have to + // add to the class + // in the following step the methods are inserted into the method table + // if there is already a method with the same name it is replaced iff ce != fn.scope + // --> all inherited methods are overridden, methods defined in the class are leaved + // untouched + zend_hash_apply_with_arguments(resulting_table, (apply_func_args_t)_merge_functions_to_class, 1, ce TSRMLS_CC); + + // free temporary function tables + for (i = 0; i < ce->num_traits; i++) { + //zend_hash_destroy(function_tables[i]); // + zend_hash_graceful_destroy(function_tables[i]); + free(function_tables[i]); + } + free(function_tables); + + // free temporary resulting table + //zend_hash_destroy(resulting_table); // + zend_hash_graceful_destroy(resulting_table); + free(resulting_table); + + zend_verify_abstract_class(ce TSRMLS_CC); + // now everything should be fine and an added ZEND_ACC_IMPLICIT_ABSTRACT_CLASS should be removed + if (ce->ce_flags & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) { + ce->ce_flags -= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS; + } +} +/* }}} */ + + ZEND_API int do_bind_function(zend_op *opline, HashTable *function_table, zend_bool compile_time) /* {{{ */ { zend_function *function; @@ -3082,6 +3573,93 @@ } /* }}} */ +void zend_add_trait_precedence(znode *precedence_znode TSRMLS_DC) /* {{{ */ +{ + zend_class_entry *ce = CG(active_class_entry); + zend_add_to_list(&ce->trait_precedences, (void*)precedence_znode->u.var); +} +/* }}} */ + +void zend_add_trait_alias(znode *alias_znode TSRMLS_DC) /* {{{ */ +{ + zend_class_entry *ce = CG(active_class_entry); + zend_add_to_list(&ce->trait_aliases, (void*)alias_znode->u.var); +} +/* }}} */ + +void zend_prepare_reference(znode *result, znode *class_name, znode *method_name TSRMLS_DC) /* {{{ */ +{ + zend_trait_method_reference *method_ref = emalloc(sizeof(zend_trait_method_reference)); + method_ref->ce = NULL; + + // TODO: check whether this strings have to be copied, but think this is used for class names, too, so should be save somehow + if (class_name) { + method_ref->class_name = Z_UNIVAL(class_name->u.constant); + method_ref->cname_len = Z_UNILEN(class_name->u.constant); + } else { + method_ref->class_name.v = NULL; + method_ref->cname_len = 0; + } + + method_ref->method_name = Z_UNIVAL(method_name->u.constant); + method_ref->mname_len = Z_UNILEN(method_name->u.constant); + + result->u.var = (intptr_t)method_ref; +} +/* }}} */ + + +void zend_prepare_trait_alias(znode *result, znode *method_reference, znode *modifiers, znode *alias TSRMLS_DC) /* {{{ */ +{ + zend_trait_alias *trait_alias = emalloc(sizeof(zend_trait_alias)); + + trait_alias->trait_method = (zend_trait_method_reference*)method_reference->u.var; + trait_alias->modifiers = Z_LVAL(modifiers->u.constant); + + if (alias) { + trait_alias->alias = Z_UNIVAL(alias->u.constant); + trait_alias->alias_len = Z_UNILEN(alias->u.constant); + } else { + trait_alias->alias.v = NULL; + } + trait_alias->function = NULL; + + result->u.var = (intptr_t)trait_alias; +} +/* }}} */ + +//void init_trait_alias(znode* result, const znode* method_name, const znode* alias, const znode* modifiers TSRMLS_DC) /* {{{ */ +/*{ + zend_trait_alias* trait_alias = emalloc(sizeof(zend_trait_alias)); + trait_alias->method_name = Z_UNIVAL(method_name->u.constant); + + // may be method is only excluded, then the alias node is NULL + if (alias) { + trait_alias->alias = Z_UNIVAL(alias->u.constant); + trait_alias->modifiers = Z_LVAL(modifiers->u.constant); + } else { + + } + trait_alias->function = NULL; + + + result->u.var = trait_alias; +} +/* }}} */ + +void zend_prepare_trait_precedence(znode *result, znode *method_reference, znode *trait_list TSRMLS_DC) /* {{{ */ +{ + zend_trait_precedence *trait_precedence = emalloc(sizeof(zend_trait_precedence)); + + trait_precedence->trait_method = (zend_trait_method_reference*)method_reference->u.var; + trait_precedence->exclude_from_classes = (zend_class_entry**) trait_list->u.var; + + trait_precedence->function = NULL; + + result->u.var = (intptr_t)trait_precedence; +} +/* }}} */ + ZEND_API zend_class_entry *do_bind_class(const zend_op *opline, HashTable *class_table, zend_bool compile_time TSRMLS_DC) /* {{{ */ { zend_class_entry *ce, **pce; @@ -3213,7 +3791,10 @@ } case ZEND_VERIFY_ABSTRACT_CLASS: case ZEND_ADD_INTERFACE: + case ZEND_ADD_TRAIT: + case ZEND_BIND_TRAITS: /* We currently don't early-bind classes that implement interfaces */ + /* Classes with traits are handled exactly the same, no early-bind here */ return; default: zend_error(E_COMPILE_ERROR, "Invalid binding type"); @@ -3627,7 +4208,7 @@ void zend_do_end_class_declaration(const znode *class_token, const znode *parent_token TSRMLS_DC) /* {{{ */ { zend_class_entry *ce = CG(active_class_entry); - + do_inherit_parent_constructor(ce TSRMLS_CC); if (ce->constructor) { @@ -3667,6 +4248,22 @@ ce->num_interfaces = 0; ce->ce_flags |= ZEND_ACC_IMPLEMENT_INTERFACES; } + + /* Check for traits and proceed like with interfaces. + * The only difference will be a combined handling of them in the end. + * Thus, we need another opcode here. */ + if (ce->num_traits > 0) { + ce->traits = NULL; + ce->num_traits = 0; + ce->ce_flags |= ZEND_ACC_IMPLEMENT_TRAITS; + + // opcode generation: + zend_op *opline; + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + opline->opcode = ZEND_BIND_TRAITS; + opline->op1 = CG(implementing_class); + } + CG(active_class_entry) = NULL; } /* }}} */ @@ -3674,6 +4271,11 @@ void zend_do_implements_interface(znode *interface_name TSRMLS_DC) /* {{{ */ { zend_op *opline; + + // Traits can not implement interfaces + if ((CG(active_class_entry)->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) { + zend_error(E_COMPILE_ERROR, "Cannot use '%R' as interface on '%R' since it is a Trait", Z_TYPE(interface_name->u.constant), Z_UNIVAL(interface_name->u.constant), ZEND_STR_TYPE, CG(active_class_entry)->name); + } switch (zend_get_class_fetch_type(Z_TYPE(interface_name->u.constant), Z_UNIVAL(interface_name->u.constant), Z_UNILEN(interface_name->u.constant))) { case ZEND_FETCH_CLASS_SELF: @@ -3695,6 +4297,30 @@ } /* }}} */ +void zend_do_implements_trait(znode *trait_name TSRMLS_DC) /* {{{ */ +{ + zend_op *opline; + + switch (zend_get_class_fetch_type(Z_TYPE(trait_name->u.constant), Z_UNIVAL(trait_name->u.constant), Z_UNILEN(trait_name->u.constant))) { + case ZEND_FETCH_CLASS_SELF: + case ZEND_FETCH_CLASS_PARENT: + case ZEND_FETCH_CLASS_STATIC: + zend_error(E_COMPILE_ERROR, "Cannot use '%R' as trait name as it is reserved", Z_TYPE(trait_name->u.constant), Z_UNIVAL(trait_name->u.constant)); + break; + default: + break; + } + + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + opline->opcode = ZEND_ADD_TRAIT; + opline->op1 = CG(implementing_class); + zend_resolve_class_name(trait_name, &opline->extended_value, 0 TSRMLS_CC); + opline->extended_value = (opline->extended_value & ~ZEND_FETCH_CLASS_MASK) | ZEND_FETCH_CLASS_TRAIT; + opline->op2 = *trait_name; + CG(active_class_entry)->num_traits++; +} +/* }}} */ + ZEND_API void zend_mangle_property_name(char **dest, int *dest_length, const char *src1, int src1_length, const char *src2, int src2_length, int internal) /* {{{ */ { char *prop_name; @@ -4423,6 +5049,33 @@ } /* }}} */ +void zend_init_list(void *result, void *item TSRMLS_DC) /* {{{ */ +{ + void** list = emalloc(sizeof(void*) * 2); + + list[0] = item; + list[1] = NULL; + + *(void**)result = list; +} +/* }}} */ + +void zend_add_to_list(void *result, void *item TSRMLS_DC) /* {{{ */ +{ + void** list = *(void**)result; + size_t n = 0; + + while (list && list[n]) { n++; } + + list = erealloc(list, sizeof(void*) * (n+2)); + + list[n] = item; + list[n+1] = NULL; + + *(void**)result = list; +} +/* }}} */ + void zend_do_fetch_static_variable(znode *varname, const znode *static_assignment, int fetch_type TSRMLS_DC) /* {{{ */ { zval *tmp; @@ -5262,6 +5915,10 @@ ce->parent = NULL; ce->num_interfaces = 0; ce->interfaces = NULL; + ce->num_traits = 0; + ce->traits = NULL; + ce->trait_aliases = NULL; + ce->trait_precedences = NULL; ce->module = NULL; ce->serialize = NULL; ce->unserialize = NULL; Index: Zend/zend_compile.h =================================================================== RCS file: /repository/ZendEngine2/zend_compile.h,v retrieving revision 1.391 diff -u -r1.391 zend_compile.h --- Zend/zend_compile.h 31 Dec 2008 11:12:28 -0000 1.391 +++ Zend/zend_compile.h 18 Jan 2009 13:48:35 -0000 @@ -120,6 +120,7 @@ #define ZEND_ACC_EXPLICIT_ABSTRACT_CLASS 0x20 #define ZEND_ACC_FINAL_CLASS 0x40 #define ZEND_ACC_INTERFACE 0x80 +#define ZEND_ACC_TRAIT 0x120 /* op_array flags */ #define ZEND_ACC_INTERACTIVE 0x10 @@ -150,6 +151,7 @@ /* class implement interface(s) flag */ #define ZEND_ACC_IMPLEMENT_INTERFACES 0x80000 +#define ZEND_ACC_IMPLEMENT_TRAITS 0x400000 #define ZEND_ACC_CLOSURE 0x100000 @@ -457,6 +459,21 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry *iface TSRMLS_DC); void zend_do_implements_interface(znode *interface_znode TSRMLS_DC); +/* Trait related functions */ +void zend_add_trait_precedence(znode *precedence_znode TSRMLS_DC); +void zend_add_trait_alias(znode *alias_znode TSRMLS_DC); + + +//void zend_do_implements_trait(znode *interface_znode /*, znode* aliases */ TSRMLS_DC); +//ZEND_API void zend_do_implement_trait(zend_class_entry *ce, const zend_class_entry *trait TSRMLS_DC); +//ZEND_API void zend_do_bind_traits(zend_class_entry *ce TSRMLS_DC); +//void zend_do_binds_traits(TSRMLS_D); +//ZEND_API void zend_do_add_trait_preparative_to_class(zend_class_entry *ce, zend_class_entry *trait TSRMLS_DC); + +void init_trait_alias_list(znode* result, const znode* trait_alias TSRMLS_DC); +void add_trait_alias(znode* result, const znode* trait_alias TSRMLS_DC); +void init_trait_alias(znode* result, const znode* method_name, const znode* alias, const znode* modifiers TSRMLS_DC); + ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent_ce TSRMLS_DC); void zend_do_early_binding(TSRMLS_D); ZEND_API void zend_do_delayed_early_binding(const zend_op_array *op_array TSRMLS_DC); @@ -507,6 +524,11 @@ void zend_do_new_list_begin(TSRMLS_D); void zend_do_new_list_end(TSRMLS_D); +/* Functions for a null terminated pointer list, used for traits parsing and compilation */ +void zend_init_list(void *result, void *item TSRMLS_DC); +void zend_add_to_list(void *result, void *item TSRMLS_DC); + + void zend_do_cast(znode *result, const znode *expr, int type TSRMLS_DC); void zend_do_include_or_eval(int type, znode *result, const znode *op1 TSRMLS_DC); @@ -660,6 +682,7 @@ #define ZEND_FETCH_CLASS_AUTO 5 #define ZEND_FETCH_CLASS_INTERFACE 6 #define ZEND_FETCH_CLASS_STATIC 7 +#define ZEND_FETCH_CLASS_TRAIT 14 #define ZEND_FETCH_CLASS_MASK 0x0f #define ZEND_FETCH_CLASS_NO_AUTOLOAD 0x80 #define ZEND_FETCH_CLASS_SILENT 0x0100 Index: Zend/zend_execute_API.c =================================================================== RCS file: /repository/ZendEngine2/zend_execute_API.c,v retrieving revision 1.471 diff -u -r1.471 zend_execute_API.c --- Zend/zend_execute_API.c 2 Jan 2009 13:14:17 -0000 1.471 +++ Zend/zend_execute_API.c 18 Jan 2009 13:48:35 -0000 @@ -1641,6 +1641,8 @@ if (!silent && !EG(exception)) { if (fetch_type == ZEND_FETCH_CLASS_INTERFACE) { zend_error(E_ERROR, "Interface '%R' not found", type, class_name); + } else if (fetch_type == ZEND_FETCH_CLASS_TRAIT) { + zend_error(E_ERROR, "Trait '%R' not found", type, class_name); } else { zend_error(E_ERROR, "Class '%R' not found", type, class_name); } Index: Zend/zend_language_parser.y =================================================================== RCS file: /repository/ZendEngine2/zend_language_parser.y,v retrieving revision 1.214 diff -u -r1.214 zend_language_parser.y --- Zend/zend_language_parser.y 31 Dec 2008 11:09:46 -0000 1.214 +++ Zend/zend_language_parser.y 18 Jan 2009 13:48:36 -0000 @@ -113,6 +113,8 @@ %token T_CATCH %token T_THROW %token T_USE +%token T_USES +%token T_INSTEAD %token T_GLOBAL %right T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC %token T_VAR @@ -121,6 +123,7 @@ %token T_EMPTY %token T_HALT_COMPILER %token T_CLASS +%token T_TRAIT %token T_INTERFACE %token T_EXTENDS %token T_IMPLEMENTS @@ -331,6 +334,7 @@ class_entry_type: T_CLASS { $$.u.opline_num = CG(zend_lineno); $$.u.EA.type = 0; } | T_ABSTRACT T_CLASS { $$.u.opline_num = CG(zend_lineno); $$.u.EA.type = ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; } + | T_TRAIT { $$.u.opline_num = CG(zend_lineno); $$.u.EA.type = ZEND_ACC_TRAIT; } | T_FINAL T_CLASS { $$.u.opline_num = CG(zend_lineno); $$.u.EA.type = ZEND_ACC_FINAL_CLASS; } ; @@ -516,10 +520,67 @@ class_statement: variable_modifiers { CG(access_type) = Z_LVAL($1.u.constant); } class_variable_declaration ';' | class_constant_declaration ';' + | trait_use_statement | method_modifiers function is_reference T_STRING { zend_do_begin_function_declaration(&$2, &$4, 1, $3.op_type, &$1 TSRMLS_CC); } '(' parameter_list ')' method_body { zend_do_abstract_method(&$4, &$1, &$9 TSRMLS_CC); zend_do_end_function_declaration(&$2 TSRMLS_CC); } ; +trait_use_statement: + T_USE trait_list trait_adaptations +; + +trait_list: + fully_qualified_class_name { zend_do_implements_trait(&$1 TSRMLS_CC); } + | trait_list ',' fully_qualified_class_name { zend_do_implements_trait(&$3 TSRMLS_CC); } +; + +trait_adaptations: + ';' + | '{' trait_adaptation_list '}' +; + +trait_adaptation_list: + /* empty */ + | non_empty_trait_adaptation_list +; + +non_empty_trait_adaptation_list: + trait_adaptation_statement + | non_empty_trait_adaptation_list trait_adaptation_statement +; + +trait_adaptation_statement: + trait_precedence ';' { zend_add_trait_precedence(&$1 TSRMLS_CC); } + | trait_alias ';' { zend_add_trait_alias(&$1 TSRMLS_CC); } +; + +trait_precedence: + trait_method_reference_fully_qualified T_INSTEAD trait_reference_list { zend_prepare_trait_precedence(&$$, &$1, &$3 TSRMLS_CC); } +; + +trait_reference_list: + fully_qualified_class_name { zend_init_list(&$$.u.var, Z_UNIVAL($1.u.constant).v TSRMLS_CC); } + | trait_reference_list ',' fully_qualified_class_name { zend_add_to_list(&$1.u.var, Z_UNIVAL($3.u.constant).v TSRMLS_CC); $$ = $1; } +; + +trait_method_reference: + T_STRING { zend_prepare_reference(&$$, NULL, &$1 TSRMLS_CC); } + | trait_method_reference_fully_qualified { $$ = $1; } +; + +trait_method_reference_fully_qualified: + fully_qualified_class_name T_PAAMAYIM_NEKUDOTAYIM T_STRING { zend_prepare_reference(&$$, &$1, &$3 TSRMLS_CC); } +; + +trait_alias: + trait_method_reference T_AS trait_modifiers T_STRING { zend_prepare_trait_alias(&$$, &$1, &$3, &$4 TSRMLS_CC); } + | trait_method_reference T_AS member_modifier { zend_prepare_trait_alias(&$$, &$1, &$3, NULL TSRMLS_CC); } +; + +trait_modifiers: + /* empty */ { Z_LVAL($$.u.constant) = 0x0; } /* No change of methods visibility */ + | member_modifier { $$ = $1; } /* REM: Keep in mind, there are not only visibility modifiers */ +; method_body: ';' /* abstract method */ { Z_LVAL($$.u.constant) = ZEND_ACC_ABSTRACT; } Index: Zend/zend_language_scanner.l =================================================================== RCS file: /repository/ZendEngine2/zend_language_scanner.l,v retrieving revision 1.200 diff -u -r1.200 zend_language_scanner.l --- Zend/zend_language_scanner.l 9 Jan 2009 17:20:56 -0000 1.200 +++ Zend/zend_language_scanner.l 18 Jan 2009 13:48:37 -0000 @@ -1543,6 +1543,10 @@ return T_INTERFACE; } +"trait" { + return T_TRAIT; +} + "extends" { return T_EXTENDS; } @@ -1669,6 +1673,10 @@ return T_USE; } +"instead" { + return T_INSTEAD; +} + "global" { return T_GLOBAL; } Index: Zend/zend_opcode.c =================================================================== RCS file: /repository/ZendEngine2/zend_opcode.c,v retrieving revision 1.136 diff -u -r1.136 zend_opcode.c --- Zend/zend_opcode.c 31 Dec 2008 11:12:29 -0000 1.136 +++ Zend/zend_opcode.c 18 Jan 2009 13:48:37 -0000 @@ -230,9 +230,50 @@ if (ce->num_interfaces > 0 && ce->interfaces) { efree(ce->interfaces); } + if (ce->num_traits > 0 && ce->traits) { + efree(ce->traits); + } if (ce->doc_comment.v) { efree(ce->doc_comment.v); } + if (ce->trait_aliases) { + size_t i = 0; + while (ce->trait_aliases[i]) { + if (ce->trait_aliases[i]->trait_method) { + if (ce->trait_aliases[i]->trait_method->method_name.v) { + efree(ce->trait_aliases[i]->trait_method->method_name.v); + } + if (ce->trait_aliases[i]->trait_method->class_name.v) { + efree(ce->trait_aliases[i]->trait_method->class_name.v); + } + efree(ce->trait_aliases[i]->trait_method); + } + if (ce->trait_aliases[i]->alias.v) { + efree(ce->trait_aliases[i]->alias.v); + } + efree(ce->trait_aliases[i]); + i++; + } + efree(ce->trait_aliases); + } + + if (ce->trait_precedences) { + size_t i = 0; + while (ce->trait_precedences[i]) { + efree(ce->trait_precedences[i]->trait_method->method_name.v); + efree(ce->trait_precedences[i]->trait_method->class_name.v); + efree(ce->trait_precedences[i]->trait_method); + + if (ce->trait_precedences[i]->exclude_from_classes) { + efree(ce->trait_precedences[i]->exclude_from_classes); + } + + efree(ce->trait_precedences[i]); + i++; + } + efree(ce->trait_precedences); + } + efree(ce); break; case ZEND_INTERNAL_CLASS: @@ -245,6 +286,10 @@ if (ce->num_interfaces > 0) { free(ce->interfaces); } + //TODO: internal class have to be handled somehow?! + //if (ce->num_traits > 0) { + // free(ce->traits); + //} if (ce->doc_comment.v) { free(ce->doc_comment.v); } Index: Zend/zend_vm_def.h =================================================================== RCS file: /repository/ZendEngine2/zend_vm_def.h,v retrieving revision 1.255 diff -u -r1.255 zend_vm_def.h --- Zend/zend_vm_def.h 15 Jan 2009 08:49:11 -0000 1.255 +++ Zend/zend_vm_def.h 18 Jan 2009 13:48:38 -0000 @@ -4432,6 +4432,33 @@ ZEND_VM_NEXT_OPCODE(); } +ZEND_VM_HANDLER(154, ZEND_ADD_TRAIT, ANY, ANY) +{ + // TODO: is this still used? if so, needs to be updated from zend_vm_execute.h + zend_op *opline = EX(opline); + zend_class_entry *ce = EX_T(opline->op1.u.var).class_entry; + zend_class_entry *trait = EX_T(opline->op2.u.var).class_entry; + + if (!(trait->ce_flags & ZEND_ACC_TRAIT)) { + zend_error_noreturn(E_ERROR, "%s cannot implement %s - it is not a trait", ce->name, trait->name); + } + + zend_do_implement_trait(ce, trait TSRMLS_CC); + + ZEND_VM_NEXT_OPCODE(); +} + +ZEND_VM_HANDLER(155, ZEND_BIND_TRAITS, ANY, ANY) +{ + // TODO: is this still used? if so, needs to be updated from zend_vm_execute.h + zend_op *opline = EX(opline); + zend_class_entry *ce = EX_T(opline->op1.u.var).class_entry; + + zend_do_bind_traits(ce TSRMLS_CC); + + ZEND_VM_NEXT_OPCODE(); +} + ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) { zend_uint op_num = EG(opline_before_exception)-EG(active_op_array)->opcodes; Index: Zend/zend_vm_execute.h =================================================================== RCS file: /repository/ZendEngine2/zend_vm_execute.h,v retrieving revision 1.259 diff -u -r1.259 zend_vm_execute.h --- Zend/zend_vm_execute.h 15 Jan 2009 08:49:11 -0000 1.259 +++ Zend/zend_vm_execute.h 18 Jan 2009 13:48:47 -0000 @@ -911,6 +911,32 @@ ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_ADD_TRAIT_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + zend_op *opline = EX(opline); + zend_class_entry *ce = EX_T(opline->op1.u.var).class_entry; + zend_class_entry *trait = zend_u_fetch_class(Z_TYPE(opline->op2.u.constant), Z_UNIVAL(opline->op2.u.constant), Z_UNILEN(opline->op2.u.constant), opline->extended_value TSRMLS_CC); + + if (!(trait->ce_flags & ZEND_ACC_TRAIT)) { + zend_error_noreturn(E_ERROR, "%s cannot use %s - it is not a trait", ce->name, trait->name); + } + + zend_do_implement_trait(ce, trait TSRMLS_CC); + + ZEND_VM_NEXT_OPCODE(); +} + +static int ZEND_FASTCALL ZEND_BIND_TRAITS_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + // TODO: make sure this code is removed if it is dead + zend_op *opline = EX(opline); + zend_class_entry *ce = EX_T(opline->op1.u.var).class_entry; + + zend_do_bind_traits(ce TSRMLS_CC); + + ZEND_VM_NEXT_OPCODE(); +} + static int ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { zend_op *opline = EX(opline); @@ -35433,6 +35459,56 @@ ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, ZEND_NULL_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, // STEFAN added for opcode 154 and 155 + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_ADD_TRAIT_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, + ZEND_BIND_TRAITS_SPEC_HANDLER, ZEND_NULL_HANDLER }; zend_opcode_handlers = (opcode_handler_t*)labels; Index: Zend/zend_vm_opcodes.h =================================================================== RCS file: /repository/ZendEngine2/zend_vm_opcodes.h,v retrieving revision 1.75 diff -u -r1.75 zend_vm_opcodes.h --- Zend/zend_vm_opcodes.h 31 Dec 2008 11:12:29 -0000 1.75 +++ Zend/zend_vm_opcodes.h 18 Jan 2009 13:48:47 -0000 @@ -154,3 +154,5 @@ #define ZEND_U_NORMALIZE 151 #define ZEND_JMP_SET 152 #define ZEND_DECLARE_LAMBDA_FUNCTION 153 +#define ZEND_ADD_TRAIT 154 +#define ZEND_BIND_TRAITS 155