Skip to content

Instantly share code, notes, and snippets.

@krakjoe
Created November 19, 2017 03:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save krakjoe/95ec4fe433124915f69915e5473a00af to your computer and use it in GitHub Desktop.
Save krakjoe/95ec4fe433124915f69915e5473a00af to your computer and use it in GitHub Desktop.
levi/generic_traits
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index 5be066e1ec..321e3d2c19 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -1749,6 +1749,133 @@ void zend_check_deprecated_constructor(const zend_class_entry *ce) /* {{{ */
}
/* }}} */
+static inline zend_type zend_specialized_trait_info_type(zend_string *type, zend_bool nullable) {
+ zend_type modified;
+
+ if (zend_string_equals_literal_ci(type, "string")) {
+ modified = ZEND_TYPE_ENCODE(IS_STRING, nullable);
+ } else if (zend_string_equals_literal_ci(type, "int")) {
+ modified = ZEND_TYPE_ENCODE(IS_LONG, nullable);
+ } else if (zend_string_equals_literal_ci(type, "float")) {
+ modified = ZEND_TYPE_ENCODE(IS_DOUBLE, nullable);
+ } else if (zend_string_equals_literal_ci(type, "array")) {
+ modified = ZEND_TYPE_ENCODE(IS_ARRAY, nullable);
+ } else if (zend_string_equals_literal_ci(type, "object")) {
+ modified = ZEND_TYPE_ENCODE(IS_OBJECT, nullable);
+ } else if (zend_string_equals_literal_ci(type, "bool")) {
+ modified = ZEND_TYPE_ENCODE(_IS_BOOL, nullable);
+ } else if (zend_string_equals_literal_ci(type, "void")) {
+ modified = ZEND_TYPE_ENCODE(IS_VOID, nullable);
+ } else {
+ modified = ZEND_TYPE_ENCODE_CLASS(type, nullable);
+ goto result;
+ }
+
+ zend_string_release(type);
+
+result:
+ return modified;
+}
+
+/* {{{ */
+static inline void zend_specialized_trait_info(zend_class_entry *trait, zend_arg_info *info) {
+ zval *type;
+
+ if (!ZEND_TYPE_IS_CLASS(info->type)) {
+ return;
+ }
+
+ type = zend_hash_find(trait->type_parameters, ZEND_TYPE_NAME(info->type));
+
+ if (!type || Z_TYPE_P(type) != IS_STRING) {
+ return;
+ }
+
+ info->type = zend_specialized_trait_info_type(Z_STR_P(type), ZEND_TYPE_ALLOW_NULL(info->type));
+} /* }}} */
+
+static inline int zend_specialized_trait_copy_function(zval *pDest, void *arg) {
+ zend_class_entry *specialized_ce = (zend_class_entry *) arg;
+ zend_function *function = Z_PTR_P(pDest);
+ zend_function *copy;
+ int var;
+
+ if (function->type != ZEND_USER_FUNCTION || !function->op_array.last_literal) {
+ return ZEND_HASH_APPLY_KEEP;
+ }
+
+ copy = zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
+
+ memcpy(copy, function, sizeof(zend_op_array));
+
+ Z_PTR_P(pDest) = function = copy;
+
+ for (var = 0; var < function->op_array.last_literal; var++) {
+ zval *type;
+
+ if (Z_TYPE(function->op_array.literals[var]) != IS_STRING)
+ continue;
+
+ type = zend_hash_find(specialized_ce->type_parameters, Z_STR(function->op_array.literals[var]));
+
+ if (!type || Z_TYPE_P(type) != IS_STRING) {
+ continue;
+ }
+
+ zval_ptr_dtor(&function->op_array.literals[var]);
+
+ ZVAL_COPY(&function->op_array.literals[var], type);
+ }
+
+ for (var = 0; var < function->op_array.last_var; var++) {
+ zval *type = zend_hash_find(specialized_ce->type_parameters, function->op_array.vars[var]);
+
+ if (!type || Z_TYPE_P(type) != IS_STRING) {
+ continue;
+ }
+
+ zend_string_release(function->op_array.vars[var]);
+
+ function->op_array.vars[var] = zend_string_copy(Z_STR_P(type));
+ }
+
+ if (!function->common.num_args && !(function->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
+ return ZEND_HASH_APPLY_KEEP;
+ }
+
+ {
+ zend_arg_info *info = function->common.arg_info;
+ uint32_t length = (function->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ?
+ function->common.num_args + 1 : function->common.num_args;
+
+ if (function->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+ info--;
+ }
+
+ function->common.arg_info = (zend_arg_info*) ecalloc(length, sizeof(zend_arg_info));
+
+ memcpy(function->common.arg_info, info, length);
+
+ if (function->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+ function->common.arg_info++;
+ }
+ }
+
+ if (function->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
+ zend_specialized_trait_info(specialized_ce, &function->common.arg_info[-1]);
+ }
+
+ if (function->common.num_args && function->common.arg_info) {
+ int arg;
+
+ for (arg = 0; arg < function->common.num_args; arg++) {
+ zend_specialized_trait_info(specialized_ce, &function->common.arg_info[arg]);
+ }
+ }
+
+ return ZEND_HASH_APPLY_KEEP;
+}
+
ZEND_API zend_class_entry * zend_specialize_trait(zend_class_entry *trait, HashTable * type_parameters) /* {{{ */
{
// todo: find proper arena?
@@ -1762,7 +1889,7 @@ ZEND_API zend_class_entry * zend_specialize_trait(zend_class_entry *trait, HashT
);
}
- specialized_ce = (zend_class_entry *) emalloc(sizeof(zend_class_entry));
+ specialized_ce = (zend_class_entry *) zend_arena_alloc(&CG(arena), sizeof(zend_class_entry));
// todo: figure out the proper way to duplicate the members
memcpy(specialized_ce, trait, sizeof(zend_class_entry));
@@ -1774,9 +1901,34 @@ ZEND_API zend_class_entry * zend_specialize_trait(zend_class_entry *trait, HashT
zval * key;
ZEND_HASH_FOREACH_NUM_KEY_VAL(trait->type_parameters, hash, key) {
zval * value = zend_hash_index_find(type_parameters, hash);
- zend_hash_update(specialized_ce->type_parameters, Z_STR_P(key), value);
+
+ if (!zend_hash_update(specialized_ce->type_parameters, Z_STR_P(key), value)) {
+ continue;
+ }
+
+ Z_ADDREF_P(value);
+
+ ZVAL_STR(value, zend_string_tolower(Z_STR_P(value)));
+
+ if (!zend_hash_update(specialized_ce->type_parameters, zend_string_tolower(Z_STR_P(key)), value)) {
+ continue;
+ }
} ZEND_HASH_FOREACH_END();
}
+
+ {
+ HashTable copied;
+
+ zend_hash_init(&copied,
+ zend_hash_num_elements(&specialized_ce->function_table),
+ NULL, ZEND_FUNCTION_DTOR, 0);
+ zend_hash_copy(&copied,
+ &specialized_ce->function_table,
+ NULL);
+ zend_hash_apply_with_argument(&copied, zend_specialized_trait_copy_function, specialized_ce);
+
+ specialized_ce->function_table = copied;
+ }
return specialized_ce;
}
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 3a975bf5a4..df5bbd4426 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -2387,13 +2387,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SPECIALIZE_TRAIT_SPEC_CONST_HA
}
type_parameters = Z_ARRVAL_P(RT_CONSTANT(opline, opline->op2));
- if (zend_hash_num_elements(type_parameters) != trait->num_interfaces) {
- zend_error_noreturn(E_ERROR,
- "Number of type arguments %d does not match expected %d",
- zend_hash_num_elements(type_parameters),
- trait->num_interfaces
- );
- }
Z_CE_P(EX_VAR(opline->result.var)) = zend_specialize_trait(trait, type_parameters);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment