Browse Source

Implemented Traits for PHP as proposed in the RFC [TRAITS]

# RFC http://wiki.php.net/rfc/horizontalreuse#traits_-_reuse_of_behavior
# Ok, here we go, I guess that will result in more discussion, which is fine
# by me. But now, the patch is here, and properly archived.
# 
# See below a list of notes to the patch, it also includes a list of
# points which should be fixed
# 
# Internals of the Traits Patch
# -----------------------------
# 
# Open TODOs
# """"""""""
# 
# - Reflection API
# - support for traits for internal classes
#   - currently destroy_zend_class does not handle that case 
# 
# Introduced Structures
# """""""""""""""""""""
# 
# Data structures to encode the composition information specified in the
# source:
#  - zend_trait_method_reference
#  - zend_trait_precedence
#  - zend_trait_alias
# 
# Changes
# """""""
# 
# zend_class_entry
#  - uses NULL terminated lists of pointers for
#    - trait_aliases
#    - trait_precedences
#    - do you prefer an explicit counter?
#    - the information is only necessary during class composition
#      but might be interesting for reflection
#    - did not want to blow up class further with not really necessary length counters
# 
# added keywords
#   - trait
#   - insteadof
# 
# Added opcodes
#  ZEND_ADD_TRAIT
#    - similar to ZEND_ADD_INTERFACE
#    - adds the trait to the list of traits of a class, no actual composition done
#  ZEND_BIND_TRAITS
#    - emitted in zend_do_end_class_declaration
#    - concludes the class definition and will initiate the trait composition
#      when the class definition is encountered during runtime
# 
# Added Flags
#   ZEND_ACC_TRAIT = 0x120
#   ZEND_ACC_IMPLEMENT_TRAITS = 0x400000
#   ZEND_FETCH_CLASS_TRAIT = 14
# 
# zend_vm_execute.h
#  - not sure whether the handler initialization (ZEND_ADD_TRAIT_SPEC_HANDLER,
#    ZEND_BIND_TRAITS_SPEC_HANDLER) is correct, maybe it should be more selective
# 
# zend_compile.c
#  - refactored do_inherit_method_check
#    split into do_inherit_method_check and do_inheritance_check_on_method
#  - added helper functions use a '_' as prefix and are not mentioned in the
#    headers
#  - _copy_functions
#    prepare hash-maps of functions which should be merged into a class
#    here the aliases are handled
#  - _merge_functions
#    builds a hash-table of the methods which need to be added to a class
#    does the conflict detection
#  - reused php_runkit_function_copy_ctor
#    - it is not identical with the original code anymore, needed to update it
#      think I fixed some bugs, not sure whether all have been reported back to runkit
#    - has to be renamed, left the name for the moment, to make its origin obvious
#    - here might be optimization potential
#    - not sure whether everything needs to be copied
#      - copying the literals might be broken
#        - added it since the literals array is freed by efree and gave problems
#          with doubled frees
#      - all immutable parts of the zend_op array should not be copied
#        - am not sure which parts are immutable
#        - and not sure how to avoid doubled frees on the same arrays on shutdown
#  - _merge_functions_to_class
#    does the final merging with the target class to handle inherited
#    and overridden methods
#  - small helper for NULL terminated lists
#    zend_init_list, zend_add_to_list
# 
# zend_language_parser.y
#  - reused class definition for traits
#    - there should be something with regard to properties
#      - if they get explicitly defined, it might be worthwhile to
#        check that there are no collisions with other traits in a composition
#        (however, I would not introduce elaborate language features to control that
#         but a notice for such conflicts might be nice to the developers)
experimental/lemon
Stefan Marr 16 years ago
parent
commit
cd6415f1a9
  1. 43
      Zend/zend.h
  2. 736
      Zend/zend_compile.c
  3. 27
      Zend/zend_compile.h
  4. 4
      Zend/zend_execute_API.c
  5. 60
      Zend/zend_language_parser.y
  6. 1765
      Zend/zend_language_scanner.c
  7. 8
      Zend/zend_language_scanner.l
  8. 2
      Zend/zend_language_scanner_defs.h
  9. 41
      Zend/zend_opcode.c
  10. 28
      Zend/zend_vm_def.h
  11. 78
      Zend/zend_vm_execute.h
  12. 2
      Zend/zend_vm_opcodes.h

43
Zend/zend.h

