dmitry                                   Tue, 17 Jan 2012 08:09:13 +0000

Revision: http://svn.php.net/viewvc?view=revision&revision=322378

Log:
Improved traits implementation. Now to support __CLASS__ constant in traits php 
doesn't have to copy the complete compiled method, but can reuse the same code. 
The resolution of __CLASS__ constants in methods defined in traits are delayed 
till run-time. This approach also made possible to use __CLASS__ constant as 
default value for traits properties and method arguments.

Changed paths:
    U   php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55214.phpt
    U   php/php-src/branches/PHP_5_4/Zend/zend_compile.c
    U   php/php-src/branches/PHP_5_4/Zend/zend_constants.c
    U   php/php-src/branches/PHP_5_4/Zend/zend_execute_API.c
    U   php/php-src/branches/PHP_5_4/Zend/zend_language_parser.y
    U   php/php-src/branches/PHP_5_4/Zend/zend_language_scanner.l
    U   php/php-src/branches/PHP_5_4/ext/reflection/tests/traits001.phpt
    U   php/php-src/trunk/Zend/tests/traits/bug55214.phpt
    U   php/php-src/trunk/Zend/zend_compile.c
    U   php/php-src/trunk/Zend/zend_constants.c
    U   php/php-src/trunk/Zend/zend_execute_API.c
    U   php/php-src/trunk/Zend/zend_language_parser.y
    U   php/php-src/trunk/Zend/zend_language_scanner.l
    U   php/php-src/trunk/ext/reflection/tests/traits001.phpt

Modified: php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55214.phpt
===================================================================
--- php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55214.phpt	2012-01-17 08:06:33 UTC (rev 322377)
+++ php/php-src/branches/PHP_5_4/Zend/tests/traits/bug55214.phpt	2012-01-17 08:09:13 UTC (rev 322378)
@@ -4,6 +4,9 @@
 <?php

 trait ATrait {
+  public static $static_var = __CLASS__;
+  public $var = __CLASS__;
+
   public static function get_class_name() {
     return __CLASS__;
   }
@@ -11,6 +14,14 @@
   public function get_class_name_obj() {
     return __CLASS__;
   }
+
+  public static function get_class_name2() {
+    return self::$static_var;
+  }
+
+  public function get_class_name_obj2() {
+    return $this->var;
+  }
 }

 trait Indirect {
@@ -27,22 +38,34 @@

 $r = SomeClass::get_class_name();
 var_dump($r);
+$r = SomeClass::get_class_name2();
+var_dump($r);

 $o = new SomeClass();
 $r = $o->get_class_name_obj();
 var_dump($r);
+$r = $o->get_class_name_obj2();
+var_dump($r);

 $r = UsingIndirect::get_class_name();
 var_dump($r);
+$r = UsingIndirect::get_class_name2();
+var_dump($r);

 $o = new UsingIndirect();
 $r = $o->get_class_name_obj();
 var_dump($r);
+$r = $o->get_class_name_obj2();
+var_dump($r);


 ?>
 --EXPECT--
 string(9) "SomeClass"
 string(9) "SomeClass"
+string(9) "SomeClass"
+string(9) "SomeClass"
 string(13) "UsingIndirect"
 string(13) "UsingIndirect"
+string(13) "UsingIndirect"
+string(13) "UsingIndirect"

Modified: php/php-src/branches/PHP_5_4/Zend/zend_compile.c
===================================================================
--- php/php-src/branches/PHP_5_4/Zend/zend_compile.c	2012-01-17 08:06:33 UTC (rev 322377)
+++ php/php-src/branches/PHP_5_4/Zend/zend_compile.c	2012-01-17 08:09:13 UTC (rev 322378)
@@ -3670,142 +3670,6 @@
 }
 /* }}} */