@ -420,6 +420,44 @@ struct _zend_unserialize_data;
typedef struct _zend_serialize_data zend_serialize_data;
typedef struct _zend_unserialize_data zend_unserialize_data;
struct _zend_trait_method_reference {
char* method_name;
unsigned int mname_len;
zend_class_entry *ce;
char* 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
*/
char* 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;
char *name;
@ -464,6 +502,11 @@ struct _zend_class_entry {
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;

736
Zend/zend_compile.c

@ -2851,20 +2851,11 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c
}
/* }}} */
static zend_bool do_inherit_method_check(HashTable *child_function_table, zend_function *parent, const zend_hash_key *hash_key, zend_class_entry *child_ce) /* {{{ */
static void do_inheritance_check_on_method(zend_function *child, zend_function *parent)
{
zend_uint child_flags;
zend_uint parent_flags = parent->common.fn_flags;
zend_function *child;
TSRMLS_FETCH();
if (zend_hash_quick_find(child_function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void **) &child)==FAILURE) {
if (parent_flags & (ZEND_ACC_ABSTRACT)) {
child_ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS;
}
return 1; /* method doesn't exist in child, copy from parent */
}
zend_uint parent_flags = parent->common.fn_flags;
if (parent->common.fn_flags & ZEND_ACC_ABSTRACT
&& parent->common.scope != (child->common.prototype ? child->common.prototype->common.scope : child->common.scope)
&& child->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_IMPLEMENTED_ABSTRACT)) {
@ -2926,7 +2917,23 @@ static zend_bool do_inherit_method_check(HashTable *child_function_table, zend_f
zend_error(E_STRICT, "Declaration of %s::%s() should be compatible with that of %s::%s()", ZEND_FN_SCOPE_NAME(child), child->common.function_name, ZEND_FN_SCOPE_NAME(parent), parent->common.function_name);
}
}
}
static zend_bool do_inherit_method_check(HashTable *child_function_table, zend_function *parent, const zend_hash_key *hash_key, zend_class_entry *child_ce) /* {{{ */
{
zend_uint parent_flags = parent->common.fn_flags;
zend_function *child;
TSRMLS_FETCH();
if (zend_hash_quick_find(child_function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void **) &child)==FAILURE) {
if (parent_flags & (ZEND_ACC_ABSTRACT)) {
child_ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS;
}
return 1; /* method doesn't exist in child, copy from parent */
}
do_inheritance_check_on_method(child, parent);
return 0;
}
/* }}} */
@ -3111,7 +3118,8 @@ ZEND_API void zend_do_inheritance(zend_class_entry *ce, zend_class_entry *parent
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);
}
@ -3182,6 +3190,537 @@ ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry
}
/* }}} */
ZEND_API void zend_do_implement_trait(zend_class_entry *ce, 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_hash_find(function_tables[i], 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_hash_del(function_tables[i], 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_hash_del(function_tables[i], hash_key->arKey, hash_key->nKeyLength);
}
}
}
}
if (collision) {
zend_function* class_fn;
// make sure method is not already overridden in class
if (zend_hash_find(&ce->function_table, hash_key->arKey, hash_key->nKeyLength, &class_fn) == FAILURE
|| class_fn->common.scope != ce) {
zend_error(E_WARNING, "Trait method %s has not been applied, because there are collisions with other trait methods on %s",
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_hash_add(resulting_table, 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 resulting trait method table.",
fn->common.function_name);
}
}
//efree(lcname.v);
return ZEND_HASH_APPLY_REMOVE;
}
/* }}} */
#define IS_EQUAL(mname, mname_len, str) \
strncmp(mname, str, mname_len)
#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, char *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 = estrndup(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_type == IS_CONST) {
zval_copy_ctor(&opcode_copy[i].op1.constant);
} else {
if (opcode_copy[i].op1.jmp_addr >= fe->op_array.opcodes &&
opcode_copy[i].op1.jmp_addr < fe->op_array.opcodes + fe->op_array.last) {
opcode_copy[i].op1.jmp_addr = opcode_copy + (fe->op_array.opcodes[i].op1.jmp_addr - fe->op_array.opcodes);
}
}
if (opcode_copy[i].op2_type == IS_CONST) {
zval_copy_ctor(&opcode_copy[i].op2.constant);
} else {
if (opcode_copy[i].op2.jmp_addr >= fe->op_array.opcodes &&
opcode_copy[i].op2.jmp_addr < fe->op_array.opcodes + fe->op_array.last) {
opcode_copy[i].op2.jmp_addr = opcode_copy + (fe->op_array.opcodes[i].op2.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->op_array.prototype; //was setting it to fe which does not work since fe is stack allocated and not a stable address
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 = estrndup(tmpArginfo[i].name, tmpArginfo[i].name_len);
if (tmpArginfo[i].class_name) {
tmpArginfo[i].class_name = estrndup(tmpArginfo[i].class_name, tmpArginfo[i].class_name_len);
}
}
fe->op_array.arg_info = tmpArginfo;
}
fe->op_array.doc_comment = estrndup(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);
// TODO: check whether there is something similar and whether that is ok
zend_literal* literals_copy = (zend_literal*)emalloc(fe->op_array.size_literal * sizeof(zend_literal));
for (i = 0; i < fe->op_array.size_literal; i++) {
literals_copy[i] = fe->op_array.literals[i];
}
fe->op_array.literals = literals_copy;
}
/* }}}} */
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;
zend_function* prototype = NULL; // is used to determine the prototype according to the inheritance chain
if (zend_hash_quick_find(&ce->function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void**) &existing_fn) == FAILURE) {
add = 1; // not found
} else if (existing_fn->common.scope != ce) {
add = 1; // or inherited from other class or interface
// prototype = existing_fn; // it is just a reference which was added to the subclass while doing the inheritance
// function_add_ref(prototype); //memory is scrambled anyway????
zend_hash_quick_del(&ce->function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h);
}
if (add) {
zend_function* parent_function;
if (ce->parent && zend_hash_quick_find(&ce->parent->function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void**) &parent_function) != FAILURE) {
prototype = parent_function; //->common.fn_flags |= ZEND_ACC_ABSTRACT;
}
fn->common.scope = ce;
fn->common.prototype = prototype;
if (prototype
&& (prototype->common.fn_flags & ZEND_ACC_IMPLEMENTED_ABSTRACT
|| prototype->common.fn_flags & ZEND_ACC_ABSTRACT)) {
fn->common.fn_flags |= ZEND_ACC_IMPLEMENTED_ABSTRACT;
}
else // 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;
}
// check whether the trait method fullfills the inheritance requirements
if (prototype) {
do_inheritance_check_on_method(fn, prototype);
}
if (fn->common.fn_flags & ZEND_ACC_ABSTRACT) {
ce->ce_flags |= ZEND_ACC_IMPLICIT_ABSTRACT_CLASS;
}
if (zend_hash_update(&ce->function_table, 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;
char* 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 = strlen(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 &&
(zend_binary_strcasecmp(aliases[i]->trait_method->method_name,
aliases[i]->trait_method->mname_len,
fn->common.function_name, fnname_len) == 0)) {
if (aliases[i]->alias) {
fn_copy = *fn;
php_runkit_function_copy_ctor(&fn_copy, estrndup(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 = aliases[i]->alias_len;
lcname = zend_str_tolower_dup(aliases[i]->alias, lcname_len);
if (zend_hash_add(target, lcname, lcname_len+1, &fn_copy, sizeof(zend_function), NULL)==FAILURE) {
zend_error(E_ERROR, "Failed to added aliased trait method (%s) 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);
}
}
i++;
}
}
lcname_len = strlen(fn->common.function_name);
lcname = zend_str_tolower_dup(fn->common.function_name, fnname_len);
void* dummy;
if (zend_hash_find(exclude_table, 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, estrndup(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) &&
(zend_binary_strcasecmp(aliases[i]->trait_method->method_name,
aliases[i]->trait_method->mname_len,
fn->common.function_name, fnname_len) == 0)) {
if (aliases[i]->alias) {
zend_function fn_copy2 = *fn;
php_runkit_function_copy_ctor(&fn_copy2, estrndup(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;
}
}
zend_uint lcname2_len = aliases[i]->alias_len;
char* lcname2 = zend_str_tolower_dup(aliases[i]->alias, lcname2_len);
if (zend_hash_add(target, lcname2, lcname2_len+1, &fn_copy2, sizeof(zend_function), NULL)==FAILURE) {
zend_error(E_ERROR, "Failed to added aliased trait method (%s) to trait table. Propably there is already a trait method with same name\n",
fn_copy2.common.function_name);
}
efree(lcname2);
} 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_hash_add(target, lcname, lcname_len+1, &fn_copy, sizeof(zend_function), NULL)==FAILURE) {
zend_error(E_ERROR, "Failed to added trait method (%s) to trait table. Propably there is already a trait method with same name\n",
fn_copy.common.function_name);
}
}
efree(lcname);
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_fetch_class(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]) {
char* class_name = (char*)cur_precedence->exclude_from_classes[j];
zend_uint name_length = strlen(class_name);
cur_ce = zend_fetch_class(class_name, name_length, ZEND_FETCH_CLASS_TRAIT TSRMLS_CC);
efree(class_name);
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) {
cur_method_ref->ce = zend_fetch_class(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) {
zend_uint lcname_len = precedences[i]->trait_method->mname_len;
char* lcname = zend_str_tolower_dup(precedences[i]->trait_method->method_name,
lcname_len);
if (zend_hash_add(exclude_table, lcname, lcname_len, NULL, 0, NULL)==FAILURE) {
zend_error(E_ERROR, "Failed to evaluate a trait precedence (%s). Method of trait %s was defined to be excluded multiple times.\n",
precedences[i]->trait_method->method_name, trait->name);
}
efree(lcname);
}
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_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, 0);
for (i = 0; i < ce->num_traits; i++) {
function_tables[i] = (HashTable *) malloc(sizeof(HashTable));
zend_hash_init_ex(function_tables[i], ce->traits[i]->function_table.nNumOfElements,
//NULL, ZEND_FUNCTION_DTOR, 0, 0);
NULL, NULL, 0, 0);
zend_hash_init_ex(&exclude_table, 2, // TODO: revisit this start size, may be its not optimal
NULL, NULL, 0, 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(const zend_op_array *op_array, zend_op *opline, HashTable *function_table, zend_bool compile_time) /* {{{ */
{
zend_function *function;
@ -3219,6 +3758,96 @@ ZEND_API int do_bind_function(const zend_op_array *op_array, zend_op *opline, Ha
}
/* }}} */
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,precedence_znode->u.op.ptr);
}
/* }}} */
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, alias_znode->u.op.ptr);
}
/* }}} */
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;
// REM: There should not be a need for copying,
// zend_do_begin_class_declaration is also just using that string
if (class_name) {
method_ref->class_name = Z_STRVAL(class_name->u.constant);
method_ref->cname_len = Z_STRLEN(class_name->u.constant);
} else {
method_ref->class_name = NULL;
method_ref->cname_len = 0;
}
method_ref->method_name = Z_STRVAL(method_name->u.constant);
method_ref->mname_len = Z_STRLEN(method_name->u.constant);
result->u.op.ptr = method_ref;
result->op_type = IS_TMP_VAR;
}
/* }}} */
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.op.ptr;
trait_alias->modifiers = Z_LVAL(modifiers->u.constant);
if (alias) {
trait_alias->alias = Z_STRVAL(alias->u.constant);
trait_alias->alias_len = Z_STRLEN(alias->u.constant);
} else {
trait_alias->alias = NULL;
}
trait_alias->function = NULL;
result->u.op.ptr = 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.op.ptr;
trait_precedence->exclude_from_classes = (zend_class_entry**) trait_list->u.op.ptr;
trait_precedence->function = NULL;
result->u.op.ptr = trait_precedence;
}
/* }}} */
ZEND_API zend_class_entry *do_bind_class(const zend_op_array* op_array, const zend_op *opline, HashTable *class_table, zend_bool compile_time TSRMLS_DC) /* {{{ */
{
zend_class_entry *ce, **pce;
@ -3361,7 +3990,10 @@ void zend_do_early_binding(TSRMLS_D) /* {{{ */
}
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");
@ -3802,6 +4434,22 @@ void zend_do_end_class_declaration(const znode *class_token, const znode *parent
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;
SET_NODE(opline->op1, &CG(implementing_class));
}
CG(active_class_entry) = NULL;
}
/* }}} */
@ -3810,6 +4458,13 @@ 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 '%s' as interface on '%s' since it is a Trait",
Z_STRVAL(interface_name->u.constant),
CG(active_class_entry)->name);
}
switch (zend_get_class_fetch_type(Z_STRVAL(interface_name->u.constant), Z_STRLEN(interface_name->u.constant))) {
case ZEND_FETCH_CLASS_SELF:
case ZEND_FETCH_CLASS_PARENT:
@ -3831,6 +4486,30 @@ void zend_do_implements_interface(znode *interface_name TSRMLS_DC) /* {{{ */
}
/* }}} */
void zend_do_implements_trait(znode *trait_name TSRMLS_DC) /* {{{ */
{
zend_op *opline;
switch (zend_get_class_fetch_type(Z_STRVAL(trait_name->u.constant), Z_STRLEN(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 '%s' as trait name as it is reserved", Z_STRVAL(trait_name->u.constant));
break;
default:
break;
}
opline = get_next_op(CG(active_op_array) TSRMLS_CC);
opline->opcode = ZEND_ADD_TRAIT;
SET_NODE(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;
SET_NODE(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;
@ -4476,6 +5155,33 @@ void zend_do_list_end(znode *result, znode *expr TSRMLS_DC) /* {{{ */
}
/* }}} */
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;
@ -5303,6 +6009,10 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify
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;

27
Zend/zend_compile.h

@ -69,6 +69,7 @@ typedef union _znode_op {
zend_op *jmp_addr;
zval *zv;
zend_literal *literal;
void *ptr; /* Used for passing pointers from the compile to execution phase, currently used for traits */
} znode_op;
typedef struct _znode { /* used only during compilation */
@ -136,6 +137,7 @@ typedef struct _zend_try_catch_element {
#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
@ -166,6 +168,7 @@ typedef struct _zend_try_catch_element {
/* class implement interface(s) flag */
#define ZEND_ACC_IMPLEMENT_INTERFACES 0x80000
#define ZEND_ACC_IMPLEMENT_TRAITS 0x400000
#define ZEND_ACC_CLOSURE 0x100000
@ -466,6 +469,24 @@ ZEND_API void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_
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, 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 zend_prepare_trait_precedence(znode *result, znode *method_reference, znode *trait_list TSRMLS_DC);
void zend_prepare_reference(znode *result, znode *class_name, znode *method_name TSRMLS_DC);
void zend_prepare_trait_alias(znode *result, znode *method_reference, znode *modifiers, znode *alias 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);
@ -515,6 +536,11 @@ void zend_do_add_list_element(const znode *element TSRMLS_DC);
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);
@ -643,6 +669,7 @@ int zend_add_literal(zend_op_array *op_array, const zval *zv);
#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

4
Zend/zend_execute_API.c

@ -1561,7 +1561,9 @@ check_fetch_type:
if (!silent && !EG(exception)) {
if (fetch_type == ZEND_FETCH_CLASS_INTERFACE) {
zend_error(E_ERROR, "Interface '%s' not found", class_name);
} else {
} else if (fetch_type == ZEND_FETCH_CLASS_TRAIT) {
zend_error(E_ERROR, "Trait '%s' not found", class_name);
} else {
zend_error(E_ERROR, "Class '%s' not found", class_name);
}
}

60
Zend/zend_language_parser.y

@ -113,6 +113,7 @@
%token T_CATCH
%token T_THROW
%token T_USE
%token T_INSTEADOF
%token T_GLOBAL
%right T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC
%token T_VAR
@ -121,6 +122,7 @@
%token T_EMPTY
%token T_HALT_COMPILER
%token T_CLASS
%token T_TRAIT
%token T_INTERFACE
%token T_EXTENDS
%token T_IMPLEMENTS
@ -329,6 +331,7 @@ unticked_class_declaration_statement:
class_entry_type:
T_CLASS { $$.u.op.opline_num = CG(zend_lineno); $$.EA = 0; }
| T_ABSTRACT T_CLASS { $$.u.op.opline_num = CG(zend_lineno); $$.EA = ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; }
| T_TRAIT { $$.u.op.opline_num = CG(zend_lineno); $$.EA = ZEND_ACC_TRAIT; }
| T_FINAL T_CLASS { $$.u.op.opline_num = CG(zend_lineno); $$.EA = ZEND_ACC_FINAL_CLASS; }
;
@ -514,10 +517,67 @@ class_statement_list:
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_INSTEADOF trait_reference_list { zend_prepare_trait_precedence(&$$, &$1, &$3 TSRMLS_CC); }
;
trait_reference_list:
fully_qualified_class_name { zend_init_list(&$$.u.op.ptr, Z_STRVAL($1.u.constant) TSRMLS_CC); }
| trait_reference_list ',' fully_qualified_class_name { zend_add_to_list(&$1.u.op.ptr, Z_STRVAL($3.u.constant) 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; }

1765
Zend/zend_language_scanner.c
File diff suppressed because it is too large
View File

8
Zend/zend_language_scanner.l

@ -990,6 +990,10 @@ NEWLINE ("\r"|"\n"|"\r\n")
return T_INTERFACE;
}
<ST_IN_SCRIPTING>"trait" {
return T_TRAIT;
}
<ST_IN_SCRIPTING>"extends" {
return T_EXTENDS;
}
@ -1108,6 +1112,10 @@ NEWLINE ("\r"|"\n"|"\r\n")
return T_USE;
}
<ST_IN_SCRIPTING>"insteadof" {
return T_INSTEADOF;
}
<ST_IN_SCRIPTING>"global" {
return T_GLOBAL;
}

2
Zend/zend_language_scanner_defs.h

@ -1,4 +1,4 @@
/* Generated by re2c 0.13.5 on Tue Apr 20 15:11:14 2010 */
/* Generated by re2c 0.13.5 on Thu Apr 22 23:49:41 2010 */
#line 3 "Zend/zend_language_scanner_defs.h"
enum YYCONDTYPE {

41
Zend/zend_opcode.c

@ -194,9 +194,50 @@ ZEND_API void destroy_zend_class(zend_class_entry **pce)
if (ce->num_interfaces > 0 && ce->interfaces) {
efree(ce->interfaces);
}
if (ce->num_traits > 0 && ce->traits) {
efree(ce->traits);
}
if (ce->doc_comment) {
efree(ce->doc_comment);
}
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) {
efree(ce->trait_aliases[i]->trait_method->method_name);
}
if (ce->trait_aliases[i]->trait_method->class_name) {
efree(ce->trait_aliases[i]->trait_method->class_name);
}
efree(ce->trait_aliases[i]->trait_method);
}
if (ce->trait_aliases[i]->alias) {
efree(ce->trait_aliases[i]->alias);
}
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);
efree(ce->trait_precedences[i]->trait_method->class_name);
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:

28
Zend/zend_vm_def.h

@ -4638,6 +4638,34 @@ ZEND_VM_HANDLER(144, ZEND_ADD_INTERFACE, ANY, CONST)
ZEND_VM_NEXT_OPCODE();
}
ZEND_VM_HANDLER(154, ZEND_ADD_TRAIT, ANY, ANY)
{
zend_op *opline = EX(opline);
zend_class_entry *ce = EX_T(opline->op1.var).class_entry;
zend_class_entry *trait = zend_fetch_class(Z_STRVAL_P(opline->op2.zv),
Z_STRLEN_P(opline->op2.zv),
opline->extended_value TSRMLS_CC);
if (trait) {
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();
}
ZEND_VM_HANDLER(155, ZEND_BIND_TRAITS, ANY, ANY)
{
zend_op *opline = EX(opline);
zend_class_entry *ce = EX_T(opline->op1.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;

78
Zend/zend_vm_execute.h

@ -681,6 +681,34 @@ static int ZEND_FASTCALL ZEND_NOP_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
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.var).class_entry;
zend_class_entry *trait = zend_fetch_class(Z_STRVAL_P(opline->op2.zv),
Z_STRLEN_P(opline->op2.zv),
opline->extended_value TSRMLS_CC);
if (trait) {
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)
{
zend_op *opline = EX(opline);
zend_class_entry *ce = EX_T(opline->op1.var).class_entry;
zend_do_bind_traits(ce TSRMLS_CC);
ZEND_VM_NEXT_OPCODE();
}
static int ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
zend_uint op_num = EG(opline_before_exception)-EG(active_op_array)->opcodes;
@ -36648,6 +36676,56 @@ void zend_init_opcodes_handlers(void)
ZEND_NULL_HANDLER,
ZEND_NULL_HANDLER,
ZEND_NULL_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_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;

2
Zend/zend_vm_opcodes.h

@ -154,3 +154,5 @@
#define ZEND_USER_OPCODE 150
#define ZEND_JMP_SET 152
#define ZEND_DECLARE_LAMBDA_FUNCTION 153
#define ZEND_ADD_TRAIT 154
#define ZEND_BIND_TRAITS 155
Loading…
Cancel
Save