-/* {{{ Originates from php_runkit_function_copy_ctor
-	Duplicate structures in an op_array where necessary to make an outright duplicate */
-static void zend_traits_duplicate_function(zend_function *fe, zend_class_entry *target_ce, char *newname TSRMLS_DC)
-{
-	zend_literal *literals_copy;
-	zend_compiled_variable *dupvars;
-	zend_op *opcode_copy;
-	zval class_name_zv;
-	int class_name_literal;
-	int i;
-	int number_of_literals;
-
-	if (fe->op_array.static_variables) {
-		HashTable *tmpHash;
-
-		ALLOC_HASHTABLE(tmpHash);
-		zend_hash_init(tmpHash, zend_hash_num_elements(fe->op_array.static_variables), NULL, ZVAL_PTR_DTOR, 0);
-		zend_hash_apply_with_arguments(fe->op_array.static_variables TSRMLS_CC, (apply_func_args_t)zval_copy_static_var, 1, tmpHash);
-
-		fe->op_array.static_variables = tmpHash;
-	}
-
-	number_of_literals = fe->op_array.last_literal;
-	literals_copy = (zend_literal*)emalloc(number_of_literals * sizeof(zend_literal));
-
-	for (i = 0; i < number_of_literals; i++) {
-		literals_copy[i] = fe->op_array.literals[i];
-		zval_copy_ctor(&literals_copy[i].constant);
-	}
-	fe->op_array.literals = literals_copy;
-
-
-	fe->op_array.refcount = emalloc(sizeof(zend_uint));
-	*(fe->op_array.refcount) = 1;
-
-	if (fe->op_array.vars) {
-		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;
-	} else {
-		fe->op_array.vars = NULL;
-	}
-
-	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) {
-			switch (opcode_copy[i].opcode) {
-				case ZEND_GOTO:
-				case ZEND_JMP:
-					if (opcode_copy[i].op1.jmp_addr && 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);
-					}
-				break;
-			}
-		} else {
-			/* if __CLASS__ i.e. T_CLASS_C was used, we need to fix it up here */
-			if (target_ce
-				/* REM: used a IS_NULL place holder with a special marker LVAL */
-				&& Z_TYPE_P(opcode_copy[i].op1.zv) == IS_NULL
-				&& Z_LVAL_P(opcode_copy[i].op1.zv) == ZEND_ACC_TRAIT
-				/* Only on merge into an actual class */
-				&&  (ZEND_ACC_TRAIT != (target_ce->ce_flags & ZEND_ACC_TRAIT)))
-			{
-				INIT_ZVAL(class_name_zv);
-				ZVAL_STRINGL(&class_name_zv, target_ce->name, target_ce->name_length, 1);
-				class_name_literal = zend_append_individual_literal(&fe->op_array, &class_name_zv TSRMLS_CC);
-				opcode_copy[i].op1.zv = &fe->op_array.literals[class_name_literal].constant;
-			}
-		}
-
-		if (opcode_copy[i].op2_type != IS_CONST) {
-			switch (opcode_copy[i].opcode) {
-				case ZEND_JMPZ:
-				case ZEND_JMPNZ:
-				case ZEND_JMPZ_EX:
-				case ZEND_JMPNZ_EX:
-				case ZEND_JMP_SET:
-				case ZEND_JMP_SET_VAR:
-					if (opcode_copy[i].op2.jmp_addr && 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);
-					}
-				break;
-			}
-		} else {
-			/* if __CLASS__ i.e. T_CLASS_C was used, we need to fix it up here */
-			if (target_ce
-				/* REM: used a IS_NULL place holder with a special marker LVAL */
-				&& Z_TYPE_P(opcode_copy[i].op2.zv) == IS_NULL
-				&& Z_LVAL_P(opcode_copy[i].op2.zv) == ZEND_ACC_TRAIT
-				/* Only on merge into an actual class */
-				&&  (ZEND_ACC_TRAIT != (target_ce->ce_flags & ZEND_ACC_TRAIT)))
-			{
-				INIT_ZVAL(class_name_zv);
-				ZVAL_STRINGL(&class_name_zv, target_ce->name, target_ce->name_length, 1);
-				class_name_literal = zend_append_individual_literal(&fe->op_array, &class_name_zv TSRMLS_CC);
-				opcode_copy[i].op2.zv = &fe->op_array.literals[class_name_literal].constant;
-			}
-		}
-	}
-	fe->op_array.opcodes = opcode_copy;
-	fe->op_array.function_name = newname;
-
-	/* was setting it to fe which does not work since fe is stack allocated and not a stable address */
-	/* fe->op_array.prototype = fe->op_array.prototype; */
-	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);
-
-}
-/* }}} */
-
 static void zend_add_magic_methods(zend_class_entry* ce, const char* mname, uint mname_len, zend_function* fe TSRMLS_DC) /* {{{ */
 {
 	if (!strncmp(mname, ZEND_CLONE_FUNC_NAME, mname_len)) {
@@ -3916,7 +3780,7 @@
 			ce->ce_flags |= ZEND_HAS_STATIC_IN_METHODS;
 		}
 		fn_copy = *fn;
-		zend_traits_duplicate_function(&fn_copy, ce, estrdup(fn->common.function_name) TSRMLS_CC);
+		function_add_ref(&fn_copy);

 		if (zend_hash_quick_update(&ce->function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, &fn_copy, sizeof(zend_function), (void**)&fn_copy_p)==FAILURE) {
 			zend_error(E_COMPILE_ERROR, "Trait method %s has not been applied, because failure occured during updating class method table", hash_key->arKey);
@@ -3961,7 +3825,11 @@
 				&& aliases[i]->trait_method->mname_len == fnname_len
 				&& (zend_binary_strcasecmp(aliases[i]->trait_method->method_name, aliases[i]->trait_method->mname_len, fn->common.function_name, fnname_len) == 0)) {
 				fn_copy = *fn;
-				zend_traits_duplicate_function(&fn_copy, NULL, estrndup(aliases[i]->alias, aliases[i]->alias_len) TSRMLS_CC);
+				function_add_ref(&fn_copy);
+				/* this function_name is never destroyed, because its refcount
+				   greater than 1, classes are always destoyed in reverse order
+				   and trait is declared early than this class */
+				fn_copy.common.function_name = aliases[i]->alias;

 				/* if it is 0, no modifieres has been changed */
 				if (aliases[i]->modifiers) {
@@ -3993,7 +3861,7 @@
 	if (exclude_table == NULL || zend_hash_find(exclude_table, lcname, fnname_len, &dummy) == FAILURE) {
 		/* is not in hashtable, thus, function is not to be excluded */
 		fn_copy = *fn;
-		zend_traits_duplicate_function(&fn_copy, NULL, estrndup(fn->common.function_name, fnname_len) TSRMLS_CC);
+		function_add_ref(&fn_copy);

 		/* apply aliases which are not qualified by a class name, or which have not
 		 * alias name, just setting visibility */

Modified: php/php-src/branches/PHP_5_4/Zend/zend_constants.c
===================================================================
--- php/php-src/branches/PHP_5_4/Zend/zend_constants.c	2012-01-17 08:06:33 UTC (rev 322377)
+++ php/php-src/branches/PHP_5_4/Zend/zend_constants.c	2012-01-17 08:09:13 UTC (rev 322378)
@@ -224,13 +224,45 @@
 	zend_register_stringl_constant(name, name_len, strval, strlen(strval), flags, module_number TSRMLS_CC);
 }

-static int zend_get_halt_offset_constant(const char *name, uint name_len, zend_constant **c TSRMLS_DC)
+static int zend_get_special_constant(const char *name, uint name_len, zend_constant **c TSRMLS_DC)
 {
 	int ret;
 	static char haltoff[] = "__COMPILER_HALT_OFFSET__";

 	if (!EG(in_execution)) {
 		return 0;
+	} else if (name_len == sizeof("__CLASS__")-1 &&
+	          !memcmp(name, "__CLASS__", sizeof("__CLASS__")-1)) {
+		zend_constant tmp;
+
+		/* Returned constants may be cached, so they have to be stored */
+		if (EG(scope) && EG(scope)->name) {
+			int const_name_len;
+			char *const_name;
+			ALLOCA_FLAG(use_heap)
+
+			const_name_len = sizeof("\0__CLASS__") + EG(scope)->name_length;
+			const_name = do_alloca(const_name_len, use_heap);
+			memcpy(const_name, "\0__CLASS__", sizeof("\0__CLASS__")-1);
+			zend_str_tolower_copy(const_name + sizeof("\0__CLASS__")-1, EG(scope)->name, EG(scope)->name_length);
+			if (zend_hash_find(EG(zend_constants), const_name, const_name_len, (void**)c) == FAILURE) {
+				zend_hash_add(EG(zend_constants), const_name, const_name_len, (void*)&tmp, sizeof(zend_constant), (void**)c);
+				memset(*c, 0, sizeof(zend_constant));
+				Z_STRVAL((**c).value) = estrndup(EG(scope)->name, EG(scope)->name_length);
+				Z_STRLEN((**c).value) = EG(scope)->name_length;
+				Z_TYPE((**c).value) = IS_STRING;
+			}
+			free_alloca(const_name, use_heap);
+		} else {
+			if (zend_hash_find(EG(zend_constants), "\0__CLASS__", sizeof("\0__CLASS__"), (void**)c) == FAILURE) {
+				zend_hash_add(EG(zend_constants), "\0__CLASS__", sizeof("\0__CLASS__"), (void*)&tmp, sizeof(zend_constant), (void**)c);
+				memset(*c, 0, sizeof(zend_constant));
+				Z_STRVAL((**c).value) = estrndup("", 0);
+				Z_STRLEN((**c).value) = 0;
+				Z_TYPE((**c).value) = IS_STRING;
+			}
+		}
+		return 1;
 	} else if (name_len == sizeof("__COMPILER_HALT_OFFSET__")-1 &&
 	          !memcmp(name, "__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__")-1)) {
 		const char *cfilename;
@@ -265,7 +297,7 @@
 				retval=0;
 			}
 		} else {
-			retval = zend_get_halt_offset_constant(name, name_len, &c TSRMLS_CC);
+			retval = zend_get_special_constant(name, name_len, &c TSRMLS_CC);
 		}
 		efree(lookup_name);
 	}
@@ -432,14 +464,14 @@
 					    (c->flags & CONST_CS) != 0) {

 						key--;
-						if (!zend_get_halt_offset_constant(Z_STRVAL(key->constant), Z_STRLEN(key->constant), &c TSRMLS_CC)) {
+						if (!zend_get_special_constant(Z_STRVAL(key->constant), Z_STRLEN(key->constant), &c TSRMLS_CC)) {
 							return NULL;
 						}
 					}
 				}
 			} else {
 				key--;
-				if (!zend_get_halt_offset_constant(Z_STRVAL(key->constant), Z_STRLEN(key->constant), &c TSRMLS_CC)) {
+				if (!zend_get_special_constant(Z_STRVAL(key->constant), Z_STRLEN(key->constant), &c TSRMLS_CC)) {
 					return NULL;
 				}
 			}

Modified: php/php-src/branches/PHP_5_4/Zend/zend_execute_API.c
===================================================================
--- php/php-src/branches/PHP_5_4/Zend/zend_execute_API.c	2012-01-17 08:06:33 UTC (rev 322377)
+++ php/php-src/branches/PHP_5_4/Zend/zend_execute_API.c	2012-01-17 08:09:13 UTC (rev 322378)
@@ -110,7 +110,7 @@

 static int clean_non_persistent_function_full(zend_function *function TSRMLS_DC) /* {{{ */
 {
-	return (function->type != ZEND_INTERNAL_FUNCTION);
+	return (function->type == ZEND_INTERNAL_FUNCTION) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_REMOVE;
 }
 /* }}} */

@@ -122,7 +122,7 @@

 static int clean_non_persistent_class_full(zend_class_entry **ce TSRMLS_DC) /* {{{ */
 {
-	return ((*ce)->type != ZEND_INTERNAL_CLASS);
+	return ((*ce)->type == ZEND_INTERNAL_CLASS) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_REMOVE;
 }
 /* }}} */

@@ -298,8 +298,8 @@

 		/* Destroy all op arrays */
 		if (EG(full_tables_cleanup)) {
-			zend_hash_apply(EG(function_table), (apply_func_t) clean_non_persistent_function_full TSRMLS_CC);
-			zend_hash_apply(EG(class_table), (apply_func_t) clean_non_persistent_class_full TSRMLS_CC);
+			zend_hash_reverse_apply(EG(function_table), (apply_func_t) clean_non_persistent_function_full TSRMLS_CC);
+			zend_hash_reverse_apply(EG(class_table), (apply_func_t) clean_non_persistent_class_full TSRMLS_CC);
 		} else {
 			zend_hash_reverse_apply(EG(function_table), (apply_func_t) clean_non_persistent_function TSRMLS_CC);
 			zend_hash_reverse_apply(EG(class_table), (apply_func_t) clean_non_persistent_class TSRMLS_CC);

Modified: php/php-src/branches/PHP_5_4/Zend/zend_language_parser.y
===================================================================
--- php/php-src/branches/PHP_5_4/Zend/zend_language_parser.y	2012-01-17 08:06:33 UTC (rev 322377)
+++ php/php-src/branches/PHP_5_4/Zend/zend_language_parser.y	2012-01-17 08:09:13 UTC (rev 322378)
@@ -907,7 +907,6 @@
 	|	T_LINE 						{ $$ = $1; }
 	|	T_FILE 						{ $$ = $1; }
 	|	T_DIR   					{ $$ = $1; }
-	|	T_CLASS_C					{ $$ = $1; }
 	|	T_TRAIT_C					{ $$ = $1; }
 	|	T_METHOD_C					{ $$ = $1; }
 	|	T_FUNC_C					{ $$ = $1; }
@@ -927,6 +926,7 @@
 	|	T_ARRAY '(' static_array_pair_list ')' { $$ = $3; Z_TYPE($$.u.constant) = IS_CONSTANT_ARRAY; }
 	|	'[' static_array_pair_list ']' { $$ = $2; Z_TYPE($$.u.constant) = IS_CONSTANT_ARRAY; }
 	|	static_class_constant { $$ = $1; }
+	|	T_CLASS_C			{ $$ = $1; }
 ;

 static_class_constant:
@@ -942,6 +942,7 @@
 	|	common_scalar			{ $$ = $1; }
 	|	'"' encaps_list '"' 	{ $$ = $2; }
 	|	T_START_HEREDOC encaps_list T_END_HEREDOC { $$ = $2; CG(heredoc) = Z_STRVAL($1.u.constant); CG(heredoc_len) = Z_STRLEN($1.u.constant); }
+	|	T_CLASS_C				{ if (Z_TYPE($1.u.constant) == IS_CONSTANT) {zend_do_fetch_constant(&$$, NULL, &$1, ZEND_RT, 1 TSRMLS_CC);} else {$$ = $1;} }
 ;



Modified: php/php-src/branches/PHP_5_4/Zend/zend_language_scanner.l
===================================================================
--- php/php-src/branches/PHP_5_4/Zend/zend_language_scanner.l	2012-01-17 08:06:33 UTC (rev 322377)
+++ php/php-src/branches/PHP_5_4/Zend/zend_language_scanner.l	2012-01-17 08:09:13 UTC (rev 322378)
@@ -1564,11 +1564,11 @@
 	if (CG(active_class_entry)
 		&& (ZEND_ACC_TRAIT ==
 			(CG(active_class_entry)->ce_flags & ZEND_ACC_TRAIT))) {
-		// This is a hack, we abuse IS_NULL to indicate an invalid value
-		// if __CLASS__ is encountered in a trait, however, we also not that we
-		// should fix it up when we copy the method into an actual class
-		zendlval->value.lval = ZEND_ACC_TRAIT;
-		zendlval->type = IS_NULL;
+		/* We create a special __CLASS__ constant that is going to be resolved
+		   at run-time */
+		zendlval->value.str.len = sizeof("__CLASS__")-1;
+		zendlval->value.str.val = estrndup("__CLASS__", zendlval->value.str.len);
+		zendlval->type = IS_CONSTANT;
 	} else {
 		if (CG(active_class_entry)) {
 			class_name = CG(active_class_entry)->name;

Modified: php/php-src/branches/PHP_5_4/ext/reflection/tests/traits001.phpt
===================================================================
--- php/php-src/branches/PHP_5_4/ext/reflection/tests/traits001.phpt	2012-01-17 08:06:33 UTC (rev 322377)
+++ php/php-src/branches/PHP_5_4/ext/reflection/tests/traits001.phpt	2012-01-17 08:09:13 UTC (rev 322378)
@@ -63,7 +63,6 @@
       @@ %straits001.php 9 - 9
     }

-
     Method [ <user> public method someMethod ] {
       @@ %straits001.php 3 - 3
     }

Modified: php/php-src/trunk/Zend/tests/traits/bug55214.phpt
===================================================================
--- php/php-src/trunk/Zend/tests/traits/bug55214.phpt	2012-01-17 08:06:33 UTC (rev 322377)
+++ php/php-src/trunk/Zend/tests/traits/bug55214.phpt	2012-01-17 08:09:13 UTC (rev 322378)
@@ -4,6 +4,9 @@
 <?php

 trait ATrait {
+  public static $static_var = __CLASS__;
+  public $var = __CLASS__;
+
   public static function get_class_name() {
     return __CLASS__;
   }
@@ -11,6 +14,14 @@
   public function get_class_name_obj() {
     return __CLASS__;
   }
+
+  public static function get_class_name2() {
+    return self::$static_var;
+  }
+
+  public function get_class_name_obj2() {
+    return $this->var;
+  }
 }

 trait Indirect {
@@ -27,22 +38,34 @@

 $r = SomeClass::get_class_name();
 var_dump($r);
+$r = SomeClass::get_class_name2();
+var_dump($r);

 $o = new SomeClass();
 $r = $o->get_class_name_obj();
 var_dump($r);
+$r = $o->get_class_name_obj2();
+var_dump($r);

 $r = UsingIndirect::get_class_name();
 var_dump($r);
+$r = UsingIndirect::get_class_name2();
+var_dump($r);

 $o = new UsingIndirect();
 $r = $o->get_class_name_obj();
 var_dump($r);
+$r = $o->get_class_name_obj2();
+var_dump($r);


 ?>
 --EXPECT--
 string(9) "SomeClass"
 string(9) "SomeClass"
+string(9) "SomeClass"
+string(9) "SomeClass"
 string(13) "UsingIndirect"
 string(13) "UsingIndirect"
+string(13) "UsingIndirect"
+string(13) "UsingIndirect"

Modified: php/php-src/trunk/Zend/zend_compile.c
===================================================================
--- php/php-src/trunk/Zend/zend_compile.c	2012-01-17 08:06:33 UTC (rev 322377)
+++ php/php-src/trunk/Zend/zend_compile.c	2012-01-17 08:09:13 UTC (rev 322378)
@@ -3709,142 +3709,6 @@
 }
 /* }}} */

-/* {{{ Originates from php_runkit_function_copy_ctor
-	Duplicate structures in an op_array where necessary to make an outright duplicate */
-static void zend_traits_duplicate_function(zend_function *fe, zend_class_entry *target_ce, char *newname TSRMLS_DC)
-{
-	zend_literal *literals_copy;
-	zend_compiled_variable *dupvars;
-	zend_op *opcode_copy;
-	zval class_name_zv;
-	int class_name_literal;
-	int i;
-	int number_of_literals;
-
-	if (fe->op_array.static_variables) {
-		HashTable *tmpHash;
-
-		ALLOC_HASHTABLE(tmpHash);
-		zend_hash_init(tmpHash, zend_hash_num_elements(fe->op_array.static_variables), NULL, ZVAL_PTR_DTOR, 0);
-		zend_hash_apply_with_arguments(fe->op_array.static_variables TSRMLS_CC, (apply_func_args_t)zval_copy_static_var, 1, tmpHash);
-
-		fe->op_array.static_variables = tmpHash;
-	}
-
-	number_of_literals = fe->op_array.last_literal;
-	literals_copy = (zend_literal*)emalloc(number_of_literals * sizeof(zend_literal));
-
-	for (i = 0; i < number_of_literals; i++) {
-		literals_copy[i] = fe->op_array.literals[i];
-		zval_copy_ctor(&literals_copy[i].constant);
-	}
-	fe->op_array.literals = literals_copy;
-
-
-	fe->op_array.refcount = emalloc(sizeof(zend_uint));
-	*(fe->op_array.refcount) = 1;
-
-	if (fe->op_array.vars) {
-		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;
-	} else {
-		fe->op_array.vars = NULL;
-	}
-
-	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) {
-			switch (opcode_copy[i].opcode) {
-				case ZEND_GOTO:
-				case ZEND_JMP:
-					if (opcode_copy[i].op1.jmp_addr && 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);
-					}
-				break;
-			}
-		} else {
-			/* if __CLASS__ i.e. T_CLASS_C was used, we need to fix it up here */
-			if (target_ce
-				/* REM: used a IS_NULL place holder with a special marker LVAL */
-				&& Z_TYPE_P(opcode_copy[i].op1.zv) == IS_NULL
-				&& Z_LVAL_P(opcode_copy[i].op1.zv) == ZEND_ACC_TRAIT
-				/* Only on merge into an actual class */
-				&&  (ZEND_ACC_TRAIT != (target_ce->ce_flags & ZEND_ACC_TRAIT)))
-			{
-				INIT_ZVAL(class_name_zv);
-				ZVAL_STRINGL(&class_name_zv, target_ce->name, target_ce->name_length, 1);
-				class_name_literal = zend_append_individual_literal(&fe->op_array, &class_name_zv TSRMLS_CC);
-				opcode_copy[i].op1.zv = &fe->op_array.literals[class_name_literal].constant;
-			}
-		}
-
-		if (opcode_copy[i].op2_type != IS_CONST) {
-			switch (opcode_copy[i].opcode) {
-				case ZEND_JMPZ:
-				case ZEND_JMPNZ:
-				case ZEND_JMPZ_EX:
-				case ZEND_JMPNZ_EX:
-				case ZEND_JMP_SET:
-				case ZEND_JMP_SET_VAR:
-					if (opcode_copy[i].op2.jmp_addr && 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);
-					}
-				break;
-			}
-		} else {
-			/* if __CLASS__ i.e. T_CLASS_C was used, we need to fix it up here */
-			if (target_ce
-				/* REM: used a IS_NULL place holder with a special marker LVAL */
-				&& Z_TYPE_P(opcode_copy[i].op2.zv) == IS_NULL
-				&& Z_LVAL_P(opcode_copy[i].op2.zv) == ZEND_ACC_TRAIT
-				/* Only on merge into an actual class */
-				&&  (ZEND_ACC_TRAIT != (target_ce->ce_flags & ZEND_ACC_TRAIT)))
-			{
-				INIT_ZVAL(class_name_zv);
-				ZVAL_STRINGL(&class_name_zv, target_ce->name, target_ce->name_length, 1);
-				class_name_literal = zend_append_individual_literal(&fe->op_array, &class_name_zv TSRMLS_CC);
-				opcode_copy[i].op2.zv = &fe->op_array.literals[class_name_literal].constant;
-			}
-		}
-	}
-	fe->op_array.opcodes = opcode_copy;
-	fe->op_array.function_name = newname;
-
-	/* was setting it to fe which does not work since fe is stack allocated and not a stable address */
-	/* fe->op_array.prototype = fe->op_array.prototype; */
-	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);
-
-}
-/* }}} */
-
 static void zend_add_magic_methods(zend_class_entry* ce, const char* mname, uint mname_len, zend_function* fe TSRMLS_DC) /* {{{ */
 {
 	if (!strncmp(mname, ZEND_CLONE_FUNC_NAME, mname_len)) {
@@ -3955,7 +3819,7 @@
 			ce->ce_flags |= ZEND_HAS_STATIC_IN_METHODS;
 		}
 		fn_copy = *fn;
-		zend_traits_duplicate_function(&fn_copy, ce, estrdup(fn->common.function_name) TSRMLS_CC);
+		function_add_ref(&fn_copy);

 		if (zend_hash_quick_update(&ce->function_table, hash_key->arKey, hash_key->nKeyLength, hash_key->h, &fn_copy, sizeof(zend_function), (void**)&fn_copy_p)==FAILURE) {
 			zend_error(E_COMPILE_ERROR, "Trait method %s has not been applied, because failure occured during updating class method table", hash_key->arKey);
@@ -4000,7 +3864,11 @@
 				&& aliases[i]->trait_method->mname_len == fnname_len
 				&& (zend_binary_strcasecmp(aliases[i]->trait_method->method_name, aliases[i]->trait_method->mname_len, fn->common.function_name, fnname_len) == 0)) {
 				fn_copy = *fn;
-				zend_traits_duplicate_function(&fn_copy, NULL, estrndup(aliases[i]->alias, aliases[i]->alias_len) TSRMLS_CC);
+				function_add_ref(&fn_copy);
+				/* this function_name is never destroyed, because its refcount
+				   greater than 1, classes are always destoyed in reverse order
+				   and trait is declared early than this class */
+				fn_copy.common.function_name = aliases[i]->alias;

 				/* if it is 0, no modifieres has been changed */
 				if (aliases[i]->modifiers) {
@@ -4032,7 +3900,7 @@
 	if (exclude_table == NULL || zend_hash_find(exclude_table, lcname, fnname_len, &dummy) == FAILURE) {
 		/* is not in hashtable, thus, function is not to be excluded */
 		fn_copy = *fn;
-		zend_traits_duplicate_function(&fn_copy, NULL, estrndup(fn->common.function_name, fnname_len) TSRMLS_CC);
+		function_add_ref(&fn_copy);

 		/* apply aliases which are not qualified by a class name, or which have not
 		 * alias name, just setting visibility */

Modified: php/php-src/trunk/Zend/zend_constants.c
===================================================================
--- php/php-src/trunk/Zend/zend_constants.c	2012-01-17 08:06:33 UTC (rev 322377)
+++ php/php-src/trunk/Zend/zend_constants.c	2012-01-17 08:09:13 UTC (rev 322378)
@@ -224,13 +224,45 @@
 	zend_register_stringl_constant(name, name_len, strval, strlen(strval), flags, module_number TSRMLS_CC);
 }

-static int zend_get_halt_offset_constant(const char *name, uint name_len, zend_constant **c TSRMLS_DC)
+static int zend_get_special_constant(const char *name, uint name_len, zend_constant **c TSRMLS_DC)
 {
 	int ret;
 	static char haltoff[] = "__COMPILER_HALT_OFFSET__";

 	if (!EG(in_execution)) {
 		return 0;
+	} else if (name_len == sizeof("__CLASS__")-1 &&
+	          !memcmp(name, "__CLASS__", sizeof("__CLASS__")-1)) {
+		zend_constant tmp;
+
+		/* Returned constants may be cached, so they have to be stored */
+		if (EG(scope) && EG(scope)->name) {
+			int const_name_len;
+			char *const_name;
+			ALLOCA_FLAG(use_heap)
+
+			const_name_len = sizeof("\0__CLASS__") + EG(scope)->name_length;
+			const_name = do_alloca(const_name_len, use_heap);
+			memcpy(const_name, "\0__CLASS__", sizeof("\0__CLASS__")-1);
+			zend_str_tolower_copy(const_name + sizeof("\0__CLASS__")-1, EG(scope)->name, EG(scope)->name_length);
+			if (zend_hash_find(EG(zend_constants), const_name, const_name_len, (void**)c) == FAILURE) {
+				zend_hash_add(EG(zend_constants), const_name, const_name_len, (void*)&tmp, sizeof(zend_constant), (void**)c);
+				memset(*c, 0, sizeof(zend_constant));
+				Z_STRVAL((**c).value) = estrndup(EG(scope)->name, EG(scope)->name_length);
+				Z_STRLEN((**c).value) = EG(scope)->name_length;
+				Z_TYPE((**c).value) = IS_STRING;
+			}
+			free_alloca(const_name, use_heap);
+		} else {
+			if (zend_hash_find(EG(zend_constants), "\0__CLASS__", sizeof("\0__CLASS__"), (void**)c) == FAILURE) {
+				zend_hash_add(EG(zend_constants), "\0__CLASS__", sizeof("\0__CLASS__"), (void*)&tmp, sizeof(zend_constant), (void**)c);
+				memset(*c, 0, sizeof(zend_constant));
+				Z_STRVAL((**c).value) = estrndup("", 0);
+				Z_STRLEN((**c).value) = 0;
+				Z_TYPE((**c).value) = IS_STRING;
+			}
+		}
+		return 1;
 	} else if (name_len == sizeof("__COMPILER_HALT_OFFSET__")-1 &&
 	          !memcmp(name, "__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__")-1)) {
 		const char *cfilename;
@@ -265,7 +297,7 @@
 				retval=0;
 			}
 		} else {
-			retval = zend_get_halt_offset_constant(name, name_len, &c TSRMLS_CC);
+			retval = zend_get_special_constant(name, name_len, &c TSRMLS_CC);
 		}
 		efree(lookup_name);
 	}
@@ -432,14 +464,14 @@
 					    (c->flags & CONST_CS) != 0) {

 						key--;
-						if (!zend_get_halt_offset_constant(Z_STRVAL(key->constant), Z_STRLEN(key->constant), &c TSRMLS_CC)) {
+						if (!zend_get_special_constant(Z_STRVAL(key->constant), Z_STRLEN(key->constant), &c TSRMLS_CC)) {
 							return NULL;
 						}
 					}
 				}
 			} else {
 				key--;
-				if (!zend_get_halt_offset_constant(Z_STRVAL(key->constant), Z_STRLEN(key->constant), &c TSRMLS_CC)) {
+				if (!zend_get_special_constant(Z_STRVAL(key->constant), Z_STRLEN(key->constant), &c TSRMLS_CC)) {
 					return NULL;
 				}
 			}

Modified: php/php-src/trunk/Zend/zend_execute_API.c
===================================================================
--- php/php-src/trunk/Zend/zend_execute_API.c	2012-01-17 08:06:33 UTC (rev 322377)
+++ php/php-src/trunk/Zend/zend_execute_API.c	2012-01-17 08:09:13 UTC (rev 322378)
@@ -110,7 +110,7 @@

 static int clean_non_persistent_function_full(zend_function *function TSRMLS_DC) /* {{{ */
 {
-	return (function->type != ZEND_INTERNAL_FUNCTION);
+	return (function->type == ZEND_INTERNAL_FUNCTION) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_REMOVE;
 }
 /* }}} */

@@ -122,7 +122,7 @@

 static int clean_non_persistent_class_full(zend_class_entry **ce TSRMLS_DC) /* {{{ */
 {
-	return ((*ce)->type != ZEND_INTERNAL_CLASS);
+	return ((*ce)->type == ZEND_INTERNAL_CLASS) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_REMOVE;
 }
 /* }}} */

@@ -298,8 +298,8 @@

 		/* Destroy all op arrays */
 		if (EG(full_tables_cleanup)) {
-			zend_hash_apply(EG(function_table), (apply_func_t) clean_non_persistent_function_full TSRMLS_CC);
-			zend_hash_apply(EG(class_table), (apply_func_t) clean_non_persistent_class_full TSRMLS_CC);
+			zend_hash_reverse_apply(EG(function_table), (apply_func_t) clean_non_persistent_function_full TSRMLS_CC);
+			zend_hash_reverse_apply(EG(class_table), (apply_func_t) clean_non_persistent_class_full TSRMLS_CC);
 		} else {
 			zend_hash_reverse_apply(EG(function_table), (apply_func_t) clean_non_persistent_function TSRMLS_CC);
 			zend_hash_reverse_apply(EG(class_table), (apply_func_t) clean_non_persistent_class TSRMLS_CC);

Modified: php/php-src/trunk/Zend/zend_language_parser.y
===================================================================
--- php/php-src/trunk/Zend/zend_language_parser.y	2012-01-17 08:06:33 UTC (rev 322377)
+++ php/php-src/trunk/Zend/zend_language_parser.y	2012-01-17 08:09:13 UTC (rev 322378)
@@ -907,7 +907,6 @@
 	|	T_LINE 						{ $$ = $1; }
 	|	T_FILE 						{ $$ = $1; }
 	|	T_DIR   					{ $$ = $1; }
-	|	T_CLASS_C					{ $$ = $1; }
 	|	T_TRAIT_C					{ $$ = $1; }
 	|	T_METHOD_C					{ $$ = $1; }
 	|	T_FUNC_C					{ $$ = $1; }
@@ -927,6 +926,7 @@
 	|	T_ARRAY '(' static_array_pair_list ')' { $$ = $3; Z_TYPE($$.u.constant) = IS_CONSTANT_ARRAY; }
 	|	'[' static_array_pair_list ']' { $$ = $2; Z_TYPE($$.u.constant) = IS_CONSTANT_ARRAY; }
 	|	static_class_constant { $$ = $1; }
+	|	T_CLASS_C			{ $$ = $1; }
 ;

 static_class_constant:
@@ -942,6 +942,7 @@
 	|	common_scalar			{ $$ = $1; }
 	|	'"' encaps_list '"' 	{ $$ = $2; }
 	|	T_START_HEREDOC encaps_list T_END_HEREDOC { $$ = $2; CG(heredoc) = Z_STRVAL($1.u.constant); CG(heredoc_len) = Z_STRLEN($1.u.constant); }
+	|	T_CLASS_C				{ if (Z_TYPE($1.u.constant) == IS_CONSTANT) {zend_do_fetch_constant(&$$, NULL, &$1, ZEND_RT, 1 TSRMLS_CC);} else {$$ = $1;} }
 ;



Modified: php/php-src/trunk/Zend/zend_language_scanner.l
===================================================================
--- php/php-src/trunk/Zend/zend_language_scanner.l	2012-01-17 08:06:33 UTC (rev 322377)
+++ php/php-src/trunk/Zend/zend_language_scanner.l	2012-01-17 08:09:13 UTC (rev 322378)
@@ -1564,11 +1564,11 @@
 	if (CG(active_class_entry)
 		&& (ZEND_ACC_TRAIT ==
 			(CG(active_class_entry)->ce_flags & ZEND_ACC_TRAIT))) {
-		// This is a hack, we abuse IS_NULL to indicate an invalid value
-		// if __CLASS__ is encountered in a trait, however, we also not that we
-		// should fix it up when we copy the method into an actual class
-		zendlval->value.lval = ZEND_ACC_TRAIT;
-		zendlval->type = IS_NULL;
+		/* We create a special __CLASS__ constant that is going to be resolved
+		   at run-time */
+		zendlval->value.str.len = sizeof("__CLASS__")-1;
+		zendlval->value.str.val = estrndup("__CLASS__", zendlval->value.str.len);
+		zendlval->type = IS_CONSTANT;
 	} else {
 		if (CG(active_class_entry)) {
 			class_name = CG(active_class_entry)->name;

Modified: php/php-src/trunk/ext/reflection/tests/traits001.phpt
===================================================================
--- php/php-src/trunk/ext/reflection/tests/traits001.phpt	2012-01-17 08:06:33 UTC (rev 322377)
+++ php/php-src/trunk/ext/reflection/tests/traits001.phpt	2012-01-17 08:09:13 UTC (rev 322378)
@@ -63,7 +63,6 @@
       @@ %straits001.php 9 - 9
     }

-
     Method [ <user> public method someMethod ] {
       @@ %straits001.php 3 - 3
     }
-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to