From 9b101ac8b364610c20f710b8c6c631db5e6230a5 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 15 May 2012 18:30:48 +0200 Subject: [PATCH 01/70] Add T_YIELD "yield" keyword --- Zend/zend_compile.c | 6 + Zend/zend_language_parser.y | 2 + Zend/zend_language_scanner.c | 5144 +++++++++++++++-------------- Zend/zend_language_scanner.l | 4 + Zend/zend_language_scanner_defs.h | 2 +- ext/standard/url_scanner_ex.c | 2 +- 6 files changed, 2604 insertions(+), 2556 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 602b6004100b..2b2b58f9ac80 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2655,6 +2655,12 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */ } /* }}} */ +void zend_do_yield(znode *expr TSRMLS_DC) /* {{{ */ +{ + /* do nothing for now */ +} +/* }}} */ + static int zend_add_try_element(zend_uint try_op TSRMLS_DC) /* {{{ */ { int try_catch_offset = CG(active_op_array)->last_try_catch++; diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 893e0133efe0..61198cb10e86 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -158,6 +158,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %token T_FUNCTION "function (T_FUNCTION)" %token T_CONST "const (T_CONST)" %token T_RETURN "return (T_RETURN)" +%token T_YIELD "yield (T_YIELD)" %token T_TRY "try (T_TRY)" %token T_CATCH "catch (T_CATCH)" %token T_THROW "throw (T_THROW)" @@ -297,6 +298,7 @@ unticked_statement: | T_RETURN ';' { zend_do_return(NULL, 0 TSRMLS_CC); } | T_RETURN expr_without_variable ';' { zend_do_return(&$2, 0 TSRMLS_CC); } | T_RETURN variable ';' { zend_do_return(&$2, 1 TSRMLS_CC); } + | T_YIELD expr ';' { zend_do_yield(&$2 TSRMLS_CC); } | T_GLOBAL global_var_list ';' | T_STATIC static_var_list ';' | T_ECHO echo_expr_list ';' diff --git a/Zend/zend_language_scanner.c b/Zend/zend_language_scanner.c index 0bfbac99750e..a388e96a262b 100644 --- a/Zend/zend_language_scanner.c +++ b/Zend/zend_language_scanner.c @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 on Mon Apr 30 15:56:25 2012 */ +/* Generated by re2c 0.13.5 on Tue May 15 13:07:23 2012 */ #line 1 "Zend/zend_language_scanner.l" /* +----------------------------------------------------------------------+ @@ -1097,7 +1097,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) yy3: YYDEBUG(3, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1791 "Zend/zend_language_scanner.l" +#line 1795 "Zend/zend_language_scanner.l" { if (YYCURSOR > YYLIMIT) { return 0; @@ -1175,7 +1175,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) yy6: YYDEBUG(6, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1779 "Zend/zend_language_scanner.l" +#line 1783 "Zend/zend_language_scanner.l" { if (CG(short_tags)) { zendlval->value.str.val = yytext; /* no copying - intentional */ @@ -1194,7 +1194,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) if ((yych = *YYCURSOR) == '=') goto yy43; YYDEBUG(8, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1756 "Zend/zend_language_scanner.l" +#line 1760 "Zend/zend_language_scanner.l" { if (CG(asp_tags)) { zendlval->value.str.val = yytext; /* no copying - intentional */ @@ -1392,7 +1392,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) ++YYCURSOR; YYDEBUG(38, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1716 "Zend/zend_language_scanner.l" +#line 1720 "Zend/zend_language_scanner.l" { YYCTYPE *bracket = (YYCTYPE*)zend_memrchr(yytext, '<', yyleng - (sizeof("script language=php>") - 1)); @@ -1436,7 +1436,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) ++YYCURSOR; YYDEBUG(44, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1734 "Zend/zend_language_scanner.l" +#line 1738 "Zend/zend_language_scanner.l" { if (CG(asp_tags)) { zendlval->value.str.val = yytext; /* no copying - intentional */ @@ -1454,7 +1454,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) ++YYCURSOR; YYDEBUG(46, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1747 "Zend/zend_language_scanner.l" +#line 1751 "Zend/zend_language_scanner.l" { zendlval->value.str.val = yytext; /* no copying - intentional */ zendlval->value.str.len = yyleng; @@ -1489,7 +1489,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) yy51: YYDEBUG(51, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1769 "Zend/zend_language_scanner.l" +#line 1773 "Zend/zend_language_scanner.l" { zendlval->value.str.val = yytext; /* no copying - intentional */ zendlval->value.str.len = yyleng; @@ -1569,7 +1569,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) yy56: YYDEBUG(56, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2242 "Zend/zend_language_scanner.l" +#line 2246 "Zend/zend_language_scanner.l" { if (YYCURSOR > YYLIMIT) { return 0; @@ -1621,7 +1621,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) ++YYCURSOR; YYDEBUG(59, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2186 "Zend/zend_language_scanner.l" +#line 2190 "Zend/zend_language_scanner.l" { BEGIN(ST_IN_SCRIPTING); return '`'; @@ -1636,7 +1636,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) ++YYCURSOR; YYDEBUG(62, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2173 "Zend/zend_language_scanner.l" +#line 2177 "Zend/zend_language_scanner.l" { zendlval->value.lval = (long) '{'; yy_push_state(ST_IN_SCRIPTING TSRMLS_CC); @@ -1659,7 +1659,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) yy65: YYDEBUG(65, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1873 "Zend/zend_language_scanner.l" +#line 1877 "Zend/zend_language_scanner.l" { zend_copy_value(zendlval, (yytext+1), (yyleng-1)); zendlval->type = IS_STRING; @@ -1671,7 +1671,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) ++YYCURSOR; YYDEBUG(67, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1449 "Zend/zend_language_scanner.l" +#line 1453 "Zend/zend_language_scanner.l" { yy_push_state(ST_LOOKING_FOR_VARNAME TSRMLS_CC); return T_DOLLAR_OPEN_CURLY_BRACES; @@ -1690,7 +1690,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) ++YYCURSOR; YYDEBUG(71, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1865 "Zend/zend_language_scanner.l" +#line 1869 "Zend/zend_language_scanner.l" { yyless(yyleng - 1); yy_push_state(ST_VAR_OFFSET TSRMLS_CC); @@ -1716,7 +1716,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) ++YYCURSOR; YYDEBUG(74, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1855 "Zend/zend_language_scanner.l" +#line 1859 "Zend/zend_language_scanner.l" { yyless(yyleng - 3); yy_push_state(ST_LOOKING_FOR_PROPERTY TSRMLS_CC); @@ -1792,7 +1792,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) yy78: YYDEBUG(78, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2192 "Zend/zend_language_scanner.l" +#line 2196 "Zend/zend_language_scanner.l" { if (GET_DOUBLE_QUOTES_SCANNED_LENGTH()) { YYCURSOR += GET_DOUBLE_QUOTES_SCANNED_LENGTH() - 1; @@ -1852,7 +1852,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) ++YYCURSOR; YYDEBUG(81, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2181 "Zend/zend_language_scanner.l" +#line 2185 "Zend/zend_language_scanner.l" { BEGIN(ST_IN_SCRIPTING); return '"'; @@ -1867,7 +1867,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) ++YYCURSOR; YYDEBUG(84, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2173 "Zend/zend_language_scanner.l" +#line 2177 "Zend/zend_language_scanner.l" { zendlval->value.lval = (long) '{'; yy_push_state(ST_IN_SCRIPTING TSRMLS_CC); @@ -1890,7 +1890,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) yy87: YYDEBUG(87, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1873 "Zend/zend_language_scanner.l" +#line 1877 "Zend/zend_language_scanner.l" { zend_copy_value(zendlval, (yytext+1), (yyleng-1)); zendlval->type = IS_STRING; @@ -1902,7 +1902,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) ++YYCURSOR; YYDEBUG(89, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1449 "Zend/zend_language_scanner.l" +#line 1453 "Zend/zend_language_scanner.l" { yy_push_state(ST_LOOKING_FOR_VARNAME TSRMLS_CC); return T_DOLLAR_OPEN_CURLY_BRACES; @@ -1921,7 +1921,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) ++YYCURSOR; YYDEBUG(93, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1865 "Zend/zend_language_scanner.l" +#line 1869 "Zend/zend_language_scanner.l" { yyless(yyleng - 1); yy_push_state(ST_VAR_OFFSET TSRMLS_CC); @@ -1947,7 +1947,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) ++YYCURSOR; YYDEBUG(96, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1855 "Zend/zend_language_scanner.l" +#line 1859 "Zend/zend_language_scanner.l" { yyless(yyleng - 3); yy_push_state(ST_LOOKING_FOR_PROPERTY TSRMLS_CC); @@ -1966,7 +1966,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) ++YYCURSOR; YYDEBUG(100, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2160 "Zend/zend_language_scanner.l" +#line 2164 "Zend/zend_language_scanner.l" { YYCURSOR += CG(heredoc_len) - 1; yyleng = CG(heredoc_len); @@ -2040,7 +2040,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) yy104: YYDEBUG(104, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2284 "Zend/zend_language_scanner.l" +#line 2288 "Zend/zend_language_scanner.l" { int newline = 0; @@ -2126,7 +2126,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) ++YYCURSOR; YYDEBUG(108, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2173 "Zend/zend_language_scanner.l" +#line 2177 "Zend/zend_language_scanner.l" { zendlval->value.lval = (long) '{'; yy_push_state(ST_IN_SCRIPTING TSRMLS_CC); @@ -2149,7 +2149,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) yy111: YYDEBUG(111, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1873 "Zend/zend_language_scanner.l" +#line 1877 "Zend/zend_language_scanner.l" { zend_copy_value(zendlval, (yytext+1), (yyleng-1)); zendlval->type = IS_STRING; @@ -2161,7 +2161,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) ++YYCURSOR; YYDEBUG(113, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1449 "Zend/zend_language_scanner.l" +#line 1453 "Zend/zend_language_scanner.l" { yy_push_state(ST_LOOKING_FOR_VARNAME TSRMLS_CC); return T_DOLLAR_OPEN_CURLY_BRACES; @@ -2180,7 +2180,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) ++YYCURSOR; YYDEBUG(117, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1865 "Zend/zend_language_scanner.l" +#line 1869 "Zend/zend_language_scanner.l" { yyless(yyleng - 1); yy_push_state(ST_VAR_OFFSET TSRMLS_CC); @@ -2206,7 +2206,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) ++YYCURSOR; YYDEBUG(120, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1855 "Zend/zend_language_scanner.l" +#line 1859 "Zend/zend_language_scanner.l" { yyless(yyleng - 3); yy_push_state(ST_LOOKING_FOR_PROPERTY TSRMLS_CC); @@ -2286,32 +2286,32 @@ int lex_scan(zval *zendlval TSRMLS_DC) case 0x1C: case 0x1D: case 0x1E: - case 0x1F: goto yy183; + case 0x1F: goto yy184; case '\t': case '\n': case '\r': - case ' ': goto yy139; - case '!': goto yy152; - case '"': goto yy179; - case '#': goto yy175; - case '$': goto yy164; - case '%': goto yy158; - case '&': goto yy159; - case '\'': goto yy177; - case '(': goto yy146; + case ' ': goto yy140; + case '!': goto yy153; + case '"': goto yy180; + case '#': goto yy176; + case '$': goto yy165; + case '%': goto yy159; + case '&': goto yy160; + case '\'': goto yy178; + case '(': goto yy147; case ')': case ',': case ';': case '@': case '[': case ']': - case '~': goto yy165; - case '*': goto yy155; - case '+': goto yy151; - case '-': goto yy137; - case '.': goto yy157; - case '/': goto yy156; - case '0': goto yy171; + case '~': goto yy166; + case '*': goto yy156; + case '+': goto yy152; + case '-': goto yy138; + case '.': goto yy158; + case '/': goto yy157; + case '0': goto yy172; case '1': case '2': case '3': @@ -2320,16 +2320,16 @@ int lex_scan(zval *zendlval TSRMLS_DC) case '6': case '7': case '8': - case '9': goto yy173; - case ':': goto yy141; - case '<': goto yy153; - case '=': goto yy149; - case '>': goto yy154; - case '?': goto yy166; + case '9': goto yy174; + case ':': goto yy142; + case '<': goto yy154; + case '=': goto yy150; + case '>': goto yy155; + case '?': goto yy167; case 'A': - case 'a': goto yy132; + case 'a': goto yy133; case 'B': - case 'b': goto yy134; + case 'b': goto yy135; case 'C': case 'c': goto yy127; case 'D': @@ -2339,39 +2339,41 @@ int lex_scan(zval *zendlval TSRMLS_DC) case 'F': case 'f': goto yy126; case 'G': - case 'g': goto yy135; + case 'g': goto yy136; case 'I': - case 'i': goto yy130; + case 'i': goto yy131; case 'L': - case 'l': goto yy150; + case 'l': goto yy151; case 'N': - case 'n': goto yy144; + case 'n': goto yy145; case 'O': - case 'o': goto yy162; + case 'o': goto yy163; case 'P': - case 'p': goto yy136; + case 'p': goto yy137; case 'R': case 'r': goto yy128; case 'S': - case 's': goto yy133; + case 's': goto yy134; case 'T': - case 't': goto yy129; + case 't': goto yy130; case 'U': - case 'u': goto yy147; + case 'u': goto yy148; case 'V': - case 'v': goto yy145; + case 'v': goto yy146; case 'W': - case 'w': goto yy131; + case 'w': goto yy132; case 'X': - case 'x': goto yy163; - case '\\': goto yy142; - case '^': goto yy161; - case '_': goto yy148; - case '`': goto yy181; - case '{': goto yy167; - case '|': goto yy160; - case '}': goto yy169; - default: goto yy174; + case 'x': goto yy164; + case 'Y': + case 'y': goto yy129; + case '\\': goto yy143; + case '^': goto yy162; + case '_': goto yy149; + case '`': goto yy182; + case '{': goto yy168; + case '|': goto yy161; + case '}': goto yy170; + default: goto yy175; } yy123: YYDEBUG(123, *YYCURSOR); @@ -2379,49 +2381,49 @@ int lex_scan(zval *zendlval TSRMLS_DC) YYDEBUG(-1, yych); switch ((yych = *YYCURSOR)) { case 'C': - case 'c': goto yy726; + case 'c': goto yy732; case 'L': - case 'l': goto yy727; + case 'l': goto yy733; case 'M': - case 'm': goto yy728; + case 'm': goto yy734; case 'N': - case 'n': goto yy729; + case 'n': goto yy735; case 'V': - case 'v': goto yy730; + case 'v': goto yy736; case 'X': - case 'x': goto yy731; - default: goto yy186; + case 'x': goto yy737; + default: goto yy187; } yy124: YYDEBUG(124, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1896 "Zend/zend_language_scanner.l" +#line 1900 "Zend/zend_language_scanner.l" { zend_copy_value(zendlval, yytext, yyleng); zendlval->type = IS_STRING; return T_STRING; } -#line 2405 "Zend/zend_language_scanner.c" +#line 2407 "Zend/zend_language_scanner.c" yy125: YYDEBUG(125, *YYCURSOR); yych = *++YYCURSOR; if (yych <= 'O') { if (yych <= 'H') { - if (yych == 'E') goto yy708; - goto yy186; + if (yych == 'E') goto yy714; + goto yy187; } else { - if (yych <= 'I') goto yy709; - if (yych <= 'N') goto yy186; - goto yy710; + if (yych <= 'I') goto yy715; + if (yych <= 'N') goto yy187; + goto yy716; } } else { if (yych <= 'h') { - if (yych == 'e') goto yy708; - goto yy186; + if (yych == 'e') goto yy714; + goto yy187; } else { - if (yych <= 'i') goto yy709; - if (yych == 'o') goto yy710; - goto yy186; + if (yych <= 'i') goto yy715; + if (yych == 'o') goto yy716; + goto yy187; } } yy126: @@ -2429,21 +2431,21 @@ int lex_scan(zval *zendlval TSRMLS_DC) yych = *++YYCURSOR; if (yych <= 'U') { if (yych <= 'N') { - if (yych == 'I') goto yy687; - goto yy186; + if (yych == 'I') goto yy693; + goto yy187; } else { - if (yych <= 'O') goto yy688; - if (yych <= 'T') goto yy186; - goto yy689; + if (yych <= 'O') goto yy694; + if (yych <= 'T') goto yy187; + goto yy695; } } else { if (yych <= 'n') { - if (yych == 'i') goto yy687; - goto yy186; + if (yych == 'i') goto yy693; + goto yy187; } else { - if (yych <= 'o') goto yy688; - if (yych == 'u') goto yy689; - goto yy186; + if (yych <= 'o') goto yy694; + if (yych == 'u') goto yy695; + goto yy187; } } yy127: @@ -2451,196 +2453,202 @@ int lex_scan(zval *zendlval TSRMLS_DC) yych = *++YYCURSOR; if (yych <= 'O') { if (yych <= 'K') { - if (yych == 'A') goto yy652; - goto yy186; + if (yych == 'A') goto yy658; + goto yy187; } else { - if (yych <= 'L') goto yy653; - if (yych <= 'N') goto yy186; - goto yy654; + if (yych <= 'L') goto yy659; + if (yych <= 'N') goto yy187; + goto yy660; } } else { if (yych <= 'k') { - if (yych == 'a') goto yy652; - goto yy186; + if (yych == 'a') goto yy658; + goto yy187; } else { - if (yych <= 'l') goto yy653; - if (yych == 'o') goto yy654; - goto yy186; + if (yych <= 'l') goto yy659; + if (yych == 'o') goto yy660; + goto yy187; } } yy128: YYDEBUG(128, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy634; - if (yych == 'e') goto yy634; - goto yy186; + if (yych == 'E') goto yy640; + if (yych == 'e') goto yy640; + goto yy187; yy129: YYDEBUG(129, *YYCURSOR); yych = *++YYCURSOR; + if (yych == 'I') goto yy635; + if (yych == 'i') goto yy635; + goto yy187; +yy130: + YYDEBUG(130, *YYCURSOR); + yych = *++YYCURSOR; if (yych <= 'R') { - if (yych == 'H') goto yy622; - if (yych <= 'Q') goto yy186; - goto yy623; + if (yych == 'H') goto yy623; + if (yych <= 'Q') goto yy187; + goto yy624; } else { if (yych <= 'h') { - if (yych <= 'g') goto yy186; - goto yy622; + if (yych <= 'g') goto yy187; + goto yy623; } else { - if (yych == 'r') goto yy623; - goto yy186; + if (yych == 'r') goto yy624; + goto yy187; } } -yy130: - YYDEBUG(130, *YYCURSOR); +yy131: + YYDEBUG(131, *YYCURSOR); yych = *++YYCURSOR; if (yych <= 'S') { if (yych <= 'L') { - if (yych == 'F') goto yy569; - goto yy186; + if (yych == 'F') goto yy570; + goto yy187; } else { - if (yych <= 'M') goto yy571; - if (yych <= 'N') goto yy572; - if (yych <= 'R') goto yy186; - goto yy573; + if (yych <= 'M') goto yy572; + if (yych <= 'N') goto yy573; + if (yych <= 'R') goto yy187; + goto yy574; } } else { if (yych <= 'm') { - if (yych == 'f') goto yy569; - if (yych <= 'l') goto yy186; - goto yy571; + if (yych == 'f') goto yy570; + if (yych <= 'l') goto yy187; + goto yy572; } else { - if (yych <= 'n') goto yy572; - if (yych == 's') goto yy573; - goto yy186; + if (yych <= 'n') goto yy573; + if (yych == 's') goto yy574; + goto yy187; } } -yy131: - YYDEBUG(131, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'H') goto yy564; - if (yych == 'h') goto yy564; - goto yy186; yy132: YYDEBUG(132, *YYCURSOR); yych = *++YYCURSOR; + if (yych == 'H') goto yy565; + if (yych == 'h') goto yy565; + goto yy187; +yy133: + YYDEBUG(133, *YYCURSOR); + yych = *++YYCURSOR; if (yych <= 'S') { if (yych <= 'M') { - if (yych == 'B') goto yy546; - goto yy186; + if (yych == 'B') goto yy547; + goto yy187; } else { - if (yych <= 'N') goto yy547; - if (yych <= 'Q') goto yy186; - if (yych <= 'R') goto yy548; - goto yy549; + if (yych <= 'N') goto yy548; + if (yych <= 'Q') goto yy187; + if (yych <= 'R') goto yy549; + goto yy550; } } else { if (yych <= 'n') { - if (yych == 'b') goto yy546; - if (yych <= 'm') goto yy186; - goto yy547; + if (yych == 'b') goto yy547; + if (yych <= 'm') goto yy187; + goto yy548; } else { - if (yych <= 'q') goto yy186; - if (yych <= 'r') goto yy548; - if (yych <= 's') goto yy549; - goto yy186; + if (yych <= 'q') goto yy187; + if (yych <= 'r') goto yy549; + if (yych <= 's') goto yy550; + goto yy187; } } -yy133: - YYDEBUG(133, *YYCURSOR); +yy134: + YYDEBUG(134, *YYCURSOR); yych = *++YYCURSOR; if (yych <= 'W') { - if (yych == 'T') goto yy534; - if (yych <= 'V') goto yy186; - goto yy535; + if (yych == 'T') goto yy535; + if (yych <= 'V') goto yy187; + goto yy536; } else { if (yych <= 't') { - if (yych <= 's') goto yy186; - goto yy534; + if (yych <= 's') goto yy187; + goto yy535; } else { - if (yych == 'w') goto yy535; - goto yy186; + if (yych == 'w') goto yy536; + goto yy187; } } -yy134: - YYDEBUG(134, *YYCURSOR); +yy135: + YYDEBUG(135, *YYCURSOR); yyaccept = 0; yych = *(YYMARKER = ++YYCURSOR); if (yych <= ';') { if (yych <= '"') { - if (yych <= '!') goto yy186; - goto yy526; + if (yych <= '!') goto yy187; + goto yy527; } else { - if (yych == '\'') goto yy527; - goto yy186; + if (yych == '\'') goto yy528; + goto yy187; } } else { if (yych <= 'R') { - if (yych <= '<') goto yy525; - if (yych <= 'Q') goto yy186; - goto yy528; + if (yych <= '<') goto yy526; + if (yych <= 'Q') goto yy187; + goto yy529; } else { - if (yych == 'r') goto yy528; - goto yy186; + if (yych == 'r') goto yy529; + goto yy187; } } -yy135: - YYDEBUG(135, *YYCURSOR); +yy136: + YYDEBUG(136, *YYCURSOR); yych = *++YYCURSOR; if (yych <= 'O') { - if (yych == 'L') goto yy515; - if (yych <= 'N') goto yy186; - goto yy516; + if (yych == 'L') goto yy516; + if (yych <= 'N') goto yy187; + goto yy517; } else { if (yych <= 'l') { - if (yych <= 'k') goto yy186; - goto yy515; + if (yych <= 'k') goto yy187; + goto yy516; } else { - if (yych == 'o') goto yy516; - goto yy186; + if (yych == 'o') goto yy517; + goto yy187; } } -yy136: - YYDEBUG(136, *YYCURSOR); +yy137: + YYDEBUG(137, *YYCURSOR); yych = *++YYCURSOR; if (yych <= 'U') { - if (yych == 'R') goto yy491; - if (yych <= 'T') goto yy186; - goto yy492; + if (yych == 'R') goto yy492; + if (yych <= 'T') goto yy187; + goto yy493; } else { if (yych <= 'r') { - if (yych <= 'q') goto yy186; - goto yy491; + if (yych <= 'q') goto yy187; + goto yy492; } else { - if (yych == 'u') goto yy492; - goto yy186; + if (yych == 'u') goto yy493; + goto yy187; } } -yy137: - YYDEBUG(137, *YYCURSOR); +yy138: + YYDEBUG(138, *YYCURSOR); ++YYCURSOR; if ((yych = *YYCURSOR) <= '<') { - if (yych == '-') goto yy487; + if (yych == '-') goto yy488; } else { - if (yych <= '=') goto yy485; - if (yych <= '>') goto yy489; + if (yych <= '=') goto yy486; + if (yych <= '>') goto yy490; } -yy138: - YYDEBUG(138, *YYCURSOR); +yy139: + YYDEBUG(139, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1438 "Zend/zend_language_scanner.l" +#line 1442 "Zend/zend_language_scanner.l" { return yytext[0]; } -#line 2635 "Zend/zend_language_scanner.c" -yy139: - YYDEBUG(139, *YYCURSOR); - ++YYCURSOR; - yych = *YYCURSOR; - goto yy484; +#line 2643 "Zend/zend_language_scanner.c" yy140: YYDEBUG(140, *YYCURSOR); + ++YYCURSOR; + yych = *YYCURSOR; + goto yy485; +yy141: + YYDEBUG(141, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1169 "Zend/zend_language_scanner.l" +#line 1173 "Zend/zend_language_scanner.l" { zendlval->value.str.val = yytext; /* no copying - intentional */ zendlval->value.str.len = yyleng; @@ -2648,261 +2656,261 @@ int lex_scan(zval *zendlval TSRMLS_DC) HANDLE_NEWLINES(yytext, yyleng); return T_WHITESPACE; } -#line 2652 "Zend/zend_language_scanner.c" -yy141: - YYDEBUG(141, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == ':') goto yy481; - goto yy138; +#line 2660 "Zend/zend_language_scanner.c" yy142: YYDEBUG(142, *YYCURSOR); - ++YYCURSOR; + yych = *++YYCURSOR; + if (yych == ':') goto yy482; + goto yy139; +yy143: YYDEBUG(143, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(144, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1198 "Zend/zend_language_scanner.l" +#line 1202 "Zend/zend_language_scanner.l" { return T_NS_SEPARATOR; } -#line 2667 "Zend/zend_language_scanner.c" -yy144: - YYDEBUG(144, *YYCURSOR); +#line 2675 "Zend/zend_language_scanner.c" +yy145: + YYDEBUG(145, *YYCURSOR); yych = *++YYCURSOR; if (yych <= 'E') { - if (yych == 'A') goto yy469; - if (yych <= 'D') goto yy186; - goto yy470; + if (yych == 'A') goto yy470; + if (yych <= 'D') goto yy187; + goto yy471; } else { if (yych <= 'a') { - if (yych <= '`') goto yy186; - goto yy469; + if (yych <= '`') goto yy187; + goto yy470; } else { - if (yych == 'e') goto yy470; - goto yy186; + if (yych == 'e') goto yy471; + goto yy187; } } -yy145: - YYDEBUG(145, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'A') goto yy466; - if (yych == 'a') goto yy466; - goto yy186; yy146: YYDEBUG(146, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'A') goto yy467; + if (yych == 'a') goto yy467; + goto yy187; +yy147: + YYDEBUG(147, *YYCURSOR); yyaccept = 1; yych = *(YYMARKER = ++YYCURSOR); if (yych <= 'S') { if (yych <= 'D') { if (yych <= ' ') { - if (yych == '\t') goto yy391; - if (yych <= 0x1F) goto yy138; - goto yy391; + if (yych == '\t') goto yy392; + if (yych <= 0x1F) goto yy139; + goto yy392; } else { - if (yych <= '@') goto yy138; - if (yych == 'C') goto yy138; - goto yy391; + if (yych <= '@') goto yy139; + if (yych == 'C') goto yy139; + goto yy392; } } else { if (yych <= 'I') { - if (yych == 'F') goto yy391; - if (yych <= 'H') goto yy138; - goto yy391; + if (yych == 'F') goto yy392; + if (yych <= 'H') goto yy139; + goto yy392; } else { - if (yych == 'O') goto yy391; - if (yych <= 'Q') goto yy138; - goto yy391; + if (yych == 'O') goto yy392; + if (yych <= 'Q') goto yy139; + goto yy392; } } } else { if (yych <= 'f') { if (yych <= 'b') { - if (yych == 'U') goto yy391; - if (yych <= '`') goto yy138; - goto yy391; + if (yych == 'U') goto yy392; + if (yych <= '`') goto yy139; + goto yy392; } else { - if (yych == 'd') goto yy391; - if (yych <= 'e') goto yy138; - goto yy391; + if (yych == 'd') goto yy392; + if (yych <= 'e') goto yy139; + goto yy392; } } else { if (yych <= 'o') { - if (yych == 'i') goto yy391; - if (yych <= 'n') goto yy138; - goto yy391; + if (yych == 'i') goto yy392; + if (yych <= 'n') goto yy139; + goto yy392; } else { if (yych <= 's') { - if (yych <= 'q') goto yy138; - goto yy391; + if (yych <= 'q') goto yy139; + goto yy392; } else { - if (yych == 'u') goto yy391; - goto yy138; + if (yych == 'u') goto yy392; + goto yy139; } } } } -yy147: - YYDEBUG(147, *YYCURSOR); +yy148: + YYDEBUG(148, *YYCURSOR); yych = *++YYCURSOR; if (yych <= 'S') { - if (yych == 'N') goto yy382; - if (yych <= 'R') goto yy186; - goto yy383; + if (yych == 'N') goto yy383; + if (yych <= 'R') goto yy187; + goto yy384; } else { if (yych <= 'n') { - if (yych <= 'm') goto yy186; - goto yy382; + if (yych <= 'm') goto yy187; + goto yy383; } else { - if (yych == 's') goto yy383; - goto yy186; + if (yych == 's') goto yy384; + goto yy187; } } -yy148: - YYDEBUG(148, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == '_') goto yy300; - goto yy186; yy149: YYDEBUG(149, *YYCURSOR); yych = *++YYCURSOR; - if (yych <= '<') goto yy138; - if (yych <= '=') goto yy294; - if (yych <= '>') goto yy296; - goto yy138; + if (yych == '_') goto yy301; + goto yy187; yy150: YYDEBUG(150, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'I') goto yy290; - if (yych == 'i') goto yy290; - goto yy186; + if (yych <= '<') goto yy139; + if (yych <= '=') goto yy295; + if (yych <= '>') goto yy297; + goto yy139; yy151: YYDEBUG(151, *YYCURSOR); yych = *++YYCURSOR; - if (yych == '+') goto yy288; - if (yych == '=') goto yy286; - goto yy138; + if (yych == 'I') goto yy291; + if (yych == 'i') goto yy291; + goto yy187; yy152: YYDEBUG(152, *YYCURSOR); yych = *++YYCURSOR; - if (yych == '=') goto yy283; - goto yy138; + if (yych == '+') goto yy289; + if (yych == '=') goto yy287; + goto yy139; yy153: YYDEBUG(153, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == '=') goto yy284; + goto yy139; +yy154: + YYDEBUG(154, *YYCURSOR); yyaccept = 1; yych = *(YYMARKER = ++YYCURSOR); if (yych <= ';') { - if (yych == '/') goto yy255; - goto yy138; + if (yych == '/') goto yy256; + goto yy139; } else { - if (yych <= '<') goto yy253; - if (yych <= '=') goto yy256; - if (yych <= '>') goto yy258; - goto yy138; + if (yych <= '<') goto yy254; + if (yych <= '=') goto yy257; + if (yych <= '>') goto yy259; + goto yy139; } -yy154: - YYDEBUG(154, *YYCURSOR); - yych = *++YYCURSOR; - if (yych <= '<') goto yy138; - if (yych <= '=') goto yy249; - if (yych <= '>') goto yy247; - goto yy138; yy155: YYDEBUG(155, *YYCURSOR); yych = *++YYCURSOR; - if (yych == '=') goto yy245; - goto yy138; + if (yych <= '<') goto yy139; + if (yych <= '=') goto yy250; + if (yych <= '>') goto yy248; + goto yy139; yy156: YYDEBUG(156, *YYCURSOR); yych = *++YYCURSOR; - if (yych <= '.') { - if (yych == '*') goto yy237; - goto yy138; - } else { - if (yych <= '/') goto yy239; - if (yych == '=') goto yy240; - goto yy138; - } + if (yych == '=') goto yy246; + goto yy139; yy157: YYDEBUG(157, *YYCURSOR); yych = *++YYCURSOR; - if (yych <= '/') goto yy138; - if (yych <= '9') goto yy233; - if (yych == '=') goto yy235; - goto yy138; + if (yych <= '.') { + if (yych == '*') goto yy238; + goto yy139; + } else { + if (yych <= '/') goto yy240; + if (yych == '=') goto yy241; + goto yy139; + } yy158: YYDEBUG(158, *YYCURSOR); yych = *++YYCURSOR; - if (yych <= '<') goto yy138; - if (yych <= '=') goto yy229; - if (yych <= '>') goto yy227; - goto yy138; + if (yych <= '/') goto yy139; + if (yych <= '9') goto yy234; + if (yych == '=') goto yy236; + goto yy139; yy159: YYDEBUG(159, *YYCURSOR); yych = *++YYCURSOR; - if (yych == '&') goto yy223; - if (yych == '=') goto yy225; - goto yy138; + if (yych <= '<') goto yy139; + if (yych <= '=') goto yy230; + if (yych <= '>') goto yy228; + goto yy139; yy160: YYDEBUG(160, *YYCURSOR); yych = *++YYCURSOR; - if (yych == '=') goto yy221; - if (yych == '|') goto yy219; - goto yy138; + if (yych == '&') goto yy224; + if (yych == '=') goto yy226; + goto yy139; yy161: YYDEBUG(161, *YYCURSOR); yych = *++YYCURSOR; - if (yych == '=') goto yy217; - goto yy138; + if (yych == '=') goto yy222; + if (yych == '|') goto yy220; + goto yy139; yy162: YYDEBUG(162, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'R') goto yy215; - if (yych == 'r') goto yy215; - goto yy186; + if (yych == '=') goto yy218; + goto yy139; yy163: YYDEBUG(163, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'O') goto yy212; - if (yych == 'o') goto yy212; - goto yy186; + if (yych == 'R') goto yy216; + if (yych == 'r') goto yy216; + goto yy187; yy164: YYDEBUG(164, *YYCURSOR); yych = *++YYCURSOR; - if (yych <= '_') { - if (yych <= '@') goto yy138; - if (yych <= 'Z') goto yy209; - if (yych <= '^') goto yy138; - goto yy209; - } else { - if (yych <= '`') goto yy138; - if (yych <= 'z') goto yy209; - if (yych <= '~') goto yy138; - goto yy209; - } + if (yych == 'O') goto yy213; + if (yych == 'o') goto yy213; + goto yy187; yy165: YYDEBUG(165, *YYCURSOR); yych = *++YYCURSOR; - goto yy138; + if (yych <= '_') { + if (yych <= '@') goto yy139; + if (yych <= 'Z') goto yy210; + if (yych <= '^') goto yy139; + goto yy210; + } else { + if (yych <= '`') goto yy139; + if (yych <= 'z') goto yy210; + if (yych <= '~') goto yy139; + goto yy210; + } yy166: YYDEBUG(166, *YYCURSOR); yych = *++YYCURSOR; - if (yych == '>') goto yy205; - goto yy138; + goto yy139; yy167: YYDEBUG(167, *YYCURSOR); - ++YYCURSOR; + yych = *++YYCURSOR; + if (yych == '>') goto yy206; + goto yy139; +yy168: YYDEBUG(168, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(169, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1443 "Zend/zend_language_scanner.l" +#line 1447 "Zend/zend_language_scanner.l" { yy_push_state(ST_IN_SCRIPTING TSRMLS_CC); return '{'; } -#line 2900 "Zend/zend_language_scanner.c" -yy169: - YYDEBUG(169, *YYCURSOR); - ++YYCURSOR; +#line 2908 "Zend/zend_language_scanner.c" +yy170: YYDEBUG(170, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(171, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1455 "Zend/zend_language_scanner.l" +#line 1459 "Zend/zend_language_scanner.l" { RESET_DOC_COMMENT(); if (!zend_stack_is_empty(&SCNG(state_stack))) { @@ -2910,35 +2918,35 @@ int lex_scan(zval *zendlval TSRMLS_DC) } return '}'; } -#line 2914 "Zend/zend_language_scanner.c" -yy171: - YYDEBUG(171, *YYCURSOR); +#line 2922 "Zend/zend_language_scanner.c" +yy172: + YYDEBUG(172, *YYCURSOR); yyaccept = 2; yych = *(YYMARKER = ++YYCURSOR); if (yych <= 'E') { if (yych <= '9') { - if (yych == '.') goto yy187; - if (yych >= '0') goto yy190; + if (yych == '.') goto yy188; + if (yych >= '0') goto yy191; } else { - if (yych == 'B') goto yy198; - if (yych >= 'E') goto yy192; + if (yych == 'B') goto yy199; + if (yych >= 'E') goto yy193; } } else { if (yych <= 'b') { - if (yych == 'X') goto yy197; - if (yych >= 'b') goto yy198; + if (yych == 'X') goto yy198; + if (yych >= 'b') goto yy199; } else { if (yych <= 'e') { - if (yych >= 'e') goto yy192; + if (yych >= 'e') goto yy193; } else { - if (yych == 'x') goto yy197; + if (yych == 'x') goto yy198; } } } -yy172: - YYDEBUG(172, *YYCURSOR); +yy173: + YYDEBUG(173, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1506 "Zend/zend_language_scanner.l" +#line 1510 "Zend/zend_language_scanner.l" { if (yyleng < MAX_LENGTH_OF_LONG - 1) { /* Won't overflow */ zendlval->value.lval = strtol(yytext, NULL, 0); @@ -2959,35 +2967,35 @@ int lex_scan(zval *zendlval TSRMLS_DC) zendlval->type = IS_LONG; return T_LNUMBER; } -#line 2963 "Zend/zend_language_scanner.c" -yy173: - YYDEBUG(173, *YYCURSOR); +#line 2971 "Zend/zend_language_scanner.c" +yy174: + YYDEBUG(174, *YYCURSOR); yyaccept = 2; yych = *(YYMARKER = ++YYCURSOR); if (yych <= '9') { - if (yych == '.') goto yy187; - if (yych <= '/') goto yy172; - goto yy190; + if (yych == '.') goto yy188; + if (yych <= '/') goto yy173; + goto yy191; } else { if (yych <= 'E') { - if (yych <= 'D') goto yy172; - goto yy192; + if (yych <= 'D') goto yy173; + goto yy193; } else { - if (yych == 'e') goto yy192; - goto yy172; + if (yych == 'e') goto yy193; + goto yy173; } } -yy174: - YYDEBUG(174, *YYCURSOR); - yych = *++YYCURSOR; - goto yy186; yy175: YYDEBUG(175, *YYCURSOR); - ++YYCURSOR; + yych = *++YYCURSOR; + goto yy187; yy176: YYDEBUG(176, *YYCURSOR); + ++YYCURSOR; +yy177: + YYDEBUG(177, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1903 "Zend/zend_language_scanner.l" +#line 1907 "Zend/zend_language_scanner.l" { while (YYCURSOR < YYLIMIT) { switch (*YYCURSOR++) { @@ -3021,14 +3029,14 @@ int lex_scan(zval *zendlval TSRMLS_DC) return T_COMMENT; } -#line 3025 "Zend/zend_language_scanner.c" -yy177: - YYDEBUG(177, *YYCURSOR); - ++YYCURSOR; +#line 3033 "Zend/zend_language_scanner.c" yy178: YYDEBUG(178, *YYCURSOR); + ++YYCURSOR; +yy179: + YYDEBUG(179, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1994 "Zend/zend_language_scanner.l" +#line 1998 "Zend/zend_language_scanner.l" { register char *s, *t; char *end; @@ -3096,14 +3104,14 @@ int lex_scan(zval *zendlval TSRMLS_DC) } return T_CONSTANT_ENCAPSED_STRING; } -#line 3100 "Zend/zend_language_scanner.c" -yy179: - YYDEBUG(179, *YYCURSOR); - ++YYCURSOR; +#line 3108 "Zend/zend_language_scanner.c" yy180: YYDEBUG(180, *YYCURSOR); + ++YYCURSOR; +yy181: + YYDEBUG(181, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2063 "Zend/zend_language_scanner.l" +#line 2067 "Zend/zend_language_scanner.l" { int bprefix = (yytext[0] != '"') ? 1 : 0; @@ -3144,24 +3152,24 @@ int lex_scan(zval *zendlval TSRMLS_DC) BEGIN(ST_DOUBLE_QUOTES); return '"'; } -#line 3148 "Zend/zend_language_scanner.c" -yy181: - YYDEBUG(181, *YYCURSOR); - ++YYCURSOR; +#line 3156 "Zend/zend_language_scanner.c" +yy182: YYDEBUG(182, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(183, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2154 "Zend/zend_language_scanner.l" +#line 2158 "Zend/zend_language_scanner.l" { BEGIN(ST_BACKQUOTE); return '`'; } -#line 3159 "Zend/zend_language_scanner.c" -yy183: - YYDEBUG(183, *YYCURSOR); - ++YYCURSOR; +#line 3167 "Zend/zend_language_scanner.c" +yy184: YYDEBUG(184, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(185, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2412 "Zend/zend_language_scanner.l" +#line 2416 "Zend/zend_language_scanner.l" { if (YYCURSOR > YYLIMIT) { return 0; @@ -3170,132 +3178,132 @@ int lex_scan(zval *zendlval TSRMLS_DC) zend_error(E_COMPILE_WARNING,"Unexpected character in input: '%c' (ASCII=%d) state=%d", yytext[0], yytext[0], YYSTATE); goto restart; } -#line 3174 "Zend/zend_language_scanner.c" -yy185: - YYDEBUG(185, *YYCURSOR); +#line 3182 "Zend/zend_language_scanner.c" +yy186: + YYDEBUG(186, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; -yy186: - YYDEBUG(186, *YYCURSOR); +yy187: + YYDEBUG(187, *YYCURSOR); if (yybm[0+yych] & 4) { - goto yy185; + goto yy186; } goto yy124; -yy187: - YYDEBUG(187, *YYCURSOR); +yy188: + YYDEBUG(188, *YYCURSOR); yyaccept = 3; YYMARKER = ++YYCURSOR; YYFILL(3); yych = *YYCURSOR; - YYDEBUG(188, *YYCURSOR); + YYDEBUG(189, *YYCURSOR); if (yybm[0+yych] & 8) { - goto yy187; + goto yy188; } - if (yych == 'E') goto yy192; - if (yych == 'e') goto yy192; -yy189: - YYDEBUG(189, *YYCURSOR); + if (yych == 'E') goto yy193; + if (yych == 'e') goto yy193; +yy190: + YYDEBUG(190, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1571 "Zend/zend_language_scanner.l" +#line 1575 "Zend/zend_language_scanner.l" { zendlval->value.dval = zend_strtod(yytext, NULL); zendlval->type = IS_DOUBLE; return T_DNUMBER; } -#line 3207 "Zend/zend_language_scanner.c" -yy190: - YYDEBUG(190, *YYCURSOR); +#line 3215 "Zend/zend_language_scanner.c" +yy191: + YYDEBUG(191, *YYCURSOR); yyaccept = 2; YYMARKER = ++YYCURSOR; YYFILL(3); yych = *YYCURSOR; - YYDEBUG(191, *YYCURSOR); + YYDEBUG(192, *YYCURSOR); if (yych <= '9') { - if (yych == '.') goto yy187; - if (yych <= '/') goto yy172; - goto yy190; + if (yych == '.') goto yy188; + if (yych <= '/') goto yy173; + goto yy191; } else { if (yych <= 'E') { - if (yych <= 'D') goto yy172; + if (yych <= 'D') goto yy173; } else { - if (yych != 'e') goto yy172; + if (yych != 'e') goto yy173; } } -yy192: - YYDEBUG(192, *YYCURSOR); +yy193: + YYDEBUG(193, *YYCURSOR); yych = *++YYCURSOR; if (yych <= ',') { - if (yych == '+') goto yy194; + if (yych == '+') goto yy195; } else { - if (yych <= '-') goto yy194; - if (yych <= '/') goto yy193; - if (yych <= '9') goto yy195; + if (yych <= '-') goto yy195; + if (yych <= '/') goto yy194; + if (yych <= '9') goto yy196; } -yy193: - YYDEBUG(193, *YYCURSOR); +yy194: + YYDEBUG(194, *YYCURSOR); YYCURSOR = YYMARKER; if (yyaccept <= 2) { if (yyaccept <= 1) { if (yyaccept <= 0) { goto yy124; } else { - goto yy138; + goto yy139; } } else { - goto yy172; + goto yy173; } } else { if (yyaccept <= 4) { if (yyaccept <= 3) { - goto yy189; + goto yy190; } else { - goto yy238; + goto yy239; } } else { - goto yy254; + goto yy255; } } -yy194: - YYDEBUG(194, *YYCURSOR); - yych = *++YYCURSOR; - if (yych <= '/') goto yy193; - if (yych >= ':') goto yy193; yy195: YYDEBUG(195, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= '/') goto yy194; + if (yych >= ':') goto yy194; +yy196: + YYDEBUG(196, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; - YYDEBUG(196, *YYCURSOR); - if (yych <= '/') goto yy189; - if (yych <= '9') goto yy195; - goto yy189; -yy197: YYDEBUG(197, *YYCURSOR); - yych = *++YYCURSOR; - if (yybm[0+yych] & 32) { - goto yy202; - } - goto yy193; + if (yych <= '/') goto yy190; + if (yych <= '9') goto yy196; + goto yy190; yy198: YYDEBUG(198, *YYCURSOR); yych = *++YYCURSOR; - if (yybm[0+yych] & 16) { - goto yy199; + if (yybm[0+yych] & 32) { + goto yy203; } - goto yy193; + goto yy194; yy199: YYDEBUG(199, *YYCURSOR); + yych = *++YYCURSOR; + if (yybm[0+yych] & 16) { + goto yy200; + } + goto yy194; +yy200: + YYDEBUG(200, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; - YYDEBUG(200, *YYCURSOR); + YYDEBUG(201, *YYCURSOR); if (yybm[0+yych] & 16) { - goto yy199; + goto yy200; } - YYDEBUG(201, *YYCURSOR); + YYDEBUG(202, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1481 "Zend/zend_language_scanner.l" +#line 1485 "Zend/zend_language_scanner.l" { char *bin = yytext + 2; /* Skip "0b" */ int len = yyleng - 2; @@ -3320,19 +3328,19 @@ int lex_scan(zval *zendlval TSRMLS_DC) return T_DNUMBER; } } -#line 3324 "Zend/zend_language_scanner.c" -yy202: - YYDEBUG(202, *YYCURSOR); +#line 3332 "Zend/zend_language_scanner.c" +yy203: + YYDEBUG(203, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; - YYDEBUG(203, *YYCURSOR); + YYDEBUG(204, *YYCURSOR); if (yybm[0+yych] & 32) { - goto yy202; + goto yy203; } - YYDEBUG(204, *YYCURSOR); + YYDEBUG(205, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1527 "Zend/zend_language_scanner.l" +#line 1531 "Zend/zend_language_scanner.l" { char *hex = yytext + 2; /* Skip "0x" */ int len = yyleng - 2; @@ -3357,16 +3365,16 @@ int lex_scan(zval *zendlval TSRMLS_DC) return T_DNUMBER; } } -#line 3361 "Zend/zend_language_scanner.c" -yy205: - YYDEBUG(205, *YYCURSOR); - ++YYCURSOR; - if ((yych = *YYCURSOR) == '\n') goto yy207; - if (yych == '\r') goto yy208; +#line 3369 "Zend/zend_language_scanner.c" yy206: YYDEBUG(206, *YYCURSOR); + ++YYCURSOR; + if ((yych = *YYCURSOR) == '\n') goto yy208; + if (yych == '\r') goto yy209; +yy207: + YYDEBUG(207, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1971 "Zend/zend_language_scanner.l" +#line 1975 "Zend/zend_language_scanner.l" { zendlval->value.str.val = yytext; /* no copying - intentional */ zendlval->value.str.len = yyleng; @@ -3374,137 +3382,137 @@ int lex_scan(zval *zendlval TSRMLS_DC) BEGIN(INITIAL); return T_CLOSE_TAG; /* implicit ';' at php-end tag */ } -#line 3378 "Zend/zend_language_scanner.c" -yy207: - YYDEBUG(207, *YYCURSOR); - yych = *++YYCURSOR; - goto yy206; +#line 3386 "Zend/zend_language_scanner.c" yy208: YYDEBUG(208, *YYCURSOR); yych = *++YYCURSOR; - if (yych == '\n') goto yy207; - goto yy206; + goto yy207; yy209: YYDEBUG(209, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == '\n') goto yy208; + goto yy207; +yy210: + YYDEBUG(210, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; - YYDEBUG(210, *YYCURSOR); + YYDEBUG(211, *YYCURSOR); if (yych <= '^') { if (yych <= '9') { - if (yych >= '0') goto yy209; + if (yych >= '0') goto yy210; } else { - if (yych <= '@') goto yy211; - if (yych <= 'Z') goto yy209; + if (yych <= '@') goto yy212; + if (yych <= 'Z') goto yy210; } } else { if (yych <= '`') { - if (yych <= '_') goto yy209; + if (yych <= '_') goto yy210; } else { - if (yych <= 'z') goto yy209; - if (yych >= 0x7F) goto yy209; + if (yych <= 'z') goto yy210; + if (yych >= 0x7F) goto yy210; } } -yy211: - YYDEBUG(211, *YYCURSOR); +yy212: + YYDEBUG(212, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1873 "Zend/zend_language_scanner.l" +#line 1877 "Zend/zend_language_scanner.l" { zend_copy_value(zendlval, (yytext+1), (yyleng-1)); zendlval->type = IS_STRING; return T_VARIABLE; } -#line 3418 "Zend/zend_language_scanner.c" -yy212: - YYDEBUG(212, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'R') goto yy213; - if (yych != 'r') goto yy186; +#line 3426 "Zend/zend_language_scanner.c" yy213: YYDEBUG(213, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'R') goto yy214; + if (yych != 'r') goto yy187; +yy214: + YYDEBUG(214, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(214, *YYCURSOR); + YYDEBUG(215, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1426 "Zend/zend_language_scanner.l" +#line 1430 "Zend/zend_language_scanner.l" { return T_LOGICAL_XOR; } -#line 3436 "Zend/zend_language_scanner.c" -yy215: - YYDEBUG(215, *YYCURSOR); +#line 3444 "Zend/zend_language_scanner.c" +yy216: + YYDEBUG(216, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(216, *YYCURSOR); + YYDEBUG(217, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1418 "Zend/zend_language_scanner.l" +#line 1422 "Zend/zend_language_scanner.l" { return T_LOGICAL_OR; } -#line 3449 "Zend/zend_language_scanner.c" -yy217: - YYDEBUG(217, *YYCURSOR); - ++YYCURSOR; +#line 3457 "Zend/zend_language_scanner.c" +yy218: YYDEBUG(218, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(219, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1406 "Zend/zend_language_scanner.l" +#line 1410 "Zend/zend_language_scanner.l" { return T_XOR_EQUAL; } -#line 3459 "Zend/zend_language_scanner.c" -yy219: - YYDEBUG(219, *YYCURSOR); - ++YYCURSOR; +#line 3467 "Zend/zend_language_scanner.c" +yy220: YYDEBUG(220, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(221, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1410 "Zend/zend_language_scanner.l" +#line 1414 "Zend/zend_language_scanner.l" { return T_BOOLEAN_OR; } -#line 3469 "Zend/zend_language_scanner.c" -yy221: - YYDEBUG(221, *YYCURSOR); - ++YYCURSOR; +#line 3477 "Zend/zend_language_scanner.c" +yy222: YYDEBUG(222, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(223, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1402 "Zend/zend_language_scanner.l" +#line 1406 "Zend/zend_language_scanner.l" { return T_OR_EQUAL; } -#line 3479 "Zend/zend_language_scanner.c" -yy223: - YYDEBUG(223, *YYCURSOR); - ++YYCURSOR; +#line 3487 "Zend/zend_language_scanner.c" +yy224: YYDEBUG(224, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(225, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1414 "Zend/zend_language_scanner.l" +#line 1418 "Zend/zend_language_scanner.l" { return T_BOOLEAN_AND; } -#line 3489 "Zend/zend_language_scanner.c" -yy225: - YYDEBUG(225, *YYCURSOR); - ++YYCURSOR; +#line 3497 "Zend/zend_language_scanner.c" +yy226: YYDEBUG(226, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(227, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1398 "Zend/zend_language_scanner.l" +#line 1402 "Zend/zend_language_scanner.l" { return T_AND_EQUAL; } -#line 3499 "Zend/zend_language_scanner.c" -yy227: - YYDEBUG(227, *YYCURSOR); - ++YYCURSOR; - if ((yych = *YYCURSOR) == '\n') goto yy231; - if (yych == '\r') goto yy232; +#line 3507 "Zend/zend_language_scanner.c" yy228: YYDEBUG(228, *YYCURSOR); + ++YYCURSOR; + if ((yych = *YYCURSOR) == '\n') goto yy232; + if (yych == '\r') goto yy233; +yy229: + YYDEBUG(229, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1980 "Zend/zend_language_scanner.l" +#line 1984 "Zend/zend_language_scanner.l" { if (CG(asp_tags)) { BEGIN(INITIAL); @@ -3517,61 +3525,61 @@ int lex_scan(zval *zendlval TSRMLS_DC) return yytext[0]; } } -#line 3521 "Zend/zend_language_scanner.c" -yy229: - YYDEBUG(229, *YYCURSOR); - ++YYCURSOR; +#line 3529 "Zend/zend_language_scanner.c" +yy230: YYDEBUG(230, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(231, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1386 "Zend/zend_language_scanner.l" +#line 1390 "Zend/zend_language_scanner.l" { return T_MOD_EQUAL; } -#line 3531 "Zend/zend_language_scanner.c" -yy231: - YYDEBUG(231, *YYCURSOR); - yych = *++YYCURSOR; - goto yy228; +#line 3539 "Zend/zend_language_scanner.c" yy232: YYDEBUG(232, *YYCURSOR); yych = *++YYCURSOR; - if (yych == '\n') goto yy231; - goto yy228; + goto yy229; yy233: YYDEBUG(233, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == '\n') goto yy232; + goto yy229; +yy234: + YYDEBUG(234, *YYCURSOR); yyaccept = 3; YYMARKER = ++YYCURSOR; YYFILL(3); yych = *YYCURSOR; - YYDEBUG(234, *YYCURSOR); + YYDEBUG(235, *YYCURSOR); if (yych <= 'D') { - if (yych <= '/') goto yy189; - if (yych <= '9') goto yy233; - goto yy189; + if (yych <= '/') goto yy190; + if (yych <= '9') goto yy234; + goto yy190; } else { - if (yych <= 'E') goto yy192; - if (yych == 'e') goto yy192; - goto yy189; + if (yych <= 'E') goto yy193; + if (yych == 'e') goto yy193; + goto yy190; } -yy235: - YYDEBUG(235, *YYCURSOR); - ++YYCURSOR; +yy236: YYDEBUG(236, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(237, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1382 "Zend/zend_language_scanner.l" +#line 1386 "Zend/zend_language_scanner.l" { return T_CONCAT_EQUAL; } -#line 3566 "Zend/zend_language_scanner.c" -yy237: - YYDEBUG(237, *YYCURSOR); - yyaccept = 4; - yych = *(YYMARKER = ++YYCURSOR); - if (yych == '*') goto yy242; +#line 3574 "Zend/zend_language_scanner.c" yy238: YYDEBUG(238, *YYCURSOR); + yyaccept = 4; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == '*') goto yy243; +yy239: + YYDEBUG(239, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1937 "Zend/zend_language_scanner.l" +#line 1941 "Zend/zend_language_scanner.l" { int doc_com; @@ -3605,281 +3613,281 @@ int lex_scan(zval *zendlval TSRMLS_DC) return T_COMMENT; } -#line 3609 "Zend/zend_language_scanner.c" -yy239: - YYDEBUG(239, *YYCURSOR); - yych = *++YYCURSOR; - goto yy176; +#line 3617 "Zend/zend_language_scanner.c" yy240: YYDEBUG(240, *YYCURSOR); - ++YYCURSOR; + yych = *++YYCURSOR; + goto yy177; +yy241: YYDEBUG(241, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(242, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1378 "Zend/zend_language_scanner.l" +#line 1382 "Zend/zend_language_scanner.l" { return T_DIV_EQUAL; } -#line 3623 "Zend/zend_language_scanner.c" -yy242: - YYDEBUG(242, *YYCURSOR); +#line 3631 "Zend/zend_language_scanner.c" +yy243: + YYDEBUG(243, *YYCURSOR); yych = *++YYCURSOR; if (yybm[0+yych] & 64) { - goto yy243; + goto yy244; } - goto yy193; -yy243: - YYDEBUG(243, *YYCURSOR); + goto yy194; +yy244: + YYDEBUG(244, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; - YYDEBUG(244, *YYCURSOR); + YYDEBUG(245, *YYCURSOR); if (yybm[0+yych] & 64) { - goto yy243; + goto yy244; } - goto yy238; -yy245: - YYDEBUG(245, *YYCURSOR); - ++YYCURSOR; + goto yy239; +yy246: YYDEBUG(246, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(247, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1374 "Zend/zend_language_scanner.l" +#line 1378 "Zend/zend_language_scanner.l" { return T_MUL_EQUAL; } -#line 3650 "Zend/zend_language_scanner.c" -yy247: - YYDEBUG(247, *YYCURSOR); - ++YYCURSOR; - if ((yych = *YYCURSOR) == '=') goto yy251; +#line 3658 "Zend/zend_language_scanner.c" +yy248: YYDEBUG(248, *YYCURSOR); + ++YYCURSOR; + if ((yych = *YYCURSOR) == '=') goto yy252; + YYDEBUG(249, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1434 "Zend/zend_language_scanner.l" +#line 1438 "Zend/zend_language_scanner.l" { return T_SR; } -#line 3661 "Zend/zend_language_scanner.c" -yy249: - YYDEBUG(249, *YYCURSOR); - ++YYCURSOR; +#line 3669 "Zend/zend_language_scanner.c" +yy250: YYDEBUG(250, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(251, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1362 "Zend/zend_language_scanner.l" +#line 1366 "Zend/zend_language_scanner.l" { return T_IS_GREATER_OR_EQUAL; } -#line 3671 "Zend/zend_language_scanner.c" -yy251: - YYDEBUG(251, *YYCURSOR); - ++YYCURSOR; +#line 3679 "Zend/zend_language_scanner.c" +yy252: YYDEBUG(252, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(253, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1394 "Zend/zend_language_scanner.l" +#line 1398 "Zend/zend_language_scanner.l" { return T_SR_EQUAL; } -#line 3681 "Zend/zend_language_scanner.c" -yy253: - YYDEBUG(253, *YYCURSOR); - yyaccept = 5; - yych = *(YYMARKER = ++YYCURSOR); - if (yych <= ';') goto yy254; - if (yych <= '<') goto yy269; - if (yych <= '=') goto yy267; +#line 3689 "Zend/zend_language_scanner.c" yy254: YYDEBUG(254, *YYCURSOR); + yyaccept = 5; + yych = *(YYMARKER = ++YYCURSOR); + if (yych <= ';') goto yy255; + if (yych <= '<') goto yy270; + if (yych <= '=') goto yy268; +yy255: + YYDEBUG(255, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1430 "Zend/zend_language_scanner.l" +#line 1434 "Zend/zend_language_scanner.l" { return T_SL; } -#line 3696 "Zend/zend_language_scanner.c" -yy255: - YYDEBUG(255, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'S') goto yy260; - if (yych == 's') goto yy260; - goto yy193; +#line 3704 "Zend/zend_language_scanner.c" yy256: YYDEBUG(256, *YYCURSOR); - ++YYCURSOR; + yych = *++YYCURSOR; + if (yych == 'S') goto yy261; + if (yych == 's') goto yy261; + goto yy194; +yy257: YYDEBUG(257, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(258, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1358 "Zend/zend_language_scanner.l" +#line 1362 "Zend/zend_language_scanner.l" { return T_IS_SMALLER_OR_EQUAL; } -#line 3712 "Zend/zend_language_scanner.c" -yy258: - YYDEBUG(258, *YYCURSOR); - ++YYCURSOR; +#line 3720 "Zend/zend_language_scanner.c" yy259: YYDEBUG(259, *YYCURSOR); + ++YYCURSOR; +yy260: + YYDEBUG(260, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1354 "Zend/zend_language_scanner.l" +#line 1358 "Zend/zend_language_scanner.l" { return T_IS_NOT_EQUAL; } -#line 3723 "Zend/zend_language_scanner.c" -yy260: - YYDEBUG(260, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'C') goto yy261; - if (yych != 'c') goto yy193; +#line 3731 "Zend/zend_language_scanner.c" yy261: YYDEBUG(261, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'R') goto yy262; - if (yych != 'r') goto yy193; + if (yych == 'C') goto yy262; + if (yych != 'c') goto yy194; yy262: YYDEBUG(262, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'I') goto yy263; - if (yych != 'i') goto yy193; + if (yych == 'R') goto yy263; + if (yych != 'r') goto yy194; yy263: YYDEBUG(263, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'P') goto yy264; - if (yych != 'p') goto yy193; + if (yych == 'I') goto yy264; + if (yych != 'i') goto yy194; yy264: YYDEBUG(264, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy265; - if (yych != 't') goto yy193; + if (yych == 'P') goto yy265; + if (yych != 'p') goto yy194; yy265: YYDEBUG(265, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'T') goto yy266; + if (yych != 't') goto yy194; +yy266: + YYDEBUG(266, *YYCURSOR); ++YYCURSOR; YYFILL(3); yych = *YYCURSOR; - YYDEBUG(266, *YYCURSOR); + YYDEBUG(267, *YYCURSOR); if (yych <= '\r') { - if (yych <= 0x08) goto yy193; - if (yych <= '\n') goto yy265; - if (yych <= '\f') goto yy193; - goto yy265; + if (yych <= 0x08) goto yy194; + if (yych <= '\n') goto yy266; + if (yych <= '\f') goto yy194; + goto yy266; } else { if (yych <= ' ') { - if (yych <= 0x1F) goto yy193; - goto yy265; + if (yych <= 0x1F) goto yy194; + goto yy266; } else { - if (yych == '>') goto yy205; - goto yy193; + if (yych == '>') goto yy206; + goto yy194; } } -yy267: - YYDEBUG(267, *YYCURSOR); - ++YYCURSOR; +yy268: YYDEBUG(268, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(269, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1390 "Zend/zend_language_scanner.l" +#line 1394 "Zend/zend_language_scanner.l" { return T_SL_EQUAL; } -#line 3778 "Zend/zend_language_scanner.c" -yy269: - YYDEBUG(269, *YYCURSOR); +#line 3786 "Zend/zend_language_scanner.c" +yy270: + YYDEBUG(270, *YYCURSOR); ++YYCURSOR; YYFILL(2); yych = *YYCURSOR; - YYDEBUG(270, *YYCURSOR); + YYDEBUG(271, *YYCURSOR); if (yybm[0+yych] & 128) { - goto yy269; + goto yy270; } if (yych <= 'Z') { if (yych <= '&') { - if (yych == '"') goto yy274; - goto yy193; + if (yych == '"') goto yy275; + goto yy194; } else { - if (yych <= '\'') goto yy273; - if (yych <= '@') goto yy193; + if (yych <= '\'') goto yy274; + if (yych <= '@') goto yy194; } } else { if (yych <= '`') { - if (yych != '_') goto yy193; + if (yych != '_') goto yy194; } else { - if (yych <= 'z') goto yy271; - if (yych <= '~') goto yy193; + if (yych <= 'z') goto yy272; + if (yych <= '~') goto yy194; } } -yy271: - YYDEBUG(271, *YYCURSOR); +yy272: + YYDEBUG(272, *YYCURSOR); ++YYCURSOR; YYFILL(2); yych = *YYCURSOR; - YYDEBUG(272, *YYCURSOR); + YYDEBUG(273, *YYCURSOR); if (yych <= '@') { if (yych <= '\f') { - if (yych == '\n') goto yy278; - goto yy193; + if (yych == '\n') goto yy279; + goto yy194; } else { - if (yych <= '\r') goto yy280; - if (yych <= '/') goto yy193; - if (yych <= '9') goto yy271; - goto yy193; + if (yych <= '\r') goto yy281; + if (yych <= '/') goto yy194; + if (yych <= '9') goto yy272; + goto yy194; } } else { if (yych <= '_') { - if (yych <= 'Z') goto yy271; - if (yych <= '^') goto yy193; - goto yy271; + if (yych <= 'Z') goto yy272; + if (yych <= '^') goto yy194; + goto yy272; } else { - if (yych <= '`') goto yy193; - if (yych <= 'z') goto yy271; - if (yych <= '~') goto yy193; - goto yy271; + if (yych <= '`') goto yy194; + if (yych <= 'z') goto yy272; + if (yych <= '~') goto yy194; + goto yy272; } } -yy273: - YYDEBUG(273, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == '\'') goto yy193; - if (yych <= '/') goto yy282; - if (yych <= '9') goto yy193; - goto yy282; yy274: YYDEBUG(274, *YYCURSOR); yych = *++YYCURSOR; - if (yych == '"') goto yy193; - if (yych <= '/') goto yy276; - if (yych <= '9') goto yy193; - goto yy276; + if (yych == '\'') goto yy194; + if (yych <= '/') goto yy283; + if (yych <= '9') goto yy194; + goto yy283; yy275: YYDEBUG(275, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == '"') goto yy194; + if (yych <= '/') goto yy277; + if (yych <= '9') goto yy194; + goto yy277; +yy276: + YYDEBUG(276, *YYCURSOR); ++YYCURSOR; YYFILL(3); yych = *YYCURSOR; -yy276: - YYDEBUG(276, *YYCURSOR); +yy277: + YYDEBUG(277, *YYCURSOR); if (yych <= 'Z') { if (yych <= '/') { - if (yych != '"') goto yy193; + if (yych != '"') goto yy194; } else { - if (yych <= '9') goto yy275; - if (yych <= '@') goto yy193; - goto yy275; + if (yych <= '9') goto yy276; + if (yych <= '@') goto yy194; + goto yy276; } } else { if (yych <= '`') { - if (yych == '_') goto yy275; - goto yy193; + if (yych == '_') goto yy276; + goto yy194; } else { - if (yych <= 'z') goto yy275; - if (yych <= '~') goto yy193; - goto yy275; + if (yych <= 'z') goto yy276; + if (yych <= '~') goto yy194; + goto yy276; } } -yy277: - YYDEBUG(277, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == '\n') goto yy278; - if (yych == '\r') goto yy280; - goto yy193; yy278: YYDEBUG(278, *YYCURSOR); - ++YYCURSOR; + yych = *++YYCURSOR; + if (yych == '\n') goto yy279; + if (yych == '\r') goto yy281; + goto yy194; yy279: YYDEBUG(279, *YYCURSOR); + ++YYCURSOR; +yy280: + YYDEBUG(280, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2105 "Zend/zend_language_scanner.l" +#line 2109 "Zend/zend_language_scanner.l" { char *s; int bprefix = (yytext[0] != '<') ? 1 : 0; @@ -3927,255 +3935,255 @@ int lex_scan(zval *zendlval TSRMLS_DC) return T_START_HEREDOC; } -#line 3931 "Zend/zend_language_scanner.c" -yy280: - YYDEBUG(280, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == '\n') goto yy278; - goto yy279; +#line 3939 "Zend/zend_language_scanner.c" yy281: YYDEBUG(281, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == '\n') goto yy279; + goto yy280; +yy282: + YYDEBUG(282, *YYCURSOR); ++YYCURSOR; YYFILL(3); yych = *YYCURSOR; -yy282: - YYDEBUG(282, *YYCURSOR); +yy283: + YYDEBUG(283, *YYCURSOR); if (yych <= 'Z') { if (yych <= '/') { - if (yych == '\'') goto yy277; - goto yy193; + if (yych == '\'') goto yy278; + goto yy194; } else { - if (yych <= '9') goto yy281; - if (yych <= '@') goto yy193; - goto yy281; + if (yych <= '9') goto yy282; + if (yych <= '@') goto yy194; + goto yy282; } } else { if (yych <= '`') { - if (yych == '_') goto yy281; - goto yy193; + if (yych == '_') goto yy282; + goto yy194; } else { - if (yych <= 'z') goto yy281; - if (yych <= '~') goto yy193; - goto yy281; + if (yych <= 'z') goto yy282; + if (yych <= '~') goto yy194; + goto yy282; } } -yy283: - YYDEBUG(283, *YYCURSOR); - yych = *++YYCURSOR; - if (yych != '=') goto yy259; +yy284: YYDEBUG(284, *YYCURSOR); - ++YYCURSOR; + yych = *++YYCURSOR; + if (yych != '=') goto yy260; YYDEBUG(285, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(286, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1346 "Zend/zend_language_scanner.l" +#line 1350 "Zend/zend_language_scanner.l" { return T_IS_NOT_IDENTICAL; } -#line 3975 "Zend/zend_language_scanner.c" -yy286: - YYDEBUG(286, *YYCURSOR); - ++YYCURSOR; +#line 3983 "Zend/zend_language_scanner.c" +yy287: YYDEBUG(287, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(288, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1366 "Zend/zend_language_scanner.l" +#line 1370 "Zend/zend_language_scanner.l" { return T_PLUS_EQUAL; } -#line 3985 "Zend/zend_language_scanner.c" -yy288: - YYDEBUG(288, *YYCURSOR); - ++YYCURSOR; +#line 3993 "Zend/zend_language_scanner.c" +yy289: YYDEBUG(289, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(290, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1334 "Zend/zend_language_scanner.l" +#line 1338 "Zend/zend_language_scanner.l" { return T_INC; } -#line 3995 "Zend/zend_language_scanner.c" -yy290: - YYDEBUG(290, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'S') goto yy291; - if (yych != 's') goto yy186; +#line 4003 "Zend/zend_language_scanner.c" yy291: YYDEBUG(291, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy292; - if (yych != 't') goto yy186; + if (yych == 'S') goto yy292; + if (yych != 's') goto yy187; yy292: YYDEBUG(292, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'T') goto yy293; + if (yych != 't') goto yy187; +yy293: + YYDEBUG(293, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(293, *YYCURSOR); + YYDEBUG(294, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1322 "Zend/zend_language_scanner.l" +#line 1326 "Zend/zend_language_scanner.l" { return T_LIST; } -#line 4018 "Zend/zend_language_scanner.c" -yy294: - YYDEBUG(294, *YYCURSOR); - ++YYCURSOR; - if ((yych = *YYCURSOR) == '=') goto yy298; +#line 4026 "Zend/zend_language_scanner.c" +yy295: YYDEBUG(295, *YYCURSOR); + ++YYCURSOR; + if ((yych = *YYCURSOR) == '=') goto yy299; + YYDEBUG(296, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1350 "Zend/zend_language_scanner.l" +#line 1354 "Zend/zend_language_scanner.l" { return T_IS_EQUAL; } -#line 4029 "Zend/zend_language_scanner.c" -yy296: - YYDEBUG(296, *YYCURSOR); - ++YYCURSOR; +#line 4037 "Zend/zend_language_scanner.c" +yy297: YYDEBUG(297, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(298, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1318 "Zend/zend_language_scanner.l" +#line 1322 "Zend/zend_language_scanner.l" { return T_DOUBLE_ARROW; } -#line 4039 "Zend/zend_language_scanner.c" -yy298: - YYDEBUG(298, *YYCURSOR); - ++YYCURSOR; +#line 4047 "Zend/zend_language_scanner.c" +yy299: YYDEBUG(299, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(300, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1342 "Zend/zend_language_scanner.l" +#line 1346 "Zend/zend_language_scanner.l" { return T_IS_IDENTICAL; } -#line 4049 "Zend/zend_language_scanner.c" -yy300: - YYDEBUG(300, *YYCURSOR); +#line 4057 "Zend/zend_language_scanner.c" +yy301: + YYDEBUG(301, *YYCURSOR); yych = *++YYCURSOR; YYDEBUG(-1, yych); switch (yych) { case 'C': - case 'c': goto yy302; + case 'c': goto yy303; case 'D': - case 'd': goto yy307; + case 'd': goto yy308; case 'F': - case 'f': goto yy304; + case 'f': goto yy305; case 'H': - case 'h': goto yy301; + case 'h': goto yy302; case 'L': - case 'l': goto yy306; + case 'l': goto yy307; case 'M': - case 'm': goto yy305; + case 'm': goto yy306; case 'N': - case 'n': goto yy308; + case 'n': goto yy309; case 'T': - case 't': goto yy303; - default: goto yy186; + case 't': goto yy304; + default: goto yy187; } -yy301: - YYDEBUG(301, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'A') goto yy369; - if (yych == 'a') goto yy369; - goto yy186; yy302: YYDEBUG(302, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'L') goto yy362; - if (yych == 'l') goto yy362; - goto yy186; + if (yych == 'A') goto yy370; + if (yych == 'a') goto yy370; + goto yy187; yy303: YYDEBUG(303, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'R') goto yy355; - if (yych == 'r') goto yy355; - goto yy186; + if (yych == 'L') goto yy363; + if (yych == 'l') goto yy363; + goto yy187; yy304: YYDEBUG(304, *YYCURSOR); yych = *++YYCURSOR; + if (yych == 'R') goto yy356; + if (yych == 'r') goto yy356; + goto yy187; +yy305: + YYDEBUG(305, *YYCURSOR); + yych = *++YYCURSOR; if (yych <= 'U') { - if (yych == 'I') goto yy339; - if (yych <= 'T') goto yy186; - goto yy340; + if (yych == 'I') goto yy340; + if (yych <= 'T') goto yy187; + goto yy341; } else { if (yych <= 'i') { - if (yych <= 'h') goto yy186; - goto yy339; + if (yych <= 'h') goto yy187; + goto yy340; } else { - if (yych == 'u') goto yy340; - goto yy186; + if (yych == 'u') goto yy341; + goto yy187; } } -yy305: - YYDEBUG(305, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'E') goto yy331; - if (yych == 'e') goto yy331; - goto yy186; yy306: YYDEBUG(306, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'I') goto yy325; - if (yych == 'i') goto yy325; - goto yy186; + if (yych == 'E') goto yy332; + if (yych == 'e') goto yy332; + goto yy187; yy307: YYDEBUG(307, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'I') goto yy320; - if (yych == 'i') goto yy320; - goto yy186; + if (yych == 'I') goto yy326; + if (yych == 'i') goto yy326; + goto yy187; yy308: YYDEBUG(308, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'A') goto yy309; - if (yych != 'a') goto yy186; + if (yych == 'I') goto yy321; + if (yych == 'i') goto yy321; + goto yy187; yy309: YYDEBUG(309, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'M') goto yy310; - if (yych != 'm') goto yy186; + if (yych == 'A') goto yy310; + if (yych != 'a') goto yy187; yy310: YYDEBUG(310, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy311; - if (yych != 'e') goto yy186; + if (yych == 'M') goto yy311; + if (yych != 'm') goto yy187; yy311: YYDEBUG(311, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'S') goto yy312; - if (yych != 's') goto yy186; + if (yych == 'E') goto yy312; + if (yych != 'e') goto yy187; yy312: YYDEBUG(312, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'P') goto yy313; - if (yych != 'p') goto yy186; + if (yych == 'S') goto yy313; + if (yych != 's') goto yy187; yy313: YYDEBUG(313, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'A') goto yy314; - if (yych != 'a') goto yy186; + if (yych == 'P') goto yy314; + if (yych != 'p') goto yy187; yy314: YYDEBUG(314, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'C') goto yy315; - if (yych != 'c') goto yy186; + if (yych == 'A') goto yy315; + if (yych != 'a') goto yy187; yy315: YYDEBUG(315, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy316; - if (yych != 'e') goto yy186; + if (yych == 'C') goto yy316; + if (yych != 'c') goto yy187; yy316: YYDEBUG(316, *YYCURSOR); yych = *++YYCURSOR; - if (yych != '_') goto yy186; + if (yych == 'E') goto yy317; + if (yych != 'e') goto yy187; +yy317: YYDEBUG(317, *YYCURSOR); yych = *++YYCURSOR; - if (yych != '_') goto yy186; + if (yych != '_') goto yy187; YYDEBUG(318, *YYCURSOR); + yych = *++YYCURSOR; + if (yych != '_') goto yy187; + YYDEBUG(319, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(319, *YYCURSOR); + YYDEBUG(320, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1706 "Zend/zend_language_scanner.l" +#line 1710 "Zend/zend_language_scanner.l" { if (CG(current_namespace)) { *zendlval = *CG(current_namespace); @@ -4185,27 +4193,27 @@ int lex_scan(zval *zendlval TSRMLS_DC) } return T_NS_C; } -#line 4189 "Zend/zend_language_scanner.c" -yy320: - YYDEBUG(320, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'R') goto yy321; - if (yych != 'r') goto yy186; +#line 4197 "Zend/zend_language_scanner.c" yy321: YYDEBUG(321, *YYCURSOR); yych = *++YYCURSOR; - if (yych != '_') goto yy186; + if (yych == 'R') goto yy322; + if (yych != 'r') goto yy187; +yy322: YYDEBUG(322, *YYCURSOR); yych = *++YYCURSOR; - if (yych != '_') goto yy186; + if (yych != '_') goto yy187; YYDEBUG(323, *YYCURSOR); + yych = *++YYCURSOR; + if (yych != '_') goto yy187; + YYDEBUG(324, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(324, *YYCURSOR); + YYDEBUG(325, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1679 "Zend/zend_language_scanner.l" +#line 1683 "Zend/zend_language_scanner.l" { char *filename = zend_get_compiled_filename(TSRMLS_C); const size_t filename_len = strlen(filename); @@ -4232,73 +4240,73 @@ int lex_scan(zval *zendlval TSRMLS_DC) zendlval->type = IS_STRING; return T_DIR; } -#line 4236 "Zend/zend_language_scanner.c" -yy325: - YYDEBUG(325, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'N') goto yy326; - if (yych != 'n') goto yy186; +#line 4244 "Zend/zend_language_scanner.c" yy326: YYDEBUG(326, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy327; - if (yych != 'e') goto yy186; + if (yych == 'N') goto yy327; + if (yych != 'n') goto yy187; yy327: YYDEBUG(327, *YYCURSOR); yych = *++YYCURSOR; - if (yych != '_') goto yy186; + if (yych == 'E') goto yy328; + if (yych != 'e') goto yy187; +yy328: YYDEBUG(328, *YYCURSOR); yych = *++YYCURSOR; - if (yych != '_') goto yy186; + if (yych != '_') goto yy187; YYDEBUG(329, *YYCURSOR); + yych = *++YYCURSOR; + if (yych != '_') goto yy187; + YYDEBUG(330, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(330, *YYCURSOR); + YYDEBUG(331, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1661 "Zend/zend_language_scanner.l" +#line 1665 "Zend/zend_language_scanner.l" { zendlval->value.lval = CG(zend_lineno); zendlval->type = IS_LONG; return T_LINE; } -#line 4267 "Zend/zend_language_scanner.c" -yy331: - YYDEBUG(331, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'T') goto yy332; - if (yych != 't') goto yy186; +#line 4275 "Zend/zend_language_scanner.c" yy332: YYDEBUG(332, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'H') goto yy333; - if (yych != 'h') goto yy186; + if (yych == 'T') goto yy333; + if (yych != 't') goto yy187; yy333: YYDEBUG(333, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'O') goto yy334; - if (yych != 'o') goto yy186; + if (yych == 'H') goto yy334; + if (yych != 'h') goto yy187; yy334: YYDEBUG(334, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'D') goto yy335; - if (yych != 'd') goto yy186; + if (yych == 'O') goto yy335; + if (yych != 'o') goto yy187; yy335: YYDEBUG(335, *YYCURSOR); yych = *++YYCURSOR; - if (yych != '_') goto yy186; + if (yych == 'D') goto yy336; + if (yych != 'd') goto yy187; +yy336: YYDEBUG(336, *YYCURSOR); yych = *++YYCURSOR; - if (yych != '_') goto yy186; + if (yych != '_') goto yy187; YYDEBUG(337, *YYCURSOR); + yych = *++YYCURSOR; + if (yych != '_') goto yy187; + YYDEBUG(338, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(338, *YYCURSOR); + YYDEBUG(339, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1640 "Zend/zend_language_scanner.l" +#line 1644 "Zend/zend_language_scanner.l" { const char *class_name = CG(active_class_entry) ? CG(active_class_entry)->name : NULL; const char *func_name = CG(active_op_array)? CG(active_op_array)->function_name : NULL; @@ -4319,58 +4327,58 @@ int lex_scan(zval *zendlval TSRMLS_DC) zendlval->type = IS_STRING; return T_METHOD_C; } -#line 4323 "Zend/zend_language_scanner.c" -yy339: - YYDEBUG(339, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'L') goto yy350; - if (yych == 'l') goto yy350; - goto yy186; +#line 4331 "Zend/zend_language_scanner.c" yy340: YYDEBUG(340, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'N') goto yy341; - if (yych != 'n') goto yy186; + if (yych == 'L') goto yy351; + if (yych == 'l') goto yy351; + goto yy187; yy341: YYDEBUG(341, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'C') goto yy342; - if (yych != 'c') goto yy186; + if (yych == 'N') goto yy342; + if (yych != 'n') goto yy187; yy342: YYDEBUG(342, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy343; - if (yych != 't') goto yy186; + if (yych == 'C') goto yy343; + if (yych != 'c') goto yy187; yy343: YYDEBUG(343, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'I') goto yy344; - if (yych != 'i') goto yy186; + if (yych == 'T') goto yy344; + if (yych != 't') goto yy187; yy344: YYDEBUG(344, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'O') goto yy345; - if (yych != 'o') goto yy186; + if (yych == 'I') goto yy345; + if (yych != 'i') goto yy187; yy345: YYDEBUG(345, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'N') goto yy346; - if (yych != 'n') goto yy186; + if (yych == 'O') goto yy346; + if (yych != 'o') goto yy187; yy346: YYDEBUG(346, *YYCURSOR); yych = *++YYCURSOR; - if (yych != '_') goto yy186; + if (yych == 'N') goto yy347; + if (yych != 'n') goto yy187; +yy347: YYDEBUG(347, *YYCURSOR); yych = *++YYCURSOR; - if (yych != '_') goto yy186; + if (yych != '_') goto yy187; YYDEBUG(348, *YYCURSOR); + yych = *++YYCURSOR; + if (yych != '_') goto yy187; + YYDEBUG(349, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(349, *YYCURSOR); + YYDEBUG(350, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1624 "Zend/zend_language_scanner.l" +#line 1628 "Zend/zend_language_scanner.l" { const char *func_name = NULL; @@ -4386,27 +4394,27 @@ int lex_scan(zval *zendlval TSRMLS_DC) zendlval->type = IS_STRING; return T_FUNC_C; } -#line 4390 "Zend/zend_language_scanner.c" -yy350: - YYDEBUG(350, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'E') goto yy351; - if (yych != 'e') goto yy186; +#line 4398 "Zend/zend_language_scanner.c" yy351: YYDEBUG(351, *YYCURSOR); yych = *++YYCURSOR; - if (yych != '_') goto yy186; + if (yych == 'E') goto yy352; + if (yych != 'e') goto yy187; +yy352: YYDEBUG(352, *YYCURSOR); yych = *++YYCURSOR; - if (yych != '_') goto yy186; + if (yych != '_') goto yy187; YYDEBUG(353, *YYCURSOR); + yych = *++YYCURSOR; + if (yych != '_') goto yy187; + YYDEBUG(354, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(354, *YYCURSOR); + YYDEBUG(355, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1667 "Zend/zend_language_scanner.l" +#line 1671 "Zend/zend_language_scanner.l" { char *filename = zend_get_compiled_filename(TSRMLS_C); @@ -4418,37 +4426,37 @@ int lex_scan(zval *zendlval TSRMLS_DC) zendlval->type = IS_STRING; return T_FILE; } -#line 4422 "Zend/zend_language_scanner.c" -yy355: - YYDEBUG(355, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'A') goto yy356; - if (yych != 'a') goto yy186; +#line 4430 "Zend/zend_language_scanner.c" yy356: YYDEBUG(356, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'I') goto yy357; - if (yych != 'i') goto yy186; + if (yych == 'A') goto yy357; + if (yych != 'a') goto yy187; yy357: YYDEBUG(357, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy358; - if (yych != 't') goto yy186; + if (yych == 'I') goto yy358; + if (yych != 'i') goto yy187; yy358: YYDEBUG(358, *YYCURSOR); yych = *++YYCURSOR; - if (yych != '_') goto yy186; + if (yych == 'T') goto yy359; + if (yych != 't') goto yy187; +yy359: YYDEBUG(359, *YYCURSOR); yych = *++YYCURSOR; - if (yych != '_') goto yy186; + if (yych != '_') goto yy187; YYDEBUG(360, *YYCURSOR); + yych = *++YYCURSOR; + if (yych != '_') goto yy187; + YYDEBUG(361, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(361, *YYCURSOR); + YYDEBUG(362, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1604 "Zend/zend_language_scanner.l" +#line 1608 "Zend/zend_language_scanner.l" { const char *trait_name = NULL; @@ -4468,37 +4476,37 @@ int lex_scan(zval *zendlval TSRMLS_DC) return T_TRAIT_C; } -#line 4472 "Zend/zend_language_scanner.c" -yy362: - YYDEBUG(362, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'A') goto yy363; - if (yych != 'a') goto yy186; +#line 4480 "Zend/zend_language_scanner.c" yy363: YYDEBUG(363, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'S') goto yy364; - if (yych != 's') goto yy186; + if (yych == 'A') goto yy364; + if (yych != 'a') goto yy187; yy364: YYDEBUG(364, *YYCURSOR); yych = *++YYCURSOR; if (yych == 'S') goto yy365; - if (yych != 's') goto yy186; + if (yych != 's') goto yy187; yy365: YYDEBUG(365, *YYCURSOR); yych = *++YYCURSOR; - if (yych != '_') goto yy186; + if (yych == 'S') goto yy366; + if (yych != 's') goto yy187; +yy366: YYDEBUG(366, *YYCURSOR); yych = *++YYCURSOR; - if (yych != '_') goto yy186; + if (yych != '_') goto yy187; YYDEBUG(367, *YYCURSOR); + yych = *++YYCURSOR; + if (yych != '_') goto yy187; + YYDEBUG(368, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(368, *YYCURSOR); + YYDEBUG(369, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1577 "Zend/zend_language_scanner.l" +#line 1581 "Zend/zend_language_scanner.l" { const char *class_name = NULL; @@ -4525,2648 +4533,2676 @@ int lex_scan(zval *zendlval TSRMLS_DC) } return T_CLASS_C; } -#line 4529 "Zend/zend_language_scanner.c" -yy369: - YYDEBUG(369, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'L') goto yy370; - if (yych != 'l') goto yy186; +#line 4537 "Zend/zend_language_scanner.c" yy370: YYDEBUG(370, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy371; - if (yych != 't') goto yy186; + if (yych == 'L') goto yy371; + if (yych != 'l') goto yy187; yy371: YYDEBUG(371, *YYCURSOR); yych = *++YYCURSOR; - if (yych != '_') goto yy186; + if (yych == 'T') goto yy372; + if (yych != 't') goto yy187; +yy372: YYDEBUG(372, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'C') goto yy373; - if (yych != 'c') goto yy186; -yy373: + if (yych != '_') goto yy187; YYDEBUG(373, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'O') goto yy374; - if (yych != 'o') goto yy186; + if (yych == 'C') goto yy374; + if (yych != 'c') goto yy187; yy374: YYDEBUG(374, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'M') goto yy375; - if (yych != 'm') goto yy186; + if (yych == 'O') goto yy375; + if (yych != 'o') goto yy187; yy375: YYDEBUG(375, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'P') goto yy376; - if (yych != 'p') goto yy186; + if (yych == 'M') goto yy376; + if (yych != 'm') goto yy187; yy376: YYDEBUG(376, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'I') goto yy377; - if (yych != 'i') goto yy186; + if (yych == 'P') goto yy377; + if (yych != 'p') goto yy187; yy377: YYDEBUG(377, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'L') goto yy378; - if (yych != 'l') goto yy186; + if (yych == 'I') goto yy378; + if (yych != 'i') goto yy187; yy378: YYDEBUG(378, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy379; - if (yych != 'e') goto yy186; + if (yych == 'L') goto yy379; + if (yych != 'l') goto yy187; yy379: YYDEBUG(379, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'R') goto yy380; - if (yych != 'r') goto yy186; + if (yych == 'E') goto yy380; + if (yych != 'e') goto yy187; yy380: YYDEBUG(380, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'R') goto yy381; + if (yych != 'r') goto yy187; +yy381: + YYDEBUG(381, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(381, *YYCURSOR); + YYDEBUG(382, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1286 "Zend/zend_language_scanner.l" +#line 1290 "Zend/zend_language_scanner.l" { return T_HALT_COMPILER; } -#line 4595 "Zend/zend_language_scanner.c" -yy382: - YYDEBUG(382, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'S') goto yy386; - if (yych == 's') goto yy386; - goto yy186; +#line 4603 "Zend/zend_language_scanner.c" yy383: YYDEBUG(383, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy384; - if (yych != 'e') goto yy186; + if (yych == 'S') goto yy387; + if (yych == 's') goto yy387; + goto yy187; yy384: YYDEBUG(384, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'E') goto yy385; + if (yych != 'e') goto yy187; +yy385: + YYDEBUG(385, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(385, *YYCURSOR); + YYDEBUG(386, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1266 "Zend/zend_language_scanner.l" +#line 1270 "Zend/zend_language_scanner.l" { return T_USE; } -#line 4619 "Zend/zend_language_scanner.c" -yy386: - YYDEBUG(386, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'E') goto yy387; - if (yych != 'e') goto yy186; +#line 4627 "Zend/zend_language_scanner.c" yy387: YYDEBUG(387, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy388; - if (yych != 't') goto yy186; + if (yych == 'E') goto yy388; + if (yych != 'e') goto yy187; yy388: YYDEBUG(388, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'T') goto yy389; + if (yych != 't') goto yy187; +yy389: + YYDEBUG(389, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(389, *YYCURSOR); + YYDEBUG(390, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1314 "Zend/zend_language_scanner.l" +#line 1318 "Zend/zend_language_scanner.l" { return T_UNSET; } -#line 4642 "Zend/zend_language_scanner.c" -yy390: - YYDEBUG(390, *YYCURSOR); +#line 4650 "Zend/zend_language_scanner.c" +yy391: + YYDEBUG(391, *YYCURSOR); ++YYCURSOR; YYFILL(7); yych = *YYCURSOR; -yy391: - YYDEBUG(391, *YYCURSOR); +yy392: + YYDEBUG(392, *YYCURSOR); if (yych <= 'S') { if (yych <= 'D') { if (yych <= ' ') { - if (yych == '\t') goto yy390; - if (yych <= 0x1F) goto yy193; - goto yy390; + if (yych == '\t') goto yy391; + if (yych <= 0x1F) goto yy194; + goto yy391; } else { if (yych <= 'A') { - if (yych <= '@') goto yy193; - goto yy395; + if (yych <= '@') goto yy194; + goto yy396; } else { - if (yych <= 'B') goto yy393; - if (yych <= 'C') goto yy193; - goto yy398; + if (yych <= 'B') goto yy394; + if (yych <= 'C') goto yy194; + goto yy399; } } } else { if (yych <= 'I') { - if (yych == 'F') goto yy399; - if (yych <= 'H') goto yy193; - goto yy400; + if (yych == 'F') goto yy400; + if (yych <= 'H') goto yy194; + goto yy401; } else { if (yych <= 'O') { - if (yych <= 'N') goto yy193; - goto yy394; + if (yych <= 'N') goto yy194; + goto yy395; } else { - if (yych <= 'Q') goto yy193; - if (yych <= 'R') goto yy397; - goto yy396; + if (yych <= 'Q') goto yy194; + if (yych <= 'R') goto yy398; + goto yy397; } } } } else { if (yych <= 'f') { if (yych <= 'a') { - if (yych == 'U') goto yy392; - if (yych <= '`') goto yy193; - goto yy395; + if (yych == 'U') goto yy393; + if (yych <= '`') goto yy194; + goto yy396; } else { if (yych <= 'c') { - if (yych <= 'b') goto yy393; - goto yy193; + if (yych <= 'b') goto yy394; + goto yy194; } else { - if (yych <= 'd') goto yy398; - if (yych <= 'e') goto yy193; - goto yy399; + if (yych <= 'd') goto yy399; + if (yych <= 'e') goto yy194; + goto yy400; } } } else { if (yych <= 'q') { if (yych <= 'i') { - if (yych <= 'h') goto yy193; - goto yy400; + if (yych <= 'h') goto yy194; + goto yy401; } else { - if (yych == 'o') goto yy394; - goto yy193; + if (yych == 'o') goto yy395; + goto yy194; } } else { if (yych <= 's') { - if (yych <= 'r') goto yy397; - goto yy396; + if (yych <= 'r') goto yy398; + goto yy397; } else { - if (yych != 'u') goto yy193; + if (yych != 'u') goto yy194; } } } } -yy392: - YYDEBUG(392, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'N') goto yy459; - if (yych == 'n') goto yy459; - goto yy193; yy393: YYDEBUG(393, *YYCURSOR); yych = *++YYCURSOR; + if (yych == 'N') goto yy460; + if (yych == 'n') goto yy460; + goto yy194; +yy394: + YYDEBUG(394, *YYCURSOR); + yych = *++YYCURSOR; if (yych <= 'O') { - if (yych == 'I') goto yy446; - if (yych <= 'N') goto yy193; - goto yy447; + if (yych == 'I') goto yy447; + if (yych <= 'N') goto yy194; + goto yy448; } else { if (yych <= 'i') { - if (yych <= 'h') goto yy193; - goto yy446; + if (yych <= 'h') goto yy194; + goto yy447; } else { - if (yych == 'o') goto yy447; - goto yy193; + if (yych == 'o') goto yy448; + goto yy194; } } -yy394: - YYDEBUG(394, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'B') goto yy438; - if (yych == 'b') goto yy438; - goto yy193; yy395: YYDEBUG(395, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'R') goto yy431; - if (yych == 'r') goto yy431; - goto yy193; + if (yych == 'B') goto yy439; + if (yych == 'b') goto yy439; + goto yy194; yy396: YYDEBUG(396, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy423; - if (yych == 't') goto yy423; - goto yy193; + if (yych == 'R') goto yy432; + if (yych == 'r') goto yy432; + goto yy194; yy397: YYDEBUG(397, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy421; - if (yych == 'e') goto yy421; - goto yy193; + if (yych == 'T') goto yy424; + if (yych == 't') goto yy424; + goto yy194; yy398: YYDEBUG(398, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'O') goto yy417; - if (yych == 'o') goto yy417; - goto yy193; + if (yych == 'E') goto yy422; + if (yych == 'e') goto yy422; + goto yy194; yy399: YYDEBUG(399, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'L') goto yy410; - if (yych == 'l') goto yy410; - goto yy193; + if (yych == 'O') goto yy418; + if (yych == 'o') goto yy418; + goto yy194; yy400: YYDEBUG(400, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'N') goto yy401; - if (yych != 'n') goto yy193; + if (yych == 'L') goto yy411; + if (yych == 'l') goto yy411; + goto yy194; yy401: YYDEBUG(401, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy402; - if (yych != 't') goto yy193; + if (yych == 'N') goto yy402; + if (yych != 'n') goto yy194; yy402: YYDEBUG(402, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy403; - if (yych != 'e') goto yy405; + if (yych == 'T') goto yy403; + if (yych != 't') goto yy194; yy403: YYDEBUG(403, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'G') goto yy408; - if (yych == 'g') goto yy408; - goto yy193; + if (yych == 'E') goto yy404; + if (yych != 'e') goto yy406; yy404: YYDEBUG(404, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'G') goto yy409; + if (yych == 'g') goto yy409; + goto yy194; +yy405: + YYDEBUG(405, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; -yy405: - YYDEBUG(405, *YYCURSOR); +yy406: + YYDEBUG(406, *YYCURSOR); if (yych <= 0x1F) { - if (yych == '\t') goto yy404; - goto yy193; + if (yych == '\t') goto yy405; + goto yy194; } else { - if (yych <= ' ') goto yy404; - if (yych != ')') goto yy193; + if (yych <= ' ') goto yy405; + if (yych != ')') goto yy194; } - YYDEBUG(406, *YYCURSOR); - ++YYCURSOR; YYDEBUG(407, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(408, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1214 "Zend/zend_language_scanner.l" +#line 1218 "Zend/zend_language_scanner.l" { return T_INT_CAST; } -#line 4818 "Zend/zend_language_scanner.c" -yy408: - YYDEBUG(408, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'E') goto yy409; - if (yych != 'e') goto yy193; +#line 4826 "Zend/zend_language_scanner.c" yy409: YYDEBUG(409, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'R') goto yy404; - if (yych == 'r') goto yy404; - goto yy193; + if (yych == 'E') goto yy410; + if (yych != 'e') goto yy194; yy410: YYDEBUG(410, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'O') goto yy411; - if (yych != 'o') goto yy193; + if (yych == 'R') goto yy405; + if (yych == 'r') goto yy405; + goto yy194; yy411: YYDEBUG(411, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'A') goto yy412; - if (yych != 'a') goto yy193; + if (yych == 'O') goto yy412; + if (yych != 'o') goto yy194; yy412: YYDEBUG(412, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy413; - if (yych != 't') goto yy193; + if (yych == 'A') goto yy413; + if (yych != 'a') goto yy194; yy413: YYDEBUG(413, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'T') goto yy414; + if (yych != 't') goto yy194; +yy414: + YYDEBUG(414, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; - YYDEBUG(414, *YYCURSOR); + YYDEBUG(415, *YYCURSOR); if (yych <= 0x1F) { - if (yych == '\t') goto yy413; - goto yy193; + if (yych == '\t') goto yy414; + goto yy194; } else { - if (yych <= ' ') goto yy413; - if (yych != ')') goto yy193; + if (yych <= ' ') goto yy414; + if (yych != ')') goto yy194; } - YYDEBUG(415, *YYCURSOR); - ++YYCURSOR; YYDEBUG(416, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(417, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1218 "Zend/zend_language_scanner.l" +#line 1222 "Zend/zend_language_scanner.l" { return T_DOUBLE_CAST; } -#line 4866 "Zend/zend_language_scanner.c" -yy417: - YYDEBUG(417, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'U') goto yy418; - if (yych != 'u') goto yy193; +#line 4874 "Zend/zend_language_scanner.c" yy418: YYDEBUG(418, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'B') goto yy419; - if (yych != 'b') goto yy193; + if (yych == 'U') goto yy419; + if (yych != 'u') goto yy194; yy419: YYDEBUG(419, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'L') goto yy420; - if (yych != 'l') goto yy193; + if (yych == 'B') goto yy420; + if (yych != 'b') goto yy194; yy420: YYDEBUG(420, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy413; - if (yych == 'e') goto yy413; - goto yy193; + if (yych == 'L') goto yy421; + if (yych != 'l') goto yy194; yy421: YYDEBUG(421, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'A') goto yy422; - if (yych != 'a') goto yy193; + if (yych == 'E') goto yy414; + if (yych == 'e') goto yy414; + goto yy194; yy422: YYDEBUG(422, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'L') goto yy413; - if (yych == 'l') goto yy413; - goto yy193; + if (yych == 'A') goto yy423; + if (yych != 'a') goto yy194; yy423: YYDEBUG(423, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'R') goto yy424; - if (yych != 'r') goto yy193; + if (yych == 'L') goto yy414; + if (yych == 'l') goto yy414; + goto yy194; yy424: YYDEBUG(424, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'I') goto yy425; - if (yych != 'i') goto yy193; + if (yych == 'R') goto yy425; + if (yych != 'r') goto yy194; yy425: YYDEBUG(425, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'N') goto yy426; - if (yych != 'n') goto yy193; + if (yych == 'I') goto yy426; + if (yych != 'i') goto yy194; yy426: YYDEBUG(426, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'G') goto yy427; - if (yych != 'g') goto yy193; + if (yych == 'N') goto yy427; + if (yych != 'n') goto yy194; yy427: YYDEBUG(427, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'G') goto yy428; + if (yych != 'g') goto yy194; +yy428: + YYDEBUG(428, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; - YYDEBUG(428, *YYCURSOR); + YYDEBUG(429, *YYCURSOR); if (yych <= 0x1F) { - if (yych == '\t') goto yy427; - goto yy193; + if (yych == '\t') goto yy428; + goto yy194; } else { - if (yych <= ' ') goto yy427; - if (yych != ')') goto yy193; + if (yych <= ' ') goto yy428; + if (yych != ')') goto yy194; } - YYDEBUG(429, *YYCURSOR); - ++YYCURSOR; YYDEBUG(430, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(431, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1222 "Zend/zend_language_scanner.l" +#line 1226 "Zend/zend_language_scanner.l" { return T_STRING_CAST; } -#line 4940 "Zend/zend_language_scanner.c" -yy431: - YYDEBUG(431, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'R') goto yy432; - if (yych != 'r') goto yy193; +#line 4948 "Zend/zend_language_scanner.c" yy432: YYDEBUG(432, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'A') goto yy433; - if (yych != 'a') goto yy193; + if (yych == 'R') goto yy433; + if (yych != 'r') goto yy194; yy433: YYDEBUG(433, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'Y') goto yy434; - if (yych != 'y') goto yy193; + if (yych == 'A') goto yy434; + if (yych != 'a') goto yy194; yy434: YYDEBUG(434, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'Y') goto yy435; + if (yych != 'y') goto yy194; +yy435: + YYDEBUG(435, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; - YYDEBUG(435, *YYCURSOR); + YYDEBUG(436, *YYCURSOR); if (yych <= 0x1F) { - if (yych == '\t') goto yy434; - goto yy193; + if (yych == '\t') goto yy435; + goto yy194; } else { - if (yych <= ' ') goto yy434; - if (yych != ')') goto yy193; + if (yych <= ' ') goto yy435; + if (yych != ')') goto yy194; } - YYDEBUG(436, *YYCURSOR); - ++YYCURSOR; YYDEBUG(437, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(438, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1226 "Zend/zend_language_scanner.l" +#line 1230 "Zend/zend_language_scanner.l" { return T_ARRAY_CAST; } -#line 4977 "Zend/zend_language_scanner.c" -yy438: - YYDEBUG(438, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'J') goto yy439; - if (yych != 'j') goto yy193; +#line 4985 "Zend/zend_language_scanner.c" yy439: YYDEBUG(439, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy440; - if (yych != 'e') goto yy193; + if (yych == 'J') goto yy440; + if (yych != 'j') goto yy194; yy440: YYDEBUG(440, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'C') goto yy441; - if (yych != 'c') goto yy193; + if (yych == 'E') goto yy441; + if (yych != 'e') goto yy194; yy441: YYDEBUG(441, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy442; - if (yych != 't') goto yy193; + if (yych == 'C') goto yy442; + if (yych != 'c') goto yy194; yy442: YYDEBUG(442, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'T') goto yy443; + if (yych != 't') goto yy194; +yy443: + YYDEBUG(443, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; - YYDEBUG(443, *YYCURSOR); + YYDEBUG(444, *YYCURSOR); if (yych <= 0x1F) { - if (yych == '\t') goto yy442; - goto yy193; + if (yych == '\t') goto yy443; + goto yy194; } else { - if (yych <= ' ') goto yy442; - if (yych != ')') goto yy193; + if (yych <= ' ') goto yy443; + if (yych != ')') goto yy194; } - YYDEBUG(444, *YYCURSOR); - ++YYCURSOR; YYDEBUG(445, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(446, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1230 "Zend/zend_language_scanner.l" +#line 1234 "Zend/zend_language_scanner.l" { return T_OBJECT_CAST; } -#line 5019 "Zend/zend_language_scanner.c" -yy446: - YYDEBUG(446, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'N') goto yy456; - if (yych == 'n') goto yy456; - goto yy193; +#line 5027 "Zend/zend_language_scanner.c" yy447: YYDEBUG(447, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'O') goto yy448; - if (yych != 'o') goto yy193; + if (yych == 'N') goto yy457; + if (yych == 'n') goto yy457; + goto yy194; yy448: YYDEBUG(448, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'L') goto yy449; - if (yych != 'l') goto yy193; + if (yych == 'O') goto yy449; + if (yych != 'o') goto yy194; yy449: YYDEBUG(449, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy454; - if (yych == 'e') goto yy454; - goto yy451; + if (yych == 'L') goto yy450; + if (yych != 'l') goto yy194; yy450: YYDEBUG(450, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'E') goto yy455; + if (yych == 'e') goto yy455; + goto yy452; +yy451: + YYDEBUG(451, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; -yy451: - YYDEBUG(451, *YYCURSOR); +yy452: + YYDEBUG(452, *YYCURSOR); if (yych <= 0x1F) { - if (yych == '\t') goto yy450; - goto yy193; + if (yych == '\t') goto yy451; + goto yy194; } else { - if (yych <= ' ') goto yy450; - if (yych != ')') goto yy193; + if (yych <= ' ') goto yy451; + if (yych != ')') goto yy194; } - YYDEBUG(452, *YYCURSOR); - ++YYCURSOR; YYDEBUG(453, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(454, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1234 "Zend/zend_language_scanner.l" +#line 1238 "Zend/zend_language_scanner.l" { return T_BOOL_CAST; } -#line 5064 "Zend/zend_language_scanner.c" -yy454: - YYDEBUG(454, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'A') goto yy455; - if (yych != 'a') goto yy193; +#line 5072 "Zend/zend_language_scanner.c" yy455: YYDEBUG(455, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'N') goto yy450; - if (yych == 'n') goto yy450; - goto yy193; + if (yych == 'A') goto yy456; + if (yych != 'a') goto yy194; yy456: YYDEBUG(456, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'A') goto yy457; - if (yych != 'a') goto yy193; + if (yych == 'N') goto yy451; + if (yych == 'n') goto yy451; + goto yy194; yy457: YYDEBUG(457, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'R') goto yy458; - if (yych != 'r') goto yy193; + if (yych == 'A') goto yy458; + if (yych != 'a') goto yy194; yy458: YYDEBUG(458, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'Y') goto yy427; - if (yych == 'y') goto yy427; - goto yy193; + if (yych == 'R') goto yy459; + if (yych != 'r') goto yy194; yy459: YYDEBUG(459, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'S') goto yy460; - if (yych != 's') goto yy193; + if (yych == 'Y') goto yy428; + if (yych == 'y') goto yy428; + goto yy194; yy460: YYDEBUG(460, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy461; - if (yych != 'e') goto yy193; + if (yych == 'S') goto yy461; + if (yych != 's') goto yy194; yy461: YYDEBUG(461, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy462; - if (yych != 't') goto yy193; + if (yych == 'E') goto yy462; + if (yych != 'e') goto yy194; yy462: YYDEBUG(462, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'T') goto yy463; + if (yych != 't') goto yy194; +yy463: + YYDEBUG(463, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; - YYDEBUG(463, *YYCURSOR); + YYDEBUG(464, *YYCURSOR); if (yych <= 0x1F) { - if (yych == '\t') goto yy462; - goto yy193; + if (yych == '\t') goto yy463; + goto yy194; } else { - if (yych <= ' ') goto yy462; - if (yych != ')') goto yy193; + if (yych <= ' ') goto yy463; + if (yych != ')') goto yy194; } - YYDEBUG(464, *YYCURSOR); - ++YYCURSOR; YYDEBUG(465, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(466, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1238 "Zend/zend_language_scanner.l" +#line 1242 "Zend/zend_language_scanner.l" { return T_UNSET_CAST; } -#line 5128 "Zend/zend_language_scanner.c" -yy466: - YYDEBUG(466, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'R') goto yy467; - if (yych != 'r') goto yy186; +#line 5136 "Zend/zend_language_scanner.c" yy467: YYDEBUG(467, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'R') goto yy468; + if (yych != 'r') goto yy187; +yy468: + YYDEBUG(468, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(468, *YYCURSOR); + YYDEBUG(469, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1210 "Zend/zend_language_scanner.l" +#line 1214 "Zend/zend_language_scanner.l" { return T_VAR; } -#line 5146 "Zend/zend_language_scanner.c" -yy469: - YYDEBUG(469, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'M') goto yy473; - if (yych == 'm') goto yy473; - goto yy186; +#line 5154 "Zend/zend_language_scanner.c" yy470: YYDEBUG(470, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'W') goto yy471; - if (yych != 'w') goto yy186; + if (yych == 'M') goto yy474; + if (yych == 'm') goto yy474; + goto yy187; yy471: YYDEBUG(471, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'W') goto yy472; + if (yych != 'w') goto yy187; +yy472: + YYDEBUG(472, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(472, *YYCURSOR); + YYDEBUG(473, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1202 "Zend/zend_language_scanner.l" +#line 1206 "Zend/zend_language_scanner.l" { return T_NEW; } -#line 5170 "Zend/zend_language_scanner.c" -yy473: - YYDEBUG(473, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'E') goto yy474; - if (yych != 'e') goto yy186; +#line 5178 "Zend/zend_language_scanner.c" yy474: YYDEBUG(474, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'S') goto yy475; - if (yych != 's') goto yy186; + if (yych == 'E') goto yy475; + if (yych != 'e') goto yy187; yy475: YYDEBUG(475, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'P') goto yy476; - if (yych != 'p') goto yy186; + if (yych == 'S') goto yy476; + if (yych != 's') goto yy187; yy476: YYDEBUG(476, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'A') goto yy477; - if (yych != 'a') goto yy186; + if (yych == 'P') goto yy477; + if (yych != 'p') goto yy187; yy477: YYDEBUG(477, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'C') goto yy478; - if (yych != 'c') goto yy186; + if (yych == 'A') goto yy478; + if (yych != 'a') goto yy187; yy478: YYDEBUG(478, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy479; - if (yych != 'e') goto yy186; + if (yych == 'C') goto yy479; + if (yych != 'c') goto yy187; yy479: YYDEBUG(479, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'E') goto yy480; + if (yych != 'e') goto yy187; +yy480: + YYDEBUG(480, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(480, *YYCURSOR); + YYDEBUG(481, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1262 "Zend/zend_language_scanner.l" +#line 1266 "Zend/zend_language_scanner.l" { return T_NAMESPACE; } -#line 5213 "Zend/zend_language_scanner.c" -yy481: - YYDEBUG(481, *YYCURSOR); - ++YYCURSOR; +#line 5221 "Zend/zend_language_scanner.c" +yy482: YYDEBUG(482, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(483, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1194 "Zend/zend_language_scanner.l" +#line 1198 "Zend/zend_language_scanner.l" { return T_PAAMAYIM_NEKUDOTAYIM; } -#line 5223 "Zend/zend_language_scanner.c" -yy483: - YYDEBUG(483, *YYCURSOR); +#line 5231 "Zend/zend_language_scanner.c" +yy484: + YYDEBUG(484, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; -yy484: - YYDEBUG(484, *YYCURSOR); +yy485: + YYDEBUG(485, *YYCURSOR); if (yych <= '\f') { - if (yych <= 0x08) goto yy140; - if (yych <= '\n') goto yy483; - goto yy140; + if (yych <= 0x08) goto yy141; + if (yych <= '\n') goto yy484; + goto yy141; } else { - if (yych <= '\r') goto yy483; - if (yych == ' ') goto yy483; - goto yy140; + if (yych <= '\r') goto yy484; + if (yych == ' ') goto yy484; + goto yy141; } -yy485: - YYDEBUG(485, *YYCURSOR); - ++YYCURSOR; +yy486: YYDEBUG(486, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(487, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1370 "Zend/zend_language_scanner.l" +#line 1374 "Zend/zend_language_scanner.l" { return T_MINUS_EQUAL; } -#line 5249 "Zend/zend_language_scanner.c" -yy487: - YYDEBUG(487, *YYCURSOR); - ++YYCURSOR; +#line 5257 "Zend/zend_language_scanner.c" +yy488: YYDEBUG(488, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(489, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1338 "Zend/zend_language_scanner.l" +#line 1342 "Zend/zend_language_scanner.l" { return T_DEC; } -#line 5259 "Zend/zend_language_scanner.c" -yy489: - YYDEBUG(489, *YYCURSOR); - ++YYCURSOR; +#line 5267 "Zend/zend_language_scanner.c" +yy490: YYDEBUG(490, *YYCURSOR); + ++YYCURSOR; + YYDEBUG(491, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1164 "Zend/zend_language_scanner.l" +#line 1168 "Zend/zend_language_scanner.l" { yy_push_state(ST_LOOKING_FOR_PROPERTY TSRMLS_CC); return T_OBJECT_OPERATOR; } -#line 5270 "Zend/zend_language_scanner.c" -yy491: - YYDEBUG(491, *YYCURSOR); +#line 5278 "Zend/zend_language_scanner.c" +yy492: + YYDEBUG(492, *YYCURSOR); yych = *++YYCURSOR; if (yych <= 'O') { - if (yych == 'I') goto yy498; - if (yych <= 'N') goto yy186; - goto yy499; + if (yych == 'I') goto yy499; + if (yych <= 'N') goto yy187; + goto yy500; } else { if (yych <= 'i') { - if (yych <= 'h') goto yy186; - goto yy498; + if (yych <= 'h') goto yy187; + goto yy499; } else { - if (yych == 'o') goto yy499; - goto yy186; + if (yych == 'o') goto yy500; + goto yy187; } } -yy492: - YYDEBUG(492, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'B') goto yy493; - if (yych != 'b') goto yy186; yy493: YYDEBUG(493, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'L') goto yy494; - if (yych != 'l') goto yy186; + if (yych == 'B') goto yy494; + if (yych != 'b') goto yy187; yy494: YYDEBUG(494, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'I') goto yy495; - if (yych != 'i') goto yy186; + if (yych == 'L') goto yy495; + if (yych != 'l') goto yy187; yy495: YYDEBUG(495, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'C') goto yy496; - if (yych != 'c') goto yy186; + if (yych == 'I') goto yy496; + if (yych != 'i') goto yy187; yy496: YYDEBUG(496, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'C') goto yy497; + if (yych != 'c') goto yy187; +yy497: + YYDEBUG(497, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(497, *YYCURSOR); + YYDEBUG(498, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1310 "Zend/zend_language_scanner.l" +#line 1314 "Zend/zend_language_scanner.l" { return T_PUBLIC; } -#line 5319 "Zend/zend_language_scanner.c" -yy498: - YYDEBUG(498, *YYCURSOR); +#line 5327 "Zend/zend_language_scanner.c" +yy499: + YYDEBUG(499, *YYCURSOR); yych = *++YYCURSOR; if (yych <= 'V') { - if (yych == 'N') goto yy507; - if (yych <= 'U') goto yy186; - goto yy508; + if (yych == 'N') goto yy508; + if (yych <= 'U') goto yy187; + goto yy509; } else { if (yych <= 'n') { - if (yych <= 'm') goto yy186; - goto yy507; + if (yych <= 'm') goto yy187; + goto yy508; } else { - if (yych == 'v') goto yy508; - goto yy186; + if (yych == 'v') goto yy509; + goto yy187; } } -yy499: - YYDEBUG(499, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'T') goto yy500; - if (yych != 't') goto yy186; yy500: YYDEBUG(500, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy501; - if (yych != 'e') goto yy186; + if (yych == 'T') goto yy501; + if (yych != 't') goto yy187; yy501: YYDEBUG(501, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'C') goto yy502; - if (yych != 'c') goto yy186; + if (yych == 'E') goto yy502; + if (yych != 'e') goto yy187; yy502: YYDEBUG(502, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy503; - if (yych != 't') goto yy186; + if (yych == 'C') goto yy503; + if (yych != 'c') goto yy187; yy503: YYDEBUG(503, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy504; - if (yych != 'e') goto yy186; + if (yych == 'T') goto yy504; + if (yych != 't') goto yy187; yy504: YYDEBUG(504, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'D') goto yy505; - if (yych != 'd') goto yy186; + if (yych == 'E') goto yy505; + if (yych != 'e') goto yy187; yy505: YYDEBUG(505, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'D') goto yy506; + if (yych != 'd') goto yy187; +yy506: + YYDEBUG(506, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(506, *YYCURSOR); + YYDEBUG(507, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1306 "Zend/zend_language_scanner.l" +#line 1310 "Zend/zend_language_scanner.l" { return T_PROTECTED; } -#line 5378 "Zend/zend_language_scanner.c" -yy507: - YYDEBUG(507, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'T') goto yy513; - if (yych == 't') goto yy513; - goto yy186; +#line 5386 "Zend/zend_language_scanner.c" yy508: YYDEBUG(508, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'A') goto yy509; - if (yych != 'a') goto yy186; + if (yych == 'T') goto yy514; + if (yych == 't') goto yy514; + goto yy187; yy509: YYDEBUG(509, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy510; - if (yych != 't') goto yy186; + if (yych == 'A') goto yy510; + if (yych != 'a') goto yy187; yy510: YYDEBUG(510, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy511; - if (yych != 'e') goto yy186; + if (yych == 'T') goto yy511; + if (yych != 't') goto yy187; yy511: YYDEBUG(511, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'E') goto yy512; + if (yych != 'e') goto yy187; +yy512: + YYDEBUG(512, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(512, *YYCURSOR); + YYDEBUG(513, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1302 "Zend/zend_language_scanner.l" +#line 1306 "Zend/zend_language_scanner.l" { return T_PRIVATE; } -#line 5412 "Zend/zend_language_scanner.c" -yy513: - YYDEBUG(513, *YYCURSOR); +#line 5420 "Zend/zend_language_scanner.c" +yy514: + YYDEBUG(514, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(514, *YYCURSOR); + YYDEBUG(515, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1140 "Zend/zend_language_scanner.l" +#line 1144 "Zend/zend_language_scanner.l" { return T_PRINT; } -#line 5425 "Zend/zend_language_scanner.c" -yy515: - YYDEBUG(515, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'O') goto yy520; - if (yych == 'o') goto yy520; - goto yy186; +#line 5433 "Zend/zend_language_scanner.c" yy516: YYDEBUG(516, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy517; - if (yych != 't') goto yy186; + if (yych == 'O') goto yy521; + if (yych == 'o') goto yy521; + goto yy187; yy517: YYDEBUG(517, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'O') goto yy518; - if (yych != 'o') goto yy186; + if (yych == 'T') goto yy518; + if (yych != 't') goto yy187; yy518: YYDEBUG(518, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'O') goto yy519; + if (yych != 'o') goto yy187; +yy519: + YYDEBUG(519, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(519, *YYCURSOR); + YYDEBUG(520, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1132 "Zend/zend_language_scanner.l" +#line 1136 "Zend/zend_language_scanner.l" { return T_GOTO; } -#line 5454 "Zend/zend_language_scanner.c" -yy520: - YYDEBUG(520, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'B') goto yy521; - if (yych != 'b') goto yy186; +#line 5462 "Zend/zend_language_scanner.c" yy521: YYDEBUG(521, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'A') goto yy522; - if (yych != 'a') goto yy186; + if (yych == 'B') goto yy522; + if (yych != 'b') goto yy187; yy522: YYDEBUG(522, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'L') goto yy523; - if (yych != 'l') goto yy186; + if (yych == 'A') goto yy523; + if (yych != 'a') goto yy187; yy523: YYDEBUG(523, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'L') goto yy524; + if (yych != 'l') goto yy187; +yy524: + YYDEBUG(524, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(524, *YYCURSOR); + YYDEBUG(525, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1274 "Zend/zend_language_scanner.l" +#line 1278 "Zend/zend_language_scanner.l" { return T_GLOBAL; } -#line 5482 "Zend/zend_language_scanner.c" -yy525: - YYDEBUG(525, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == '<') goto yy533; - goto yy193; +#line 5490 "Zend/zend_language_scanner.c" yy526: YYDEBUG(526, *YYCURSOR); yych = *++YYCURSOR; - goto yy180; + if (yych == '<') goto yy534; + goto yy194; yy527: YYDEBUG(527, *YYCURSOR); yych = *++YYCURSOR; - goto yy178; + goto yy181; yy528: YYDEBUG(528, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy529; - if (yych != 'e') goto yy186; + goto yy179; yy529: YYDEBUG(529, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'A') goto yy530; - if (yych != 'a') goto yy186; + if (yych == 'E') goto yy530; + if (yych != 'e') goto yy187; yy530: YYDEBUG(530, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'K') goto yy531; - if (yych != 'k') goto yy186; + if (yych == 'A') goto yy531; + if (yych != 'a') goto yy187; yy531: YYDEBUG(531, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'K') goto yy532; + if (yych != 'k') goto yy187; +yy532: + YYDEBUG(532, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(532, *YYCURSOR); + YYDEBUG(533, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1124 "Zend/zend_language_scanner.l" +#line 1128 "Zend/zend_language_scanner.l" { return T_BREAK; } -#line 5523 "Zend/zend_language_scanner.c" -yy533: - YYDEBUG(533, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == '<') goto yy269; - goto yy193; +#line 5531 "Zend/zend_language_scanner.c" yy534: YYDEBUG(534, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'A') goto yy541; - if (yych == 'a') goto yy541; - goto yy186; + if (yych == '<') goto yy270; + goto yy194; yy535: YYDEBUG(535, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'I') goto yy536; - if (yych != 'i') goto yy186; + if (yych == 'A') goto yy542; + if (yych == 'a') goto yy542; + goto yy187; yy536: YYDEBUG(536, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy537; - if (yych != 't') goto yy186; + if (yych == 'I') goto yy537; + if (yych != 'i') goto yy187; yy537: YYDEBUG(537, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'C') goto yy538; - if (yych != 'c') goto yy186; + if (yych == 'T') goto yy538; + if (yych != 't') goto yy187; yy538: YYDEBUG(538, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'H') goto yy539; - if (yych != 'h') goto yy186; + if (yych == 'C') goto yy539; + if (yych != 'c') goto yy187; yy539: YYDEBUG(539, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'H') goto yy540; + if (yych != 'h') goto yy187; +yy540: + YYDEBUG(540, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(540, *YYCURSOR); + YYDEBUG(541, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1108 "Zend/zend_language_scanner.l" +#line 1112 "Zend/zend_language_scanner.l" { return T_SWITCH; } -#line 5567 "Zend/zend_language_scanner.c" -yy541: - YYDEBUG(541, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'T') goto yy542; - if (yych != 't') goto yy186; +#line 5575 "Zend/zend_language_scanner.c" yy542: YYDEBUG(542, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'I') goto yy543; - if (yych != 'i') goto yy186; + if (yych == 'T') goto yy543; + if (yych != 't') goto yy187; yy543: YYDEBUG(543, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'C') goto yy544; - if (yych != 'c') goto yy186; + if (yych == 'I') goto yy544; + if (yych != 'i') goto yy187; yy544: YYDEBUG(544, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'C') goto yy545; + if (yych != 'c') goto yy187; +yy545: + YYDEBUG(545, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(545, *YYCURSOR); + YYDEBUG(546, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1290 "Zend/zend_language_scanner.l" +#line 1294 "Zend/zend_language_scanner.l" { return T_STATIC; } -#line 5595 "Zend/zend_language_scanner.c" -yy546: - YYDEBUG(546, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'S') goto yy557; - if (yych == 's') goto yy557; - goto yy186; +#line 5603 "Zend/zend_language_scanner.c" yy547: YYDEBUG(547, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'D') goto yy555; - if (yych == 'd') goto yy555; - goto yy186; + if (yych == 'S') goto yy558; + if (yych == 's') goto yy558; + goto yy187; yy548: YYDEBUG(548, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'R') goto yy551; - if (yych == 'r') goto yy551; - goto yy186; + if (yych == 'D') goto yy556; + if (yych == 'd') goto yy556; + goto yy187; yy549: YYDEBUG(549, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'R') goto yy552; + if (yych == 'r') goto yy552; + goto yy187; +yy550: + YYDEBUG(550, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(550, *YYCURSOR); + YYDEBUG(551, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1104 "Zend/zend_language_scanner.l" +#line 1108 "Zend/zend_language_scanner.l" { return T_AS; } -#line 5626 "Zend/zend_language_scanner.c" -yy551: - YYDEBUG(551, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'A') goto yy552; - if (yych != 'a') goto yy186; +#line 5634 "Zend/zend_language_scanner.c" yy552: YYDEBUG(552, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'Y') goto yy553; - if (yych != 'y') goto yy186; + if (yych == 'A') goto yy553; + if (yych != 'a') goto yy187; yy553: YYDEBUG(553, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'Y') goto yy554; + if (yych != 'y') goto yy187; +yy554: + YYDEBUG(554, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(554, *YYCURSOR); + YYDEBUG(555, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1326 "Zend/zend_language_scanner.l" +#line 1330 "Zend/zend_language_scanner.l" { return T_ARRAY; } -#line 5649 "Zend/zend_language_scanner.c" -yy555: - YYDEBUG(555, *YYCURSOR); +#line 5657 "Zend/zend_language_scanner.c" +yy556: + YYDEBUG(556, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(556, *YYCURSOR); + YYDEBUG(557, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1422 "Zend/zend_language_scanner.l" +#line 1426 "Zend/zend_language_scanner.l" { return T_LOGICAL_AND; } -#line 5662 "Zend/zend_language_scanner.c" -yy557: - YYDEBUG(557, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'T') goto yy558; - if (yych != 't') goto yy186; +#line 5670 "Zend/zend_language_scanner.c" yy558: YYDEBUG(558, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'R') goto yy559; - if (yych != 'r') goto yy186; + if (yych == 'T') goto yy559; + if (yych != 't') goto yy187; yy559: YYDEBUG(559, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'A') goto yy560; - if (yych != 'a') goto yy186; + if (yych == 'R') goto yy560; + if (yych != 'r') goto yy187; yy560: YYDEBUG(560, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'C') goto yy561; - if (yych != 'c') goto yy186; + if (yych == 'A') goto yy561; + if (yych != 'a') goto yy187; yy561: YYDEBUG(561, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy562; - if (yych != 't') goto yy186; + if (yych == 'C') goto yy562; + if (yych != 'c') goto yy187; yy562: YYDEBUG(562, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'T') goto yy563; + if (yych != 't') goto yy187; +yy563: + YYDEBUG(563, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(563, *YYCURSOR); + YYDEBUG(564, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1294 "Zend/zend_language_scanner.l" +#line 1298 "Zend/zend_language_scanner.l" { return T_ABSTRACT; } -#line 5700 "Zend/zend_language_scanner.c" -yy564: - YYDEBUG(564, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'I') goto yy565; - if (yych != 'i') goto yy186; +#line 5708 "Zend/zend_language_scanner.c" yy565: YYDEBUG(565, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'L') goto yy566; - if (yych != 'l') goto yy186; + if (yych == 'I') goto yy566; + if (yych != 'i') goto yy187; yy566: YYDEBUG(566, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy567; - if (yych != 'e') goto yy186; + if (yych == 'L') goto yy567; + if (yych != 'l') goto yy187; yy567: YYDEBUG(567, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'E') goto yy568; + if (yych != 'e') goto yy187; +yy568: + YYDEBUG(568, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(568, *YYCURSOR); + YYDEBUG(569, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1064 "Zend/zend_language_scanner.l" +#line 1068 "Zend/zend_language_scanner.l" { return T_WHILE; } -#line 5728 "Zend/zend_language_scanner.c" -yy569: - YYDEBUG(569, *YYCURSOR); +#line 5736 "Zend/zend_language_scanner.c" +yy570: + YYDEBUG(570, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(570, *YYCURSOR); + YYDEBUG(571, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1048 "Zend/zend_language_scanner.l" +#line 1052 "Zend/zend_language_scanner.l" { return T_IF; } -#line 5741 "Zend/zend_language_scanner.c" -yy571: - YYDEBUG(571, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'P') goto yy613; - if (yych == 'p') goto yy613; - goto yy186; +#line 5749 "Zend/zend_language_scanner.c" yy572: YYDEBUG(572, *YYCURSOR); yych = *++YYCURSOR; + if (yych == 'P') goto yy614; + if (yych == 'p') goto yy614; + goto yy187; +yy573: + YYDEBUG(573, *YYCURSOR); + yych = *++YYCURSOR; if (yych <= 'T') { if (yych <= 'C') { - if (yych <= 'B') goto yy186; - goto yy580; + if (yych <= 'B') goto yy187; + goto yy581; } else { - if (yych <= 'R') goto yy186; - if (yych <= 'S') goto yy578; - goto yy579; + if (yych <= 'R') goto yy187; + if (yych <= 'S') goto yy579; + goto yy580; } } else { if (yych <= 'r') { - if (yych == 'c') goto yy580; - goto yy186; + if (yych == 'c') goto yy581; + goto yy187; } else { - if (yych <= 's') goto yy578; - if (yych <= 't') goto yy579; - goto yy186; + if (yych <= 's') goto yy579; + if (yych <= 't') goto yy580; + goto yy187; } } -yy573: - YYDEBUG(573, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'S') goto yy574; - if (yych != 's') goto yy186; yy574: YYDEBUG(574, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy575; - if (yych != 'e') goto yy186; + if (yych == 'S') goto yy575; + if (yych != 's') goto yy187; yy575: YYDEBUG(575, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy576; - if (yych != 't') goto yy186; + if (yych == 'E') goto yy576; + if (yych != 'e') goto yy187; yy576: YYDEBUG(576, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'T') goto yy577; + if (yych != 't') goto yy187; +yy577: + YYDEBUG(577, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(577, *YYCURSOR); + YYDEBUG(578, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1278 "Zend/zend_language_scanner.l" +#line 1282 "Zend/zend_language_scanner.l" { return T_ISSET; } -#line 5797 "Zend/zend_language_scanner.c" -yy578: - YYDEBUG(578, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'T') goto yy599; - if (yych == 't') goto yy599; - goto yy186; +#line 5805 "Zend/zend_language_scanner.c" yy579: YYDEBUG(579, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy592; - if (yych == 'e') goto yy592; - goto yy186; + if (yych == 'T') goto yy600; + if (yych == 't') goto yy600; + goto yy187; yy580: YYDEBUG(580, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'L') goto yy581; - if (yych != 'l') goto yy186; + if (yych == 'E') goto yy593; + if (yych == 'e') goto yy593; + goto yy187; yy581: YYDEBUG(581, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'U') goto yy582; - if (yych != 'u') goto yy186; + if (yych == 'L') goto yy582; + if (yych != 'l') goto yy187; yy582: YYDEBUG(582, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'D') goto yy583; - if (yych != 'd') goto yy186; + if (yych == 'U') goto yy583; + if (yych != 'u') goto yy187; yy583: YYDEBUG(583, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy584; - if (yych != 'e') goto yy186; + if (yych == 'D') goto yy584; + if (yych != 'd') goto yy187; yy584: YYDEBUG(584, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'E') goto yy585; + if (yych != 'e') goto yy187; +yy585: + YYDEBUG(585, *YYCURSOR); ++YYCURSOR; if ((yych = *YYCURSOR) <= '^') { if (yych <= '9') { - if (yych >= '0') goto yy185; + if (yych >= '0') goto yy186; } else { - if (yych <= '@') goto yy585; - if (yych <= 'Z') goto yy185; + if (yych <= '@') goto yy586; + if (yych <= 'Z') goto yy186; } } else { if (yych <= '`') { - if (yych <= '_') goto yy586; + if (yych <= '_') goto yy587; } else { - if (yych <= 'z') goto yy185; - if (yych >= 0x7F) goto yy185; + if (yych <= 'z') goto yy186; + if (yych >= 0x7F) goto yy186; } } -yy585: - YYDEBUG(585, *YYCURSOR); +yy586: + YYDEBUG(586, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1246 "Zend/zend_language_scanner.l" +#line 1250 "Zend/zend_language_scanner.l" { return T_INCLUDE; } -#line 5855 "Zend/zend_language_scanner.c" -yy586: - YYDEBUG(586, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'O') goto yy587; - if (yych != 'o') goto yy186; +#line 5863 "Zend/zend_language_scanner.c" yy587: YYDEBUG(587, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'N') goto yy588; - if (yych != 'n') goto yy186; + if (yych == 'O') goto yy588; + if (yych != 'o') goto yy187; yy588: YYDEBUG(588, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'C') goto yy589; - if (yych != 'c') goto yy186; + if (yych == 'N') goto yy589; + if (yych != 'n') goto yy187; yy589: YYDEBUG(589, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy590; - if (yych != 'e') goto yy186; + if (yych == 'C') goto yy590; + if (yych != 'c') goto yy187; yy590: YYDEBUG(590, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'E') goto yy591; + if (yych != 'e') goto yy187; +yy591: + YYDEBUG(591, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(591, *YYCURSOR); + YYDEBUG(592, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1250 "Zend/zend_language_scanner.l" +#line 1254 "Zend/zend_language_scanner.l" { return T_INCLUDE_ONCE; } -#line 5888 "Zend/zend_language_scanner.c" -yy592: - YYDEBUG(592, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'R') goto yy593; - if (yych != 'r') goto yy186; +#line 5896 "Zend/zend_language_scanner.c" yy593: YYDEBUG(593, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'F') goto yy594; - if (yych != 'f') goto yy186; + if (yych == 'R') goto yy594; + if (yych != 'r') goto yy187; yy594: YYDEBUG(594, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'A') goto yy595; - if (yych != 'a') goto yy186; + if (yych == 'F') goto yy595; + if (yych != 'f') goto yy187; yy595: YYDEBUG(595, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'C') goto yy596; - if (yych != 'c') goto yy186; + if (yych == 'A') goto yy596; + if (yych != 'a') goto yy187; yy596: YYDEBUG(596, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy597; - if (yych != 'e') goto yy186; + if (yych == 'C') goto yy597; + if (yych != 'c') goto yy187; yy597: YYDEBUG(597, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'E') goto yy598; + if (yych != 'e') goto yy187; +yy598: + YYDEBUG(598, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(598, *YYCURSOR); + YYDEBUG(599, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1148 "Zend/zend_language_scanner.l" +#line 1152 "Zend/zend_language_scanner.l" { return T_INTERFACE; } -#line 5926 "Zend/zend_language_scanner.c" -yy599: - YYDEBUG(599, *YYCURSOR); +#line 5934 "Zend/zend_language_scanner.c" +yy600: + YYDEBUG(600, *YYCURSOR); yych = *++YYCURSOR; if (yych <= 'E') { - if (yych == 'A') goto yy600; - if (yych <= 'D') goto yy186; - goto yy601; + if (yych == 'A') goto yy601; + if (yych <= 'D') goto yy187; + goto yy602; } else { if (yych <= 'a') { - if (yych <= '`') goto yy186; + if (yych <= '`') goto yy187; } else { - if (yych == 'e') goto yy601; - goto yy186; + if (yych == 'e') goto yy602; + goto yy187; } } -yy600: - YYDEBUG(600, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'N') goto yy607; - if (yych == 'n') goto yy607; - goto yy186; yy601: YYDEBUG(601, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'A') goto yy602; - if (yych != 'a') goto yy186; + if (yych == 'N') goto yy608; + if (yych == 'n') goto yy608; + goto yy187; yy602: YYDEBUG(602, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'D') goto yy603; - if (yych != 'd') goto yy186; + if (yych == 'A') goto yy603; + if (yych != 'a') goto yy187; yy603: YYDEBUG(603, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'O') goto yy604; - if (yych != 'o') goto yy186; + if (yych == 'D') goto yy604; + if (yych != 'd') goto yy187; yy604: YYDEBUG(604, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'F') goto yy605; - if (yych != 'f') goto yy186; + if (yych == 'O') goto yy605; + if (yych != 'o') goto yy187; yy605: YYDEBUG(605, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'F') goto yy606; + if (yych != 'f') goto yy187; +yy606: + YYDEBUG(606, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(606, *YYCURSOR); + YYDEBUG(607, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1270 "Zend/zend_language_scanner.l" +#line 1274 "Zend/zend_language_scanner.l" { return T_INSTEADOF; } -#line 5980 "Zend/zend_language_scanner.c" -yy607: - YYDEBUG(607, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'C') goto yy608; - if (yych != 'c') goto yy186; +#line 5988 "Zend/zend_language_scanner.c" yy608: YYDEBUG(608, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy609; - if (yych != 'e') goto yy186; + if (yych == 'C') goto yy609; + if (yych != 'c') goto yy187; yy609: YYDEBUG(609, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'O') goto yy610; - if (yych != 'o') goto yy186; + if (yych == 'E') goto yy610; + if (yych != 'e') goto yy187; yy610: YYDEBUG(610, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'F') goto yy611; - if (yych != 'f') goto yy186; + if (yych == 'O') goto yy611; + if (yych != 'o') goto yy187; yy611: YYDEBUG(611, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'F') goto yy612; + if (yych != 'f') goto yy187; +yy612: + YYDEBUG(612, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(612, *YYCURSOR); + YYDEBUG(613, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1100 "Zend/zend_language_scanner.l" +#line 1104 "Zend/zend_language_scanner.l" { return T_INSTANCEOF; } -#line 6013 "Zend/zend_language_scanner.c" -yy613: - YYDEBUG(613, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'L') goto yy614; - if (yych != 'l') goto yy186; +#line 6021 "Zend/zend_language_scanner.c" yy614: YYDEBUG(614, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy615; - if (yych != 'e') goto yy186; + if (yych == 'L') goto yy615; + if (yych != 'l') goto yy187; yy615: YYDEBUG(615, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'M') goto yy616; - if (yych != 'm') goto yy186; + if (yych == 'E') goto yy616; + if (yych != 'e') goto yy187; yy616: YYDEBUG(616, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy617; - if (yych != 'e') goto yy186; + if (yych == 'M') goto yy617; + if (yych != 'm') goto yy187; yy617: YYDEBUG(617, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'N') goto yy618; - if (yych != 'n') goto yy186; + if (yych == 'E') goto yy618; + if (yych != 'e') goto yy187; yy618: YYDEBUG(618, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy619; - if (yych != 't') goto yy186; + if (yych == 'N') goto yy619; + if (yych != 'n') goto yy187; yy619: YYDEBUG(619, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'S') goto yy620; - if (yych != 's') goto yy186; + if (yych == 'T') goto yy620; + if (yych != 't') goto yy187; yy620: YYDEBUG(620, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'S') goto yy621; + if (yych != 's') goto yy187; +yy621: + YYDEBUG(621, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(621, *YYCURSOR); + YYDEBUG(622, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1160 "Zend/zend_language_scanner.l" +#line 1164 "Zend/zend_language_scanner.l" { return T_IMPLEMENTS; } -#line 6061 "Zend/zend_language_scanner.c" -yy622: - YYDEBUG(622, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'R') goto yy630; - if (yych == 'r') goto yy630; - goto yy186; +#line 6069 "Zend/zend_language_scanner.c" yy623: YYDEBUG(623, *YYCURSOR); yych = *++YYCURSOR; + if (yych == 'R') goto yy631; + if (yych == 'r') goto yy631; + goto yy187; +yy624: + YYDEBUG(624, *YYCURSOR); + yych = *++YYCURSOR; if (yych <= 'Y') { - if (yych == 'A') goto yy626; - if (yych <= 'X') goto yy186; + if (yych == 'A') goto yy627; + if (yych <= 'X') goto yy187; } else { if (yych <= 'a') { - if (yych <= '`') goto yy186; - goto yy626; + if (yych <= '`') goto yy187; + goto yy627; } else { - if (yych != 'y') goto yy186; + if (yych != 'y') goto yy187; } } - YYDEBUG(624, *YYCURSOR); + YYDEBUG(625, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(625, *YYCURSOR); + YYDEBUG(626, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1036 "Zend/zend_language_scanner.l" +#line 1040 "Zend/zend_language_scanner.l" { return T_TRY; } -#line 6093 "Zend/zend_language_scanner.c" -yy626: - YYDEBUG(626, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'I') goto yy627; - if (yych != 'i') goto yy186; +#line 6101 "Zend/zend_language_scanner.c" yy627: YYDEBUG(627, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy628; - if (yych != 't') goto yy186; + if (yych == 'I') goto yy628; + if (yych != 'i') goto yy187; yy628: YYDEBUG(628, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'T') goto yy629; + if (yych != 't') goto yy187; +yy629: + YYDEBUG(629, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(629, *YYCURSOR); + YYDEBUG(630, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1152 "Zend/zend_language_scanner.l" +#line 1156 "Zend/zend_language_scanner.l" { return T_TRAIT; } -#line 6116 "Zend/zend_language_scanner.c" -yy630: - YYDEBUG(630, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'O') goto yy631; - if (yych != 'o') goto yy186; +#line 6124 "Zend/zend_language_scanner.c" yy631: YYDEBUG(631, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'W') goto yy632; - if (yych != 'w') goto yy186; + if (yych == 'O') goto yy632; + if (yych != 'o') goto yy187; yy632: YYDEBUG(632, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'W') goto yy633; + if (yych != 'w') goto yy187; +yy633: + YYDEBUG(633, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(633, *YYCURSOR); + YYDEBUG(634, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1044 "Zend/zend_language_scanner.l" +#line 1048 "Zend/zend_language_scanner.l" { return T_THROW; } -#line 6139 "Zend/zend_language_scanner.c" -yy634: - YYDEBUG(634, *YYCURSOR); - yych = *++YYCURSOR; - if (yych <= 'T') { - if (yych == 'Q') goto yy636; - if (yych <= 'S') goto yy186; - } else { - if (yych <= 'q') { - if (yych <= 'p') goto yy186; - goto yy636; - } else { - if (yych != 't') goto yy186; - } - } +#line 6147 "Zend/zend_language_scanner.c" +yy635: YYDEBUG(635, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'U') goto yy648; - if (yych == 'u') goto yy648; - goto yy186; + if (yych == 'E') goto yy636; + if (yych != 'e') goto yy187; yy636: YYDEBUG(636, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'U') goto yy637; - if (yych != 'u') goto yy186; + if (yych == 'L') goto yy637; + if (yych != 'l') goto yy187; yy637: YYDEBUG(637, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'I') goto yy638; - if (yych != 'i') goto yy186; + if (yych == 'D') goto yy638; + if (yych != 'd') goto yy187; yy638: YYDEBUG(638, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'R') goto yy639; - if (yych != 'r') goto yy186; -yy639: + ++YYCURSOR; + if (yybm[0+(yych = *YYCURSOR)] & 4) { + goto yy186; + } YYDEBUG(639, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'E') goto yy640; - if (yych != 'e') goto yy186; + yyleng = YYCURSOR - SCNG(yy_text); +#line 1036 "Zend/zend_language_scanner.l" + { + return T_YIELD; +} +#line 6175 "Zend/zend_language_scanner.c" yy640: YYDEBUG(640, *YYCURSOR); - ++YYCURSOR; - if ((yych = *YYCURSOR) <= '^') { - if (yych <= '9') { - if (yych >= '0') goto yy185; - } else { - if (yych <= '@') goto yy641; - if (yych <= 'Z') goto yy185; - } + yych = *++YYCURSOR; + if (yych <= 'T') { + if (yych == 'Q') goto yy642; + if (yych <= 'S') goto yy187; } else { - if (yych <= '`') { - if (yych <= '_') goto yy642; + if (yych <= 'q') { + if (yych <= 'p') goto yy187; + goto yy642; } else { - if (yych <= 'z') goto yy185; - if (yych >= 0x7F) goto yy185; + if (yych != 't') goto yy187; } } -yy641: YYDEBUG(641, *YYCURSOR); - yyleng = YYCURSOR - SCNG(yy_text); -#line 1254 "Zend/zend_language_scanner.l" - { - return T_REQUIRE; -} -#line 6204 "Zend/zend_language_scanner.c" + yych = *++YYCURSOR; + if (yych == 'U') goto yy654; + if (yych == 'u') goto yy654; + goto yy187; yy642: YYDEBUG(642, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'O') goto yy643; - if (yych != 'o') goto yy186; + if (yych == 'U') goto yy643; + if (yych != 'u') goto yy187; yy643: YYDEBUG(643, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'N') goto yy644; - if (yych != 'n') goto yy186; + if (yych == 'I') goto yy644; + if (yych != 'i') goto yy187; yy644: YYDEBUG(644, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'C') goto yy645; - if (yych != 'c') goto yy186; + if (yych == 'R') goto yy645; + if (yych != 'r') goto yy187; yy645: YYDEBUG(645, *YYCURSOR); yych = *++YYCURSOR; if (yych == 'E') goto yy646; - if (yych != 'e') goto yy186; + if (yych != 'e') goto yy187; yy646: YYDEBUG(646, *YYCURSOR); ++YYCURSOR; - if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + if ((yych = *YYCURSOR) <= '^') { + if (yych <= '9') { + if (yych >= '0') goto yy186; + } else { + if (yych <= '@') goto yy647; + if (yych <= 'Z') goto yy186; + } + } else { + if (yych <= '`') { + if (yych <= '_') goto yy648; + } else { + if (yych <= 'z') goto yy186; + if (yych >= 0x7F) goto yy186; + } } +yy647: YYDEBUG(647, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); #line 1258 "Zend/zend_language_scanner.l" { - return T_REQUIRE_ONCE; + return T_REQUIRE; } -#line 6237 "Zend/zend_language_scanner.c" +#line 6240 "Zend/zend_language_scanner.c" yy648: YYDEBUG(648, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'R') goto yy649; - if (yych != 'r') goto yy186; + if (yych == 'O') goto yy649; + if (yych != 'o') goto yy187; yy649: YYDEBUG(649, *YYCURSOR); yych = *++YYCURSOR; if (yych == 'N') goto yy650; - if (yych != 'n') goto yy186; + if (yych != 'n') goto yy187; yy650: YYDEBUG(650, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'C') goto yy651; + if (yych != 'c') goto yy187; +yy651: + YYDEBUG(651, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'E') goto yy652; + if (yych != 'e') goto yy187; +yy652: + YYDEBUG(652, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(651, *YYCURSOR); + YYDEBUG(653, *YYCURSOR); + yyleng = YYCURSOR - SCNG(yy_text); +#line 1262 "Zend/zend_language_scanner.l" + { + return T_REQUIRE_ONCE; +} +#line 6273 "Zend/zend_language_scanner.c" +yy654: + YYDEBUG(654, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'R') goto yy655; + if (yych != 'r') goto yy187; +yy655: + YYDEBUG(655, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'N') goto yy656; + if (yych != 'n') goto yy187; +yy656: + YYDEBUG(656, *YYCURSOR); + ++YYCURSOR; + if (yybm[0+(yych = *YYCURSOR)] & 4) { + goto yy186; + } + YYDEBUG(657, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); #line 1032 "Zend/zend_language_scanner.l" { return T_RETURN; } -#line 6260 "Zend/zend_language_scanner.c" -yy652: - YYDEBUG(652, *YYCURSOR); +#line 6296 "Zend/zend_language_scanner.c" +yy658: + YYDEBUG(658, *YYCURSOR); yych = *++YYCURSOR; if (yych <= 'T') { if (yych <= 'L') { - if (yych <= 'K') goto yy186; - goto yy675; + if (yych <= 'K') goto yy187; + goto yy681; } else { - if (yych <= 'R') goto yy186; - if (yych <= 'S') goto yy674; - goto yy673; + if (yych <= 'R') goto yy187; + if (yych <= 'S') goto yy680; + goto yy679; } } else { if (yych <= 'r') { - if (yych == 'l') goto yy675; - goto yy186; + if (yych == 'l') goto yy681; + goto yy187; } else { - if (yych <= 's') goto yy674; - if (yych <= 't') goto yy673; - goto yy186; + if (yych <= 's') goto yy680; + if (yych <= 't') goto yy679; + goto yy187; } } -yy653: - YYDEBUG(653, *YYCURSOR); +yy659: + YYDEBUG(659, *YYCURSOR); yych = *++YYCURSOR; if (yych <= 'O') { - if (yych == 'A') goto yy665; - if (yych <= 'N') goto yy186; - goto yy666; + if (yych == 'A') goto yy671; + if (yych <= 'N') goto yy187; + goto yy672; } else { if (yych <= 'a') { - if (yych <= '`') goto yy186; - goto yy665; + if (yych <= '`') goto yy187; + goto yy671; } else { - if (yych == 'o') goto yy666; - goto yy186; + if (yych == 'o') goto yy672; + goto yy187; } } -yy654: - YYDEBUG(654, *YYCURSOR); +yy660: + YYDEBUG(660, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'N') goto yy655; - if (yych != 'n') goto yy186; -yy655: - YYDEBUG(655, *YYCURSOR); + if (yych == 'N') goto yy661; + if (yych != 'n') goto yy187; +yy661: + YYDEBUG(661, *YYCURSOR); yych = *++YYCURSOR; if (yych <= 'T') { - if (yych <= 'R') goto yy186; - if (yych >= 'T') goto yy657; + if (yych <= 'R') goto yy187; + if (yych >= 'T') goto yy663; } else { - if (yych <= 'r') goto yy186; - if (yych <= 's') goto yy656; - if (yych <= 't') goto yy657; - goto yy186; + if (yych <= 'r') goto yy187; + if (yych <= 's') goto yy662; + if (yych <= 't') goto yy663; + goto yy187; } -yy656: - YYDEBUG(656, *YYCURSOR); +yy662: + YYDEBUG(662, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy663; - if (yych == 't') goto yy663; - goto yy186; -yy657: - YYDEBUG(657, *YYCURSOR); + if (yych == 'T') goto yy669; + if (yych == 't') goto yy669; + goto yy187; +yy663: + YYDEBUG(663, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'I') goto yy658; - if (yych != 'i') goto yy186; -yy658: - YYDEBUG(658, *YYCURSOR); + if (yych == 'I') goto yy664; + if (yych != 'i') goto yy187; +yy664: + YYDEBUG(664, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'N') goto yy659; - if (yych != 'n') goto yy186; -yy659: - YYDEBUG(659, *YYCURSOR); + if (yych == 'N') goto yy665; + if (yych != 'n') goto yy187; +yy665: + YYDEBUG(665, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'U') goto yy660; - if (yych != 'u') goto yy186; -yy660: - YYDEBUG(660, *YYCURSOR); + if (yych == 'U') goto yy666; + if (yych != 'u') goto yy187; +yy666: + YYDEBUG(666, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy661; - if (yych != 'e') goto yy186; -yy661: - YYDEBUG(661, *YYCURSOR); + if (yych == 'E') goto yy667; + if (yych != 'e') goto yy187; +yy667: + YYDEBUG(667, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(662, *YYCURSOR); + YYDEBUG(668, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1128 "Zend/zend_language_scanner.l" +#line 1132 "Zend/zend_language_scanner.l" { return T_CONTINUE; } -#line 6354 "Zend/zend_language_scanner.c" -yy663: - YYDEBUG(663, *YYCURSOR); +#line 6390 "Zend/zend_language_scanner.c" +yy669: + YYDEBUG(669, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(664, *YYCURSOR); + YYDEBUG(670, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); #line 1028 "Zend/zend_language_scanner.l" { return T_CONST; } -#line 6367 "Zend/zend_language_scanner.c" -yy665: - YYDEBUG(665, *YYCURSOR); +#line 6403 "Zend/zend_language_scanner.c" +yy671: + YYDEBUG(671, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'S') goto yy670; - if (yych == 's') goto yy670; - goto yy186; -yy666: - YYDEBUG(666, *YYCURSOR); + if (yych == 'S') goto yy676; + if (yych == 's') goto yy676; + goto yy187; +yy672: + YYDEBUG(672, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'N') goto yy667; - if (yych != 'n') goto yy186; -yy667: - YYDEBUG(667, *YYCURSOR); + if (yych == 'N') goto yy673; + if (yych != 'n') goto yy187; +yy673: + YYDEBUG(673, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy668; - if (yych != 'e') goto yy186; -yy668: - YYDEBUG(668, *YYCURSOR); + if (yych == 'E') goto yy674; + if (yych != 'e') goto yy187; +yy674: + YYDEBUG(674, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(669, *YYCURSOR); + YYDEBUG(675, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1206 "Zend/zend_language_scanner.l" +#line 1210 "Zend/zend_language_scanner.l" { return T_CLONE; } -#line 6396 "Zend/zend_language_scanner.c" -yy670: - YYDEBUG(670, *YYCURSOR); +#line 6432 "Zend/zend_language_scanner.c" +yy676: + YYDEBUG(676, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'S') goto yy671; - if (yych != 's') goto yy186; -yy671: - YYDEBUG(671, *YYCURSOR); + if (yych == 'S') goto yy677; + if (yych != 's') goto yy187; +yy677: + YYDEBUG(677, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(672, *YYCURSOR); + YYDEBUG(678, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1144 "Zend/zend_language_scanner.l" +#line 1148 "Zend/zend_language_scanner.l" { return T_CLASS; } -#line 6414 "Zend/zend_language_scanner.c" -yy673: - YYDEBUG(673, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'C') goto yy684; - if (yych == 'c') goto yy684; - goto yy186; -yy674: - YYDEBUG(674, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'E') goto yy682; - if (yych == 'e') goto yy682; - goto yy186; -yy675: - YYDEBUG(675, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'L') goto yy676; - if (yych != 'l') goto yy186; -yy676: - YYDEBUG(676, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'A') goto yy677; - if (yych != 'a') goto yy186; -yy677: - YYDEBUG(677, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'B') goto yy678; - if (yych != 'b') goto yy186; -yy678: - YYDEBUG(678, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'L') goto yy679; - if (yych != 'l') goto yy186; +#line 6450 "Zend/zend_language_scanner.c" yy679: YYDEBUG(679, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy680; - if (yych != 'e') goto yy186; + if (yych == 'C') goto yy690; + if (yych == 'c') goto yy690; + goto yy187; yy680: YYDEBUG(680, *YYCURSOR); - ++YYCURSOR; - if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; - } + yych = *++YYCURSOR; + if (yych == 'E') goto yy688; + if (yych == 'e') goto yy688; + goto yy187; +yy681: YYDEBUG(681, *YYCURSOR); - yyleng = YYCURSOR - SCNG(yy_text); -#line 1330 "Zend/zend_language_scanner.l" - { - return T_CALLABLE; -} -#line 6464 "Zend/zend_language_scanner.c" + yych = *++YYCURSOR; + if (yych == 'L') goto yy682; + if (yych != 'l') goto yy187; yy682: YYDEBUG(682, *YYCURSOR); - ++YYCURSOR; - if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; - } + yych = *++YYCURSOR; + if (yych == 'A') goto yy683; + if (yych != 'a') goto yy187; +yy683: YYDEBUG(683, *YYCURSOR); - yyleng = YYCURSOR - SCNG(yy_text); -#line 1116 "Zend/zend_language_scanner.l" - { - return T_CASE; -} -#line 6477 "Zend/zend_language_scanner.c" + yych = *++YYCURSOR; + if (yych == 'B') goto yy684; + if (yych != 'b') goto yy187; yy684: YYDEBUG(684, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'H') goto yy685; - if (yych != 'h') goto yy186; + if (yych == 'L') goto yy685; + if (yych != 'l') goto yy187; yy685: YYDEBUG(685, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'E') goto yy686; + if (yych != 'e') goto yy187; +yy686: + YYDEBUG(686, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(686, *YYCURSOR); + YYDEBUG(687, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1040 "Zend/zend_language_scanner.l" +#line 1334 "Zend/zend_language_scanner.l" { - return T_CATCH; + return T_CALLABLE; } -#line 6495 "Zend/zend_language_scanner.c" -yy687: - YYDEBUG(687, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'N') goto yy704; - if (yych == 'n') goto yy704; - goto yy186; +#line 6500 "Zend/zend_language_scanner.c" yy688: YYDEBUG(688, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'R') goto yy697; - if (yych == 'r') goto yy697; - goto yy186; -yy689: + ++YYCURSOR; + if (yybm[0+(yych = *YYCURSOR)] & 4) { + goto yy186; + } YYDEBUG(689, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'N') goto yy690; - if (yych != 'n') goto yy186; + yyleng = YYCURSOR - SCNG(yy_text); +#line 1120 "Zend/zend_language_scanner.l" + { + return T_CASE; +} +#line 6513 "Zend/zend_language_scanner.c" yy690: YYDEBUG(690, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'C') goto yy691; - if (yych != 'c') goto yy186; + if (yych == 'H') goto yy691; + if (yych != 'h') goto yy187; yy691: YYDEBUG(691, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'T') goto yy692; - if (yych != 't') goto yy186; -yy692: + ++YYCURSOR; + if (yybm[0+(yych = *YYCURSOR)] & 4) { + goto yy186; + } YYDEBUG(692, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'I') goto yy693; - if (yych != 'i') goto yy186; + yyleng = YYCURSOR - SCNG(yy_text); +#line 1044 "Zend/zend_language_scanner.l" + { + return T_CATCH; +} +#line 6531 "Zend/zend_language_scanner.c" yy693: YYDEBUG(693, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'O') goto yy694; - if (yych != 'o') goto yy186; + if (yych == 'N') goto yy710; + if (yych == 'n') goto yy710; + goto yy187; yy694: YYDEBUG(694, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'N') goto yy695; - if (yych != 'n') goto yy186; + if (yych == 'R') goto yy703; + if (yych == 'r') goto yy703; + goto yy187; yy695: YYDEBUG(695, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'N') goto yy696; + if (yych != 'n') goto yy187; +yy696: + YYDEBUG(696, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'C') goto yy697; + if (yych != 'c') goto yy187; +yy697: + YYDEBUG(697, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'T') goto yy698; + if (yych != 't') goto yy187; +yy698: + YYDEBUG(698, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'I') goto yy699; + if (yych != 'i') goto yy187; +yy699: + YYDEBUG(699, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'O') goto yy700; + if (yych != 'o') goto yy187; +yy700: + YYDEBUG(700, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'N') goto yy701; + if (yych != 'n') goto yy187; +yy701: + YYDEBUG(701, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(696, *YYCURSOR); + YYDEBUG(702, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); #line 1024 "Zend/zend_language_scanner.l" { return T_FUNCTION; } -#line 6550 "Zend/zend_language_scanner.c" -yy697: - YYDEBUG(697, *YYCURSOR); +#line 6586 "Zend/zend_language_scanner.c" +yy703: + YYDEBUG(703, *YYCURSOR); ++YYCURSOR; if ((yych = *YYCURSOR) <= '^') { if (yych <= '@') { - if (yych <= '/') goto yy698; - if (yych <= '9') goto yy185; + if (yych <= '/') goto yy704; + if (yych <= '9') goto yy186; } else { - if (yych == 'E') goto yy699; - if (yych <= 'Z') goto yy185; + if (yych == 'E') goto yy705; + if (yych <= 'Z') goto yy186; } } else { if (yych <= 'd') { - if (yych != '`') goto yy185; + if (yych != '`') goto yy186; } else { - if (yych <= 'e') goto yy699; - if (yych <= 'z') goto yy185; - if (yych >= 0x7F) goto yy185; + if (yych <= 'e') goto yy705; + if (yych <= 'z') goto yy186; + if (yych >= 0x7F) goto yy186; } } -yy698: - YYDEBUG(698, *YYCURSOR); +yy704: + YYDEBUG(704, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1076 "Zend/zend_language_scanner.l" +#line 1080 "Zend/zend_language_scanner.l" { return T_FOR; } -#line 6578 "Zend/zend_language_scanner.c" -yy699: - YYDEBUG(699, *YYCURSOR); +#line 6614 "Zend/zend_language_scanner.c" +yy705: + YYDEBUG(705, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'A') goto yy700; - if (yych != 'a') goto yy186; -yy700: - YYDEBUG(700, *YYCURSOR); + if (yych == 'A') goto yy706; + if (yych != 'a') goto yy187; +yy706: + YYDEBUG(706, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'C') goto yy701; - if (yych != 'c') goto yy186; -yy701: - YYDEBUG(701, *YYCURSOR); + if (yych == 'C') goto yy707; + if (yych != 'c') goto yy187; +yy707: + YYDEBUG(707, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'H') goto yy702; - if (yych != 'h') goto yy186; -yy702: - YYDEBUG(702, *YYCURSOR); + if (yych == 'H') goto yy708; + if (yych != 'h') goto yy187; +yy708: + YYDEBUG(708, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(703, *YYCURSOR); + YYDEBUG(709, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1084 "Zend/zend_language_scanner.l" +#line 1088 "Zend/zend_language_scanner.l" { return T_FOREACH; } -#line 6606 "Zend/zend_language_scanner.c" -yy704: - YYDEBUG(704, *YYCURSOR); +#line 6642 "Zend/zend_language_scanner.c" +yy710: + YYDEBUG(710, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'A') goto yy705; - if (yych != 'a') goto yy186; -yy705: - YYDEBUG(705, *YYCURSOR); + if (yych == 'A') goto yy711; + if (yych != 'a') goto yy187; +yy711: + YYDEBUG(711, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'L') goto yy706; - if (yych != 'l') goto yy186; -yy706: - YYDEBUG(706, *YYCURSOR); + if (yych == 'L') goto yy712; + if (yych != 'l') goto yy187; +yy712: + YYDEBUG(712, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(707, *YYCURSOR); + YYDEBUG(713, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1298 "Zend/zend_language_scanner.l" +#line 1302 "Zend/zend_language_scanner.l" { return T_FINAL; } -#line 6629 "Zend/zend_language_scanner.c" -yy708: - YYDEBUG(708, *YYCURSOR); +#line 6665 "Zend/zend_language_scanner.c" +yy714: + YYDEBUG(714, *YYCURSOR); yych = *++YYCURSOR; if (yych <= 'F') { - if (yych == 'C') goto yy714; - if (yych <= 'E') goto yy186; - goto yy715; + if (yych == 'C') goto yy720; + if (yych <= 'E') goto yy187; + goto yy721; } else { if (yych <= 'c') { - if (yych <= 'b') goto yy186; - goto yy714; + if (yych <= 'b') goto yy187; + goto yy720; } else { - if (yych == 'f') goto yy715; - goto yy186; + if (yych == 'f') goto yy721; + goto yy187; } } -yy709: - YYDEBUG(709, *YYCURSOR); +yy715: + YYDEBUG(715, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy712; - if (yych == 'e') goto yy712; - goto yy186; -yy710: - YYDEBUG(710, *YYCURSOR); + if (yych == 'E') goto yy718; + if (yych == 'e') goto yy718; + goto yy187; +yy716: + YYDEBUG(716, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(711, *YYCURSOR); + YYDEBUG(717, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1072 "Zend/zend_language_scanner.l" +#line 1076 "Zend/zend_language_scanner.l" { return T_DO; } -#line 6664 "Zend/zend_language_scanner.c" -yy712: - YYDEBUG(712, *YYCURSOR); +#line 6700 "Zend/zend_language_scanner.c" +yy718: + YYDEBUG(718, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(713, *YYCURSOR); + YYDEBUG(719, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); #line 1020 "Zend/zend_language_scanner.l" { return T_EXIT; } -#line 6677 "Zend/zend_language_scanner.c" -yy714: - YYDEBUG(714, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'L') goto yy721; - if (yych == 'l') goto yy721; - goto yy186; -yy715: - YYDEBUG(715, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'A') goto yy716; - if (yych != 'a') goto yy186; -yy716: - YYDEBUG(716, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'U') goto yy717; - if (yych != 'u') goto yy186; -yy717: - YYDEBUG(717, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'L') goto yy718; - if (yych != 'l') goto yy186; -yy718: - YYDEBUG(718, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'T') goto yy719; - if (yych != 't') goto yy186; -yy719: - YYDEBUG(719, *YYCURSOR); - ++YYCURSOR; - if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; - } +#line 6713 "Zend/zend_language_scanner.c" +yy720: YYDEBUG(720, *YYCURSOR); - yyleng = YYCURSOR - SCNG(yy_text); -#line 1120 "Zend/zend_language_scanner.l" - { - return T_DEFAULT; -} -#line 6716 "Zend/zend_language_scanner.c" + yych = *++YYCURSOR; + if (yych == 'L') goto yy727; + if (yych == 'l') goto yy727; + goto yy187; yy721: YYDEBUG(721, *YYCURSOR); yych = *++YYCURSOR; if (yych == 'A') goto yy722; - if (yych != 'a') goto yy186; + if (yych != 'a') goto yy187; yy722: YYDEBUG(722, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'R') goto yy723; - if (yych != 'r') goto yy186; + if (yych == 'U') goto yy723; + if (yych != 'u') goto yy187; yy723: YYDEBUG(723, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy724; - if (yych != 'e') goto yy186; + if (yych == 'L') goto yy724; + if (yych != 'l') goto yy187; yy724: YYDEBUG(724, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'T') goto yy725; + if (yych != 't') goto yy187; +yy725: + YYDEBUG(725, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(725, *YYCURSOR); + YYDEBUG(726, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1092 "Zend/zend_language_scanner.l" +#line 1124 "Zend/zend_language_scanner.l" { - return T_DECLARE; + return T_DEFAULT; } -#line 6744 "Zend/zend_language_scanner.c" -yy726: - YYDEBUG(726, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'H') goto yy788; - if (yych == 'h') goto yy788; - goto yy186; +#line 6752 "Zend/zend_language_scanner.c" yy727: YYDEBUG(727, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'S') goto yy782; - if (yych == 's') goto yy782; - goto yy186; + if (yych == 'A') goto yy728; + if (yych != 'a') goto yy187; yy728: YYDEBUG(728, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'P') goto yy778; - if (yych == 'p') goto yy778; - goto yy186; + if (yych == 'R') goto yy729; + if (yych != 'r') goto yy187; yy729: YYDEBUG(729, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'D') goto yy744; - if (yych == 'd') goto yy744; - goto yy186; + if (yych == 'E') goto yy730; + if (yych != 'e') goto yy187; yy730: YYDEBUG(730, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'A') goto yy741; - if (yych == 'a') goto yy741; - goto yy186; -yy731: - YYDEBUG(731, *YYCURSOR); - yych = *++YYCURSOR; - if (yych <= 'T') { - if (yych == 'I') goto yy732; - if (yych <= 'S') goto yy186; - goto yy733; - } else { - if (yych <= 'i') { - if (yych <= 'h') goto yy186; - } else { - if (yych == 't') goto yy733; - goto yy186; - } + ++YYCURSOR; + if (yybm[0+(yych = *YYCURSOR)] & 4) { + goto yy186; } + YYDEBUG(731, *YYCURSOR); + yyleng = YYCURSOR - SCNG(yy_text); +#line 1096 "Zend/zend_language_scanner.l" + { + return T_DECLARE; +} +#line 6780 "Zend/zend_language_scanner.c" yy732: YYDEBUG(732, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy739; - if (yych == 't') goto yy739; - goto yy186; + if (yych == 'H') goto yy794; + if (yych == 'h') goto yy794; + goto yy187; yy733: YYDEBUG(733, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy734; - if (yych != 'e') goto yy186; + if (yych == 'S') goto yy788; + if (yych == 's') goto yy788; + goto yy187; yy734: YYDEBUG(734, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'N') goto yy735; - if (yych != 'n') goto yy186; + if (yych == 'P') goto yy784; + if (yych == 'p') goto yy784; + goto yy187; yy735: YYDEBUG(735, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'D') goto yy736; - if (yych != 'd') goto yy186; + if (yych == 'D') goto yy750; + if (yych == 'd') goto yy750; + goto yy187; yy736: YYDEBUG(736, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'S') goto yy737; - if (yych != 's') goto yy186; + if (yych == 'A') goto yy747; + if (yych == 'a') goto yy747; + goto yy187; yy737: YYDEBUG(737, *YYCURSOR); + yych = *++YYCURSOR; + if (yych <= 'T') { + if (yych == 'I') goto yy738; + if (yych <= 'S') goto yy187; + goto yy739; + } else { + if (yych <= 'i') { + if (yych <= 'h') goto yy187; + } else { + if (yych == 't') goto yy739; + goto yy187; + } + } +yy738: + YYDEBUG(738, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'T') goto yy745; + if (yych == 't') goto yy745; + goto yy187; +yy739: + YYDEBUG(739, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'E') goto yy740; + if (yych != 'e') goto yy187; +yy740: + YYDEBUG(740, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'N') goto yy741; + if (yych != 'n') goto yy187; +yy741: + YYDEBUG(741, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'D') goto yy742; + if (yych != 'd') goto yy187; +yy742: + YYDEBUG(742, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'S') goto yy743; + if (yych != 's') goto yy187; +yy743: + YYDEBUG(743, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(738, *YYCURSOR); + YYDEBUG(744, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1156 "Zend/zend_language_scanner.l" +#line 1160 "Zend/zend_language_scanner.l" { return T_EXTENDS; } -#line 6828 "Zend/zend_language_scanner.c" -yy739: - YYDEBUG(739, *YYCURSOR); +#line 6864 "Zend/zend_language_scanner.c" +yy745: + YYDEBUG(745, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(740, *YYCURSOR); + YYDEBUG(746, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); #line 1016 "Zend/zend_language_scanner.l" { return T_EXIT; } -#line 6841 "Zend/zend_language_scanner.c" -yy741: - YYDEBUG(741, *YYCURSOR); +#line 6877 "Zend/zend_language_scanner.c" +yy747: + YYDEBUG(747, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'L') goto yy742; - if (yych != 'l') goto yy186; -yy742: - YYDEBUG(742, *YYCURSOR); + if (yych == 'L') goto yy748; + if (yych != 'l') goto yy187; +yy748: + YYDEBUG(748, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(743, *YYCURSOR); + YYDEBUG(749, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1242 "Zend/zend_language_scanner.l" +#line 1246 "Zend/zend_language_scanner.l" { return T_EVAL; } -#line 6859 "Zend/zend_language_scanner.c" -yy744: - YYDEBUG(744, *YYCURSOR); +#line 6895 "Zend/zend_language_scanner.c" +yy750: + YYDEBUG(750, *YYCURSOR); yych = *++YYCURSOR; YYDEBUG(-1, yych); switch (yych) { case 'D': - case 'd': goto yy745; + case 'd': goto yy751; case 'F': - case 'f': goto yy746; + case 'f': goto yy752; case 'I': - case 'i': goto yy747; + case 'i': goto yy753; case 'S': - case 's': goto yy748; + case 's': goto yy754; case 'W': - case 'w': goto yy749; - default: goto yy186; + case 'w': goto yy755; + default: goto yy187; } -yy745: - YYDEBUG(745, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'E') goto yy771; - if (yych == 'e') goto yy771; - goto yy186; -yy746: - YYDEBUG(746, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'O') goto yy763; - if (yych == 'o') goto yy763; - goto yy186; -yy747: - YYDEBUG(747, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'F') goto yy761; - if (yych == 'f') goto yy761; - goto yy186; -yy748: - YYDEBUG(748, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'W') goto yy755; - if (yych == 'w') goto yy755; - goto yy186; -yy749: - YYDEBUG(749, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'H') goto yy750; - if (yych != 'h') goto yy186; -yy750: - YYDEBUG(750, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'I') goto yy751; - if (yych != 'i') goto yy186; yy751: YYDEBUG(751, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'L') goto yy752; - if (yych != 'l') goto yy186; + if (yych == 'E') goto yy777; + if (yych == 'e') goto yy777; + goto yy187; yy752: YYDEBUG(752, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy753; - if (yych != 'e') goto yy186; + if (yych == 'O') goto yy769; + if (yych == 'o') goto yy769; + goto yy187; yy753: YYDEBUG(753, *YYCURSOR); - ++YYCURSOR; - if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; - } + yych = *++YYCURSOR; + if (yych == 'F') goto yy767; + if (yych == 'f') goto yy767; + goto yy187; +yy754: YYDEBUG(754, *YYCURSOR); - yyleng = YYCURSOR - SCNG(yy_text); -#line 1068 "Zend/zend_language_scanner.l" - { - return T_ENDWHILE; -} -#line 6933 "Zend/zend_language_scanner.c" + yych = *++YYCURSOR; + if (yych == 'W') goto yy761; + if (yych == 'w') goto yy761; + goto yy187; yy755: YYDEBUG(755, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'I') goto yy756; - if (yych != 'i') goto yy186; + if (yych == 'H') goto yy756; + if (yych != 'h') goto yy187; yy756: YYDEBUG(756, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy757; - if (yych != 't') goto yy186; + if (yych == 'I') goto yy757; + if (yych != 'i') goto yy187; yy757: YYDEBUG(757, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'C') goto yy758; - if (yych != 'c') goto yy186; + if (yych == 'L') goto yy758; + if (yych != 'l') goto yy187; yy758: YYDEBUG(758, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'H') goto yy759; - if (yych != 'h') goto yy186; + if (yych == 'E') goto yy759; + if (yych != 'e') goto yy187; yy759: YYDEBUG(759, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } YYDEBUG(760, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1112 "Zend/zend_language_scanner.l" +#line 1072 "Zend/zend_language_scanner.l" { - return T_ENDSWITCH; + return T_ENDWHILE; } -#line 6966 "Zend/zend_language_scanner.c" +#line 6969 "Zend/zend_language_scanner.c" yy761: YYDEBUG(761, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'I') goto yy762; + if (yych != 'i') goto yy187; +yy762: + YYDEBUG(762, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'T') goto yy763; + if (yych != 't') goto yy187; +yy763: + YYDEBUG(763, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'C') goto yy764; + if (yych != 'c') goto yy187; +yy764: + YYDEBUG(764, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'H') goto yy765; + if (yych != 'h') goto yy187; +yy765: + YYDEBUG(765, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(762, *YYCURSOR); + YYDEBUG(766, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1056 "Zend/zend_language_scanner.l" +#line 1116 "Zend/zend_language_scanner.l" + { + return T_ENDSWITCH; +} +#line 7002 "Zend/zend_language_scanner.c" +yy767: + YYDEBUG(767, *YYCURSOR); + ++YYCURSOR; + if (yybm[0+(yych = *YYCURSOR)] & 4) { + goto yy186; + } + YYDEBUG(768, *YYCURSOR); + yyleng = YYCURSOR - SCNG(yy_text); +#line 1060 "Zend/zend_language_scanner.l" { return T_ENDIF; } -#line 6979 "Zend/zend_language_scanner.c" -yy763: - YYDEBUG(763, *YYCURSOR); +#line 7015 "Zend/zend_language_scanner.c" +yy769: + YYDEBUG(769, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'R') goto yy764; - if (yych != 'r') goto yy186; -yy764: - YYDEBUG(764, *YYCURSOR); + if (yych == 'R') goto yy770; + if (yych != 'r') goto yy187; +yy770: + YYDEBUG(770, *YYCURSOR); ++YYCURSOR; if ((yych = *YYCURSOR) <= '^') { if (yych <= '@') { - if (yych <= '/') goto yy765; - if (yych <= '9') goto yy185; + if (yych <= '/') goto yy771; + if (yych <= '9') goto yy186; } else { - if (yych == 'E') goto yy766; - if (yych <= 'Z') goto yy185; + if (yych == 'E') goto yy772; + if (yych <= 'Z') goto yy186; } } else { if (yych <= 'd') { - if (yych != '`') goto yy185; + if (yych != '`') goto yy186; } else { - if (yych <= 'e') goto yy766; - if (yych <= 'z') goto yy185; - if (yych >= 0x7F) goto yy185; + if (yych <= 'e') goto yy772; + if (yych <= 'z') goto yy186; + if (yych >= 0x7F) goto yy186; } } -yy765: - YYDEBUG(765, *YYCURSOR); +yy771: + YYDEBUG(771, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1080 "Zend/zend_language_scanner.l" +#line 1084 "Zend/zend_language_scanner.l" { return T_ENDFOR; } -#line 7012 "Zend/zend_language_scanner.c" -yy766: - YYDEBUG(766, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'A') goto yy767; - if (yych != 'a') goto yy186; -yy767: - YYDEBUG(767, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'C') goto yy768; - if (yych != 'c') goto yy186; -yy768: - YYDEBUG(768, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'H') goto yy769; - if (yych != 'h') goto yy186; -yy769: - YYDEBUG(769, *YYCURSOR); - ++YYCURSOR; - if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; - } - YYDEBUG(770, *YYCURSOR); - yyleng = YYCURSOR - SCNG(yy_text); -#line 1088 "Zend/zend_language_scanner.l" - { - return T_ENDFOREACH; -} -#line 7040 "Zend/zend_language_scanner.c" -yy771: - YYDEBUG(771, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'C') goto yy772; - if (yych != 'c') goto yy186; +#line 7048 "Zend/zend_language_scanner.c" yy772: YYDEBUG(772, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'L') goto yy773; - if (yych != 'l') goto yy186; + if (yych == 'A') goto yy773; + if (yych != 'a') goto yy187; yy773: YYDEBUG(773, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'A') goto yy774; - if (yych != 'a') goto yy186; + if (yych == 'C') goto yy774; + if (yych != 'c') goto yy187; yy774: YYDEBUG(774, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'R') goto yy775; - if (yych != 'r') goto yy186; + if (yych == 'H') goto yy775; + if (yych != 'h') goto yy187; yy775: YYDEBUG(775, *YYCURSOR); - yych = *++YYCURSOR; - if (yych == 'E') goto yy776; - if (yych != 'e') goto yy186; -yy776: - YYDEBUG(776, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(777, *YYCURSOR); + YYDEBUG(776, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1096 "Zend/zend_language_scanner.l" +#line 1092 "Zend/zend_language_scanner.l" { - return T_ENDDECLARE; + return T_ENDFOREACH; } -#line 7078 "Zend/zend_language_scanner.c" +#line 7076 "Zend/zend_language_scanner.c" +yy777: + YYDEBUG(777, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'C') goto yy778; + if (yych != 'c') goto yy187; yy778: YYDEBUG(778, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'T') goto yy779; - if (yych != 't') goto yy186; + if (yych == 'L') goto yy779; + if (yych != 'l') goto yy187; yy779: YYDEBUG(779, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'Y') goto yy780; - if (yych != 'y') goto yy186; + if (yych == 'A') goto yy780; + if (yych != 'a') goto yy187; yy780: YYDEBUG(780, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'R') goto yy781; + if (yych != 'r') goto yy187; +yy781: + YYDEBUG(781, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'E') goto yy782; + if (yych != 'e') goto yy187; +yy782: + YYDEBUG(782, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(781, *YYCURSOR); + YYDEBUG(783, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1282 "Zend/zend_language_scanner.l" +#line 1100 "Zend/zend_language_scanner.l" + { + return T_ENDDECLARE; +} +#line 7114 "Zend/zend_language_scanner.c" +yy784: + YYDEBUG(784, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'T') goto yy785; + if (yych != 't') goto yy187; +yy785: + YYDEBUG(785, *YYCURSOR); + yych = *++YYCURSOR; + if (yych == 'Y') goto yy786; + if (yych != 'y') goto yy187; +yy786: + YYDEBUG(786, *YYCURSOR); + ++YYCURSOR; + if (yybm[0+(yych = *YYCURSOR)] & 4) { + goto yy186; + } + YYDEBUG(787, *YYCURSOR); + yyleng = YYCURSOR - SCNG(yy_text); +#line 1286 "Zend/zend_language_scanner.l" { return T_EMPTY; } -#line 7101 "Zend/zend_language_scanner.c" -yy782: - YYDEBUG(782, *YYCURSOR); +#line 7137 "Zend/zend_language_scanner.c" +yy788: + YYDEBUG(788, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'E') goto yy783; - if (yych != 'e') goto yy186; -yy783: - YYDEBUG(783, *YYCURSOR); + if (yych == 'E') goto yy789; + if (yych != 'e') goto yy187; +yy789: + YYDEBUG(789, *YYCURSOR); ++YYCURSOR; if ((yych = *YYCURSOR) <= '^') { if (yych <= '@') { - if (yych <= '/') goto yy784; - if (yych <= '9') goto yy185; + if (yych <= '/') goto yy790; + if (yych <= '9') goto yy186; } else { - if (yych == 'I') goto yy785; - if (yych <= 'Z') goto yy185; + if (yych == 'I') goto yy791; + if (yych <= 'Z') goto yy186; } } else { if (yych <= 'h') { - if (yych != '`') goto yy185; + if (yych != '`') goto yy186; } else { - if (yych <= 'i') goto yy785; - if (yych <= 'z') goto yy185; - if (yych >= 0x7F) goto yy185; + if (yych <= 'i') goto yy791; + if (yych <= 'z') goto yy186; + if (yych >= 0x7F) goto yy186; } } -yy784: - YYDEBUG(784, *YYCURSOR); +yy790: + YYDEBUG(790, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1060 "Zend/zend_language_scanner.l" +#line 1064 "Zend/zend_language_scanner.l" { return T_ELSE; } -#line 7134 "Zend/zend_language_scanner.c" -yy785: - YYDEBUG(785, *YYCURSOR); +#line 7170 "Zend/zend_language_scanner.c" +yy791: + YYDEBUG(791, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'F') goto yy786; - if (yych != 'f') goto yy186; -yy786: - YYDEBUG(786, *YYCURSOR); + if (yych == 'F') goto yy792; + if (yych != 'f') goto yy187; +yy792: + YYDEBUG(792, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(787, *YYCURSOR); + YYDEBUG(793, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1052 "Zend/zend_language_scanner.l" +#line 1056 "Zend/zend_language_scanner.l" { return T_ELSEIF; } -#line 7152 "Zend/zend_language_scanner.c" -yy788: - YYDEBUG(788, *YYCURSOR); +#line 7188 "Zend/zend_language_scanner.c" +yy794: + YYDEBUG(794, *YYCURSOR); yych = *++YYCURSOR; - if (yych == 'O') goto yy789; - if (yych != 'o') goto yy186; -yy789: - YYDEBUG(789, *YYCURSOR); + if (yych == 'O') goto yy795; + if (yych != 'o') goto yy187; +yy795: + YYDEBUG(795, *YYCURSOR); ++YYCURSOR; if (yybm[0+(yych = *YYCURSOR)] & 4) { - goto yy185; + goto yy186; } - YYDEBUG(790, *YYCURSOR); + YYDEBUG(796, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1136 "Zend/zend_language_scanner.l" +#line 1140 "Zend/zend_language_scanner.l" { return T_ECHO; } -#line 7170 "Zend/zend_language_scanner.c" +#line 7206 "Zend/zend_language_scanner.c" } /* *********************************** */ yyc_ST_LOOKING_FOR_PROPERTY: @@ -7205,41 +7241,41 @@ int lex_scan(zval *zendlval TSRMLS_DC) 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, }; - YYDEBUG(791, *YYCURSOR); + YYDEBUG(797, *YYCURSOR); YYFILL(2); yych = *YYCURSOR; if (yych <= '-') { if (yych <= '\r') { - if (yych <= 0x08) goto yy799; - if (yych <= '\n') goto yy793; - if (yych <= '\f') goto yy799; + if (yych <= 0x08) goto yy805; + if (yych <= '\n') goto yy799; + if (yych <= '\f') goto yy805; } else { - if (yych == ' ') goto yy793; - if (yych <= ',') goto yy799; - goto yy795; + if (yych == ' ') goto yy799; + if (yych <= ',') goto yy805; + goto yy801; } } else { if (yych <= '_') { - if (yych <= '@') goto yy799; - if (yych <= 'Z') goto yy797; - if (yych <= '^') goto yy799; - goto yy797; + if (yych <= '@') goto yy805; + if (yych <= 'Z') goto yy803; + if (yych <= '^') goto yy805; + goto yy803; } else { - if (yych <= '`') goto yy799; - if (yych <= 'z') goto yy797; - if (yych <= '~') goto yy799; - goto yy797; + if (yych <= '`') goto yy805; + if (yych <= 'z') goto yy803; + if (yych <= '~') goto yy805; + goto yy803; } } -yy793: - YYDEBUG(793, *YYCURSOR); +yy799: + YYDEBUG(799, *YYCURSOR); ++YYCURSOR; yych = *YYCURSOR; - goto yy805; -yy794: - YYDEBUG(794, *YYCURSOR); + goto yy811; +yy800: + YYDEBUG(800, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1169 "Zend/zend_language_scanner.l" +#line 1173 "Zend/zend_language_scanner.l" { zendlval->value.str.val = yytext; /* no copying - intentional */ zendlval->value.str.len = yyleng; @@ -7247,73 +7283,73 @@ int lex_scan(zval *zendlval TSRMLS_DC) HANDLE_NEWLINES(yytext, yyleng); return T_WHITESPACE; } -#line 7251 "Zend/zend_language_scanner.c" -yy795: - YYDEBUG(795, *YYCURSOR); +#line 7287 "Zend/zend_language_scanner.c" +yy801: + YYDEBUG(801, *YYCURSOR); ++YYCURSOR; - if ((yych = *YYCURSOR) == '>') goto yy802; -yy796: - YYDEBUG(796, *YYCURSOR); + if ((yych = *YYCURSOR) == '>') goto yy808; +yy802: + YYDEBUG(802, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1188 "Zend/zend_language_scanner.l" +#line 1192 "Zend/zend_language_scanner.l" { yyless(0); yy_pop_state(TSRMLS_C); goto restart; } -#line 7265 "Zend/zend_language_scanner.c" -yy797: - YYDEBUG(797, *YYCURSOR); +#line 7301 "Zend/zend_language_scanner.c" +yy803: + YYDEBUG(803, *YYCURSOR); ++YYCURSOR; yych = *YYCURSOR; - goto yy801; -yy798: - YYDEBUG(798, *YYCURSOR); + goto yy807; +yy804: + YYDEBUG(804, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1181 "Zend/zend_language_scanner.l" +#line 1185 "Zend/zend_language_scanner.l" { yy_pop_state(TSRMLS_C); zend_copy_value(zendlval, yytext, yyleng); zendlval->type = IS_STRING; return T_STRING; } -#line 7281 "Zend/zend_language_scanner.c" -yy799: - YYDEBUG(799, *YYCURSOR); +#line 7317 "Zend/zend_language_scanner.c" +yy805: + YYDEBUG(805, *YYCURSOR); yych = *++YYCURSOR; - goto yy796; -yy800: - YYDEBUG(800, *YYCURSOR); + goto yy802; +yy806: + YYDEBUG(806, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; -yy801: - YYDEBUG(801, *YYCURSOR); +yy807: + YYDEBUG(807, *YYCURSOR); if (yybm[0+yych] & 64) { - goto yy800; + goto yy806; } - goto yy798; -yy802: - YYDEBUG(802, *YYCURSOR); + goto yy804; +yy808: + YYDEBUG(808, *YYCURSOR); ++YYCURSOR; - YYDEBUG(803, *YYCURSOR); + YYDEBUG(809, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1177 "Zend/zend_language_scanner.l" +#line 1181 "Zend/zend_language_scanner.l" { return T_OBJECT_OPERATOR; } -#line 7306 "Zend/zend_language_scanner.c" -yy804: - YYDEBUG(804, *YYCURSOR); +#line 7342 "Zend/zend_language_scanner.c" +yy810: + YYDEBUG(810, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; -yy805: - YYDEBUG(805, *YYCURSOR); +yy811: + YYDEBUG(811, *YYCURSOR); if (yybm[0+yych] & 128) { - goto yy804; + goto yy810; } - goto yy794; + goto yy800; } /* *********************************** */ yyc_ST_LOOKING_FOR_VARNAME: @@ -7352,74 +7388,74 @@ int lex_scan(zval *zendlval TSRMLS_DC) 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, }; - YYDEBUG(806, *YYCURSOR); + YYDEBUG(812, *YYCURSOR); YYFILL(2); yych = *YYCURSOR; if (yych <= '_') { - if (yych <= '@') goto yy810; - if (yych <= 'Z') goto yy808; - if (yych <= '^') goto yy810; + if (yych <= '@') goto yy816; + if (yych <= 'Z') goto yy814; + if (yych <= '^') goto yy816; } else { - if (yych <= '`') goto yy810; - if (yych <= 'z') goto yy808; - if (yych <= '~') goto yy810; + if (yych <= '`') goto yy816; + if (yych <= 'z') goto yy814; + if (yych <= '~') goto yy816; } -yy808: - YYDEBUG(808, *YYCURSOR); +yy814: + YYDEBUG(814, *YYCURSOR); yyaccept = 0; yych = *(YYMARKER = ++YYCURSOR); if (yych <= '_') { if (yych <= '@') { - if (yych <= '/') goto yy809; - if (yych <= '9') goto yy812; + if (yych <= '/') goto yy815; + if (yych <= '9') goto yy818; } else { - if (yych <= '[') goto yy812; - if (yych >= '_') goto yy812; + if (yych <= '[') goto yy818; + if (yych >= '_') goto yy818; } } else { if (yych <= '|') { - if (yych <= '`') goto yy809; - if (yych <= 'z') goto yy812; + if (yych <= '`') goto yy815; + if (yych <= 'z') goto yy818; } else { - if (yych != '~') goto yy812; + if (yych != '~') goto yy818; } } -yy809: - YYDEBUG(809, *YYCURSOR); +yy815: + YYDEBUG(815, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1474 "Zend/zend_language_scanner.l" +#line 1478 "Zend/zend_language_scanner.l" { yyless(0); yy_pop_state(TSRMLS_C); yy_push_state(ST_IN_SCRIPTING TSRMLS_CC); goto restart; } -#line 7398 "Zend/zend_language_scanner.c" -yy810: - YYDEBUG(810, *YYCURSOR); +#line 7434 "Zend/zend_language_scanner.c" +yy816: + YYDEBUG(816, *YYCURSOR); yych = *++YYCURSOR; - goto yy809; -yy811: - YYDEBUG(811, *YYCURSOR); + goto yy815; +yy817: + YYDEBUG(817, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; -yy812: - YYDEBUG(812, *YYCURSOR); +yy818: + YYDEBUG(818, *YYCURSOR); if (yybm[0+yych] & 128) { - goto yy811; + goto yy817; } - if (yych == '[') goto yy814; - if (yych == '}') goto yy814; - YYDEBUG(813, *YYCURSOR); + if (yych == '[') goto yy820; + if (yych == '}') goto yy820; + YYDEBUG(819, *YYCURSOR); YYCURSOR = YYMARKER; - goto yy809; -yy814: - YYDEBUG(814, *YYCURSOR); + goto yy815; +yy820: + YYDEBUG(820, *YYCURSOR); ++YYCURSOR; - YYDEBUG(815, *YYCURSOR); + YYDEBUG(821, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1464 "Zend/zend_language_scanner.l" +#line 1468 "Zend/zend_language_scanner.l" { yyless(yyleng - 1); zend_copy_value(zendlval, yytext, yyleng); @@ -7428,18 +7464,18 @@ int lex_scan(zval *zendlval TSRMLS_DC) yy_push_state(ST_IN_SCRIPTING TSRMLS_CC); return T_STRING_VARNAME; } -#line 7432 "Zend/zend_language_scanner.c" +#line 7468 "Zend/zend_language_scanner.c" } /* *********************************** */ yyc_ST_NOWDOC: - YYDEBUG(816, *YYCURSOR); + YYDEBUG(822, *YYCURSOR); YYFILL(1); yych = *YYCURSOR; - YYDEBUG(818, *YYCURSOR); + YYDEBUG(824, *YYCURSOR); ++YYCURSOR; - YYDEBUG(819, *YYCURSOR); + YYDEBUG(825, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2356 "Zend/zend_language_scanner.l" +#line 2360 "Zend/zend_language_scanner.l" { int newline = 0; @@ -7494,7 +7530,7 @@ int lex_scan(zval *zendlval TSRMLS_DC) HANDLE_NEWLINES(yytext, yyleng - newline); return T_ENCAPSED_AND_WHITESPACE; } -#line 7498 "Zend/zend_language_scanner.c" +#line 7534 "Zend/zend_language_scanner.c" /* *********************************** */ yyc_ST_VAR_OFFSET: { @@ -7532,76 +7568,76 @@ int lex_scan(zval *zendlval TSRMLS_DC) 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, }; - YYDEBUG(820, *YYCURSOR); + YYDEBUG(826, *YYCURSOR); YYFILL(3); yych = *YYCURSOR; if (yych <= '/') { if (yych <= ' ') { if (yych <= '\f') { - if (yych <= 0x08) goto yy834; - if (yych <= '\n') goto yy830; - goto yy834; + if (yych <= 0x08) goto yy840; + if (yych <= '\n') goto yy836; + goto yy840; } else { - if (yych <= '\r') goto yy830; - if (yych <= 0x1F) goto yy834; - goto yy830; + if (yych <= '\r') goto yy836; + if (yych <= 0x1F) goto yy840; + goto yy836; } } else { if (yych <= '$') { - if (yych <= '"') goto yy829; - if (yych <= '#') goto yy830; - goto yy825; + if (yych <= '"') goto yy835; + if (yych <= '#') goto yy836; + goto yy831; } else { - if (yych == '\'') goto yy830; - goto yy829; + if (yych == '\'') goto yy836; + goto yy835; } } } else { if (yych <= '\\') { if (yych <= '@') { - if (yych <= '0') goto yy822; - if (yych <= '9') goto yy824; - goto yy829; + if (yych <= '0') goto yy828; + if (yych <= '9') goto yy830; + goto yy835; } else { - if (yych <= 'Z') goto yy832; - if (yych <= '[') goto yy829; - goto yy830; + if (yych <= 'Z') goto yy838; + if (yych <= '[') goto yy835; + goto yy836; } } else { if (yych <= '_') { - if (yych <= ']') goto yy827; - if (yych <= '^') goto yy829; - goto yy832; + if (yych <= ']') goto yy833; + if (yych <= '^') goto yy835; + goto yy838; } else { - if (yych <= '`') goto yy829; - if (yych <= 'z') goto yy832; - if (yych <= '~') goto yy829; - goto yy832; + if (yych <= '`') goto yy835; + if (yych <= 'z') goto yy838; + if (yych <= '~') goto yy835; + goto yy838; } } } -yy822: - YYDEBUG(822, *YYCURSOR); +yy828: + YYDEBUG(828, *YYCURSOR); yyaccept = 0; yych = *(YYMARKER = ++YYCURSOR); if (yych <= 'W') { if (yych <= '9') { - if (yych >= '0') goto yy846; + if (yych >= '0') goto yy852; } else { - if (yych == 'B') goto yy843; + if (yych == 'B') goto yy849; } } else { if (yych <= 'b') { - if (yych <= 'X') goto yy845; - if (yych >= 'b') goto yy843; + if (yych <= 'X') goto yy851; + if (yych >= 'b') goto yy849; } else { - if (yych == 'x') goto yy845; + if (yych == 'x') goto yy851; } } -yy823: - YYDEBUG(823, *YYCURSOR); +yy829: + YYDEBUG(829, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1552 "Zend/zend_language_scanner.l" +#line 1556 "Zend/zend_language_scanner.l" { /* Offset could be treated as a long */ if (yyleng < MAX_LENGTH_OF_LONG - 1 || (yyleng == MAX_LENGTH_OF_LONG - 1 && strcmp(yytext, long_min_digits) < 0)) { zendlval->value.lval = strtol(yytext, NULL, 10); @@ -7613,81 +7649,81 @@ int lex_scan(zval *zendlval TSRMLS_DC) } return T_NUM_STRING; } -#line 7617 "Zend/zend_language_scanner.c" -yy824: - YYDEBUG(824, *YYCURSOR); +#line 7653 "Zend/zend_language_scanner.c" +yy830: + YYDEBUG(830, *YYCURSOR); yych = *++YYCURSOR; - goto yy842; -yy825: - YYDEBUG(825, *YYCURSOR); + goto yy848; +yy831: + YYDEBUG(831, *YYCURSOR); ++YYCURSOR; if ((yych = *YYCURSOR) <= '_') { - if (yych <= '@') goto yy826; - if (yych <= 'Z') goto yy838; - if (yych >= '_') goto yy838; + if (yych <= '@') goto yy832; + if (yych <= 'Z') goto yy844; + if (yych >= '_') goto yy844; } else { - if (yych <= '`') goto yy826; - if (yych <= 'z') goto yy838; - if (yych >= 0x7F) goto yy838; + if (yych <= '`') goto yy832; + if (yych <= 'z') goto yy844; + if (yych >= 0x7F) goto yy844; } -yy826: - YYDEBUG(826, *YYCURSOR); +yy832: + YYDEBUG(832, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1884 "Zend/zend_language_scanner.l" +#line 1888 "Zend/zend_language_scanner.l" { /* Only '[' can be valid, but returning other tokens will allow a more explicit parse error */ return yytext[0]; } -#line 7642 "Zend/zend_language_scanner.c" -yy827: - YYDEBUG(827, *YYCURSOR); +#line 7678 "Zend/zend_language_scanner.c" +yy833: + YYDEBUG(833, *YYCURSOR); ++YYCURSOR; - YYDEBUG(828, *YYCURSOR); + YYDEBUG(834, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1879 "Zend/zend_language_scanner.l" +#line 1883 "Zend/zend_language_scanner.l" { yy_pop_state(TSRMLS_C); return ']'; } -#line 7653 "Zend/zend_language_scanner.c" -yy829: - YYDEBUG(829, *YYCURSOR); +#line 7689 "Zend/zend_language_scanner.c" +yy835: + YYDEBUG(835, *YYCURSOR); yych = *++YYCURSOR; - goto yy826; -yy830: - YYDEBUG(830, *YYCURSOR); + goto yy832; +yy836: + YYDEBUG(836, *YYCURSOR); ++YYCURSOR; - YYDEBUG(831, *YYCURSOR); + YYDEBUG(837, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1889 "Zend/zend_language_scanner.l" +#line 1893 "Zend/zend_language_scanner.l" { /* Invalid rule to return a more explicit parse error with proper line number */ yyless(0); yy_pop_state(TSRMLS_C); return T_ENCAPSED_AND_WHITESPACE; } -#line 7670 "Zend/zend_language_scanner.c" -yy832: - YYDEBUG(832, *YYCURSOR); +#line 7706 "Zend/zend_language_scanner.c" +yy838: + YYDEBUG(838, *YYCURSOR); ++YYCURSOR; yych = *YYCURSOR; - goto yy837; -yy833: - YYDEBUG(833, *YYCURSOR); + goto yy843; +yy839: + YYDEBUG(839, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1896 "Zend/zend_language_scanner.l" +#line 1900 "Zend/zend_language_scanner.l" { zend_copy_value(zendlval, yytext, yyleng); zendlval->type = IS_STRING; return T_STRING; } -#line 7685 "Zend/zend_language_scanner.c" -yy834: - YYDEBUG(834, *YYCURSOR); +#line 7721 "Zend/zend_language_scanner.c" +yy840: + YYDEBUG(840, *YYCURSOR); ++YYCURSOR; - YYDEBUG(835, *YYCURSOR); + YYDEBUG(841, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 2412 "Zend/zend_language_scanner.l" +#line 2416 "Zend/zend_language_scanner.l" { if (YYCURSOR > YYLIMIT) { return 0; @@ -7696,118 +7732,118 @@ int lex_scan(zval *zendlval TSRMLS_DC) zend_error(E_COMPILE_WARNING,"Unexpected character in input: '%c' (ASCII=%d) state=%d", yytext[0], yytext[0], YYSTATE); goto restart; } -#line 7700 "Zend/zend_language_scanner.c" -yy836: - YYDEBUG(836, *YYCURSOR); +#line 7736 "Zend/zend_language_scanner.c" +yy842: + YYDEBUG(842, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; -yy837: - YYDEBUG(837, *YYCURSOR); +yy843: + YYDEBUG(843, *YYCURSOR); if (yybm[0+yych] & 16) { - goto yy836; + goto yy842; } - goto yy833; -yy838: - YYDEBUG(838, *YYCURSOR); + goto yy839; +yy844: + YYDEBUG(844, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; - YYDEBUG(839, *YYCURSOR); + YYDEBUG(845, *YYCURSOR); if (yych <= '^') { if (yych <= '9') { - if (yych >= '0') goto yy838; + if (yych >= '0') goto yy844; } else { - if (yych <= '@') goto yy840; - if (yych <= 'Z') goto yy838; + if (yych <= '@') goto yy846; + if (yych <= 'Z') goto yy844; } } else { if (yych <= '`') { - if (yych <= '_') goto yy838; + if (yych <= '_') goto yy844; } else { - if (yych <= 'z') goto yy838; - if (yych >= 0x7F) goto yy838; + if (yych <= 'z') goto yy844; + if (yych >= 0x7F) goto yy844; } } -yy840: - YYDEBUG(840, *YYCURSOR); +yy846: + YYDEBUG(846, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1873 "Zend/zend_language_scanner.l" +#line 1877 "Zend/zend_language_scanner.l" { zend_copy_value(zendlval, (yytext+1), (yyleng-1)); zendlval->type = IS_STRING; return T_VARIABLE; } -#line 7742 "Zend/zend_language_scanner.c" -yy841: - YYDEBUG(841, *YYCURSOR); +#line 7778 "Zend/zend_language_scanner.c" +yy847: + YYDEBUG(847, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; -yy842: - YYDEBUG(842, *YYCURSOR); +yy848: + YYDEBUG(848, *YYCURSOR); if (yybm[0+yych] & 32) { - goto yy841; + goto yy847; } - goto yy823; -yy843: - YYDEBUG(843, *YYCURSOR); + goto yy829; +yy849: + YYDEBUG(849, *YYCURSOR); yych = *++YYCURSOR; if (yybm[0+yych] & 128) { - goto yy851; + goto yy857; } -yy844: - YYDEBUG(844, *YYCURSOR); +yy850: + YYDEBUG(850, *YYCURSOR); YYCURSOR = YYMARKER; - goto yy823; -yy845: - YYDEBUG(845, *YYCURSOR); + goto yy829; +yy851: + YYDEBUG(851, *YYCURSOR); yych = *++YYCURSOR; if (yybm[0+yych] & 64) { - goto yy849; + goto yy855; } - goto yy844; -yy846: - YYDEBUG(846, *YYCURSOR); + goto yy850; +yy852: + YYDEBUG(852, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; - YYDEBUG(847, *YYCURSOR); - if (yych <= '/') goto yy848; - if (yych <= '9') goto yy846; -yy848: - YYDEBUG(848, *YYCURSOR); + YYDEBUG(853, *YYCURSOR); + if (yych <= '/') goto yy854; + if (yych <= '9') goto yy852; +yy854: + YYDEBUG(854, *YYCURSOR); yyleng = YYCURSOR - SCNG(yy_text); -#line 1564 "Zend/zend_language_scanner.l" +#line 1568 "Zend/zend_language_scanner.l" { /* Offset must be treated as a string */ zendlval->value.str.val = (char *)estrndup(yytext, yyleng); zendlval->value.str.len = yyleng; zendlval->type = IS_STRING; return T_NUM_STRING; } -#line 7789 "Zend/zend_language_scanner.c" -yy849: - YYDEBUG(849, *YYCURSOR); +#line 7825 "Zend/zend_language_scanner.c" +yy855: + YYDEBUG(855, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; - YYDEBUG(850, *YYCURSOR); + YYDEBUG(856, *YYCURSOR); if (yybm[0+yych] & 64) { - goto yy849; + goto yy855; } - goto yy848; -yy851: - YYDEBUG(851, *YYCURSOR); + goto yy854; +yy857: + YYDEBUG(857, *YYCURSOR); ++YYCURSOR; YYFILL(1); yych = *YYCURSOR; - YYDEBUG(852, *YYCURSOR); + YYDEBUG(858, *YYCURSOR); if (yybm[0+yych] & 128) { - goto yy851; + goto yy857; } - goto yy848; + goto yy854; } } -#line 2421 "Zend/zend_language_scanner.l" +#line 2425 "Zend/zend_language_scanner.l" } diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l index c73f39aedb9f..1b9d44c58719 100644 --- a/Zend/zend_language_scanner.l +++ b/Zend/zend_language_scanner.l @@ -1033,6 +1033,10 @@ NEWLINE ("\r"|"\n"|"\r\n") return T_RETURN; } +"yield" { + return T_YIELD; +} + "try" { return T_TRY; } diff --git a/Zend/zend_language_scanner_defs.h b/Zend/zend_language_scanner_defs.h index 5ef78a9faf30..981c342e19c0 100644 --- a/Zend/zend_language_scanner_defs.h +++ b/Zend/zend_language_scanner_defs.h @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 on Mon Apr 30 15:56:25 2012 */ +/* Generated by re2c 0.13.5 on Tue May 15 13:07:23 2012 */ #line 3 "Zend/zend_language_scanner_defs.h" enum YYCONDTYPE { diff --git a/ext/standard/url_scanner_ex.c b/ext/standard/url_scanner_ex.c index d106d95a36d8..dc33b96b8fe9 100644 --- a/ext/standard/url_scanner_ex.c +++ b/ext/standard/url_scanner_ex.c @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 on Sun Jan 1 10:36:29 2012 */ +/* Generated by re2c 0.13.5 on Tue May 15 13:07:17 2012 */ #line 1 "ext/standard/url_scanner_ex.re" /* +----------------------------------------------------------------------+ From 252f623464e7cf5cb794903ba07d652c9cea9a14 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 19 May 2012 14:18:20 +0200 Subject: [PATCH 02/70] Add flag for generator functions Generator functions have to specify the * (asterix) modifier after the function keyword. If they do so the ZEND_ACC_GENERATOR flag is added to the fn_flags. --- Zend/zend_compile.c | 9 ++++++--- Zend/zend_compile.h | 6 ++++-- Zend/zend_language_parser.y | 18 +++++++++++------- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 2b2b58f9ac80..399a37cc0df9 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1519,7 +1519,7 @@ int zend_do_verify_access_types(const znode *current_access_type, const znode *n } /* }}} */ -void zend_do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int return_reference, znode *fn_flags_znode TSRMLS_DC) /* {{{ */ +void zend_do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int is_generator, int return_reference, znode *fn_flags_znode TSRMLS_DC) /* {{{ */ { zend_op_array op_array; char *name = function_name->u.constant.value.str.val; @@ -1553,6 +1553,9 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n CG(interactive) = orig_interactive; op_array.function_name = name; + if (is_generator) { + op_array.fn_flags |= ZEND_ACC_GENERATOR; + } if (return_reference) { op_array.fn_flags |= ZEND_ACC_RETURN_REFERENCE; } @@ -1751,7 +1754,7 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n } /* }}} */ -void zend_do_begin_lambda_function_declaration(znode *result, znode *function_token, int return_reference, int is_static TSRMLS_DC) /* {{{ */ +void zend_do_begin_lambda_function_declaration(znode *result, znode *function_token, int is_generator, int return_reference, int is_static TSRMLS_DC) /* {{{ */ { znode function_name; zend_op_array *current_op_array = CG(active_op_array); @@ -1761,7 +1764,7 @@ void zend_do_begin_lambda_function_declaration(znode *result, znode *function_to function_name.op_type = IS_CONST; ZVAL_STRINGL(&function_name.u.constant, "{closure}", sizeof("{closure}")-1, 1); - zend_do_begin_function_declaration(function_token, &function_name, 0, return_reference, NULL TSRMLS_CC); + zend_do_begin_function_declaration(function_token, &function_name, 0, is_generator, return_reference, NULL TSRMLS_CC); result->op_type = IS_TMP_VAR; result->u.op.var = get_temporary_variable(current_op_array); diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 8a81a95362e8..375f953d86c1 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -194,6 +194,7 @@ typedef struct _zend_try_catch_element { #define ZEND_ACC_CLOSURE 0x100000 +#define ZEND_ACC_GENERATOR 0x800000 /* function flag for internal user call handlers __call, __callstatic */ #define ZEND_ACC_CALL_VIA_HANDLER 0x200000 @@ -477,7 +478,7 @@ void zend_do_add_string(znode *result, const znode *op1, znode *op2 TSRMLS_DC); void zend_do_add_variable(znode *result, const znode *op1, const znode *op2 TSRMLS_DC); int zend_do_verify_access_types(const znode *current_access_type, const znode *new_modifier); -void zend_do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int return_reference, znode *fn_flags_znode TSRMLS_DC); +void zend_do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int is_generator, int return_reference, znode *fn_flags_znode TSRMLS_DC); void zend_do_end_function_declaration(const znode *function_token TSRMLS_DC); void zend_do_receive_arg(zend_uchar op, znode *varname, const znode *offset, const znode *initialization, znode *class_type, zend_bool pass_by_reference TSRMLS_DC); int zend_do_begin_function_call(znode *function_name, zend_bool check_namespace TSRMLS_DC); @@ -489,9 +490,10 @@ void zend_do_build_full_name(znode *result, znode *prefix, znode *name, int is_c int zend_do_begin_class_member_function_call(znode *class_name, znode *method_name TSRMLS_DC); void zend_do_end_function_call(znode *function_name, znode *result, const znode *argument_list, int is_method, int is_dynamic_fcall TSRMLS_DC); void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC); +void zend_do_yield(znode *expr TSRMLS_DC); void zend_do_handle_exception(TSRMLS_D); -void zend_do_begin_lambda_function_declaration(znode *result, znode *function_token, int return_reference, int is_static TSRMLS_DC); +void zend_do_begin_lambda_function_declaration(znode *result, znode *function_token, int is_generator, int return_reference, int is_static TSRMLS_DC); void zend_do_fetch_lexical_variable(znode *varname, zend_bool is_ref TSRMLS_DC); void zend_do_try(znode *try_token TSRMLS_DC); diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 61198cb10e86..2fcf6740c4a9 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -359,6 +359,10 @@ class_declaration_statement: unticked_class_declaration_statement { DO_TICKS(); } ; +is_generator: + /* empty */ { $$.op_type = 0; } + | '*' { $$.op_type = 1; } +; is_reference: /* empty */ { $$.op_type = ZEND_RETURN_VAL; } @@ -367,7 +371,7 @@ is_reference: unticked_function_declaration_statement: - function is_reference T_STRING { zend_do_begin_function_declaration(&$1, &$3, 0, $2.op_type, NULL TSRMLS_CC); } + function is_generator is_reference T_STRING { zend_do_begin_function_declaration(&$1, &$4, 0, $2.op_type, $3.op_type, NULL TSRMLS_CC); } '(' parameter_list ')' '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); } ; @@ -578,8 +582,8 @@ 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); } + | method_modifiers function is_generator is_reference T_STRING { zend_do_begin_function_declaration(&$2, &$5, 1, $3.op_type, $4.op_type, &$1 TSRMLS_CC); } + '(' parameter_list ')' method_body { zend_do_abstract_method(&$5, &$1, &$10 TSRMLS_CC); zend_do_end_function_declaration(&$2 TSRMLS_CC); } ; trait_use_statement: @@ -795,10 +799,10 @@ expr_without_variable: | combined_scalar { $$ = $1; } | '`' backticks_expr '`' { zend_do_shell_exec(&$$, &$2 TSRMLS_CC); } | T_PRINT expr { zend_do_print(&$$, &$2 TSRMLS_CC); } - | function is_reference '(' { zend_do_begin_lambda_function_declaration(&$$, &$1, $2.op_type, 0 TSRMLS_CC); } - parameter_list ')' lexical_vars '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); $$ = $4; } - | T_STATIC function is_reference '(' { zend_do_begin_lambda_function_declaration(&$$, &$2, $3.op_type, 1 TSRMLS_CC); } - parameter_list ')' lexical_vars '{' inner_statement_list '}' { zend_do_end_function_declaration(&$2 TSRMLS_CC); $$ = $5; } + | function is_generator is_reference { zend_do_begin_lambda_function_declaration(&$$, &$1, $2.op_type, $3.op_type, 0 TSRMLS_CC); } + '(' parameter_list ')' lexical_vars '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); $$ = $4; } + | T_STATIC function is_generator is_reference { zend_do_begin_lambda_function_declaration(&$$, &$2, $3.op_type, $4.op_type, 1 TSRMLS_CC); } + '(' parameter_list ')' lexical_vars '{' inner_statement_list '}' { zend_do_end_function_declaration(&$2 TSRMLS_CC); $$ = $5; } ; combined_scalar_offset: From 9b51a3b96d89dc10d4ea65ceacaad22fa3420ea7 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 19 May 2012 14:21:49 +0200 Subject: [PATCH 03/70] Minor code cleanup The block for the foreach separator was nested unnecessary. This commit simply removes that nesting. --- Zend/zend_compile.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 399a37cc0df9..0d3ea109bff2 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1725,7 +1725,7 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n } { - /* Push a seperator to the switch and foreach stacks */ + /* Push a seperator to the switch stack */ zend_switch_entry switch_entry; switch_entry.cond.op_type = IS_UNUSED; @@ -1733,16 +1733,16 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n switch_entry.control_var = 0; zend_stack_push(&CG(switch_cond_stack), (void *) &switch_entry, sizeof(switch_entry)); + } - { - /* Foreach stack separator */ - zend_op dummy_opline; + { + /* Push a separator to the foreach stack */ + zend_op dummy_opline; - dummy_opline.result_type = IS_UNUSED; - dummy_opline.op1_type = IS_UNUSED; + dummy_opline.result_type = IS_UNUSED; + dummy_opline.op1_type = IS_UNUSED; - zend_stack_push(&CG(foreach_copy_stack), (void *) &dummy_opline, sizeof(zend_op)); - } + zend_stack_push(&CG(foreach_copy_stack), (void *) &dummy_opline, sizeof(zend_op)); } if (CG(doc_comment)) { From fd2a109f86d18b93d29153c2ddfa605651c7df05 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 19 May 2012 18:49:27 +0200 Subject: [PATCH 04/70] Add error if yield is used outside a generator The yield statement can only be used in generator functions, which are marked with an asterix. --- .../generators/yield_in_normal_function_error.phpt | 12 ++++++++++++ .../generators/yield_outside_function_error.phpt | 10 ++++++++++ Zend/zend_compile.c | 6 +++++- 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/generators/yield_in_normal_function_error.phpt create mode 100644 Zend/tests/generators/yield_outside_function_error.phpt diff --git a/Zend/tests/generators/yield_in_normal_function_error.phpt b/Zend/tests/generators/yield_in_normal_function_error.phpt new file mode 100644 index 000000000000..802510d29ccc --- /dev/null +++ b/Zend/tests/generators/yield_in_normal_function_error.phpt @@ -0,0 +1,12 @@ +--TEST-- +Yield cannot be used in normal (non-generator) functions +--FILE-- + +--EXPECTF-- +Fatal error: The "yield" statement can only be used inside a generator function in %s on line %d diff --git a/Zend/tests/generators/yield_outside_function_error.phpt b/Zend/tests/generators/yield_outside_function_error.phpt new file mode 100644 index 000000000000..fd7169d5f2e8 --- /dev/null +++ b/Zend/tests/generators/yield_outside_function_error.phpt @@ -0,0 +1,10 @@ +--TEST-- +Yield cannot be used outside of functions +--FILE-- + +--EXPECTF-- +Fatal error: The "yield" statement can only be used inside a generator function in %s on line %d diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 0d3ea109bff2..d2b3536788e7 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2660,7 +2660,11 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */ void zend_do_yield(znode *expr TSRMLS_DC) /* {{{ */ { - /* do nothing for now */ + if ((CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) == 0) { + zend_error(E_COMPILE_ERROR, "The \"yield\" statement can only be used inside a generator function"); + } + + /* do nothing for now */ } /* }}} */ From e14cfafcbfbe58e0fc3f7b814698a908b0dffca5 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 19 May 2012 20:19:45 +0200 Subject: [PATCH 05/70] Add zend_do_suspend_if_generator calls The execution of generator functions will be suspended right after the arguments were RECVed. This will be done in zend_do_suspend_if_generator. --- Zend/zend_compile.c | 11 +++++++++++ Zend/zend_compile.h | 1 + Zend/zend_language_parser.y | 12 ++++++++---- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index d2b3536788e7..831f9f9d3f09 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2668,6 +2668,17 @@ void zend_do_yield(znode *expr TSRMLS_DC) /* {{{ */ } /* }}} */ +void zend_do_suspend_if_generator(TSRMLS_D) /* {{{ */ +{ + // we only suspend execution if the current function is a generator + if ((CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) == 0) { + return; + } + + /* do nothing for now */ +} +/* }}} */ + static int zend_add_try_element(zend_uint try_op TSRMLS_DC) /* {{{ */ { int try_catch_offset = CG(active_op_array)->last_try_catch++; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 375f953d86c1..5365f9693dbb 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -491,6 +491,7 @@ int zend_do_begin_class_member_function_call(znode *class_name, znode *method_na void zend_do_end_function_call(znode *function_name, znode *result, const znode *argument_list, int is_method, int is_dynamic_fcall TSRMLS_DC); void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC); void zend_do_yield(znode *expr TSRMLS_DC); +void zend_do_suspend_if_generator(TSRMLS_D); void zend_do_handle_exception(TSRMLS_D); void zend_do_begin_lambda_function_declaration(znode *result, znode *function_token, int is_generator, int return_reference, int is_static TSRMLS_DC); diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 2fcf6740c4a9..2045a5e46539 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -372,7 +372,8 @@ is_reference: unticked_function_declaration_statement: function is_generator is_reference T_STRING { zend_do_begin_function_declaration(&$1, &$4, 0, $2.op_type, $3.op_type, NULL TSRMLS_CC); } - '(' parameter_list ')' '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); } + '(' parameter_list ')' { zend_do_suspend_if_generator(TSRMLS_C); } + '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); } ; unticked_class_declaration_statement: @@ -583,7 +584,8 @@ class_statement: | class_constant_declaration ';' | trait_use_statement | method_modifiers function is_generator is_reference T_STRING { zend_do_begin_function_declaration(&$2, &$5, 1, $3.op_type, $4.op_type, &$1 TSRMLS_CC); } - '(' parameter_list ')' method_body { zend_do_abstract_method(&$5, &$1, &$10 TSRMLS_CC); zend_do_end_function_declaration(&$2 TSRMLS_CC); } + '(' parameter_list ')' { zend_do_suspend_if_generator(TSRMLS_C); } + method_body { zend_do_abstract_method(&$5, &$1, &$11 TSRMLS_CC); zend_do_end_function_declaration(&$2 TSRMLS_CC); } ; trait_use_statement: @@ -800,9 +802,11 @@ expr_without_variable: | '`' backticks_expr '`' { zend_do_shell_exec(&$$, &$2 TSRMLS_CC); } | T_PRINT expr { zend_do_print(&$$, &$2 TSRMLS_CC); } | function is_generator is_reference { zend_do_begin_lambda_function_declaration(&$$, &$1, $2.op_type, $3.op_type, 0 TSRMLS_CC); } - '(' parameter_list ')' lexical_vars '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); $$ = $4; } + '(' parameter_list ')' lexical_vars { zend_do_suspend_if_generator(TSRMLS_C); } + '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); $$ = $4; } | T_STATIC function is_generator is_reference { zend_do_begin_lambda_function_declaration(&$$, &$2, $3.op_type, $4.op_type, 1 TSRMLS_CC); } - '(' parameter_list ')' lexical_vars '{' inner_statement_list '}' { zend_do_end_function_declaration(&$2 TSRMLS_CC); $$ = $5; } + '(' parameter_list ')' lexical_vars { zend_do_suspend_if_generator(TSRMLS_C); } + '{' inner_statement_list '}' { zend_do_end_function_declaration(&$2 TSRMLS_CC); $$ = $5; } ; combined_scalar_offset: From 1cec3f12cc719ccde286a3a55f6da1a5bf9ea2e4 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 19 May 2012 23:11:18 +0200 Subject: [PATCH 06/70] Add ZEND_SUSPEND_AND_RETURN_GENERATOR opcode If the function is a generator this opcode will be invoked right after receiving the function arguments. The current implementation is just a dummy. --- Zend/zend_compile.c | 8 +++++++- Zend/zend_vm_def.h | 7 +++++++ Zend/zend_vm_execute.h | 32 ++++++++++++++++++++++++++++++++ Zend/zend_vm_opcodes.h | 1 + 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 831f9f9d3f09..f0802efd587b 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2670,12 +2670,18 @@ void zend_do_yield(znode *expr TSRMLS_DC) /* {{{ */ void zend_do_suspend_if_generator(TSRMLS_D) /* {{{ */ { + zend_op *opline; + // we only suspend execution if the current function is a generator if ((CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) == 0) { return; } - /* do nothing for now */ + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + + opline->opcode = ZEND_SUSPEND_AND_RETURN_GENERATOR; + SET_UNUSED(opline->op1); + SET_UNUSED(opline->op2); } /* }}} */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 7b13b440faac..9dc7e0842cf2 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5195,4 +5195,11 @@ ZEND_VM_HANDLER(156, ZEND_SEPARATE, VAR, UNUSED) ZEND_VM_NEXT_OPCODE(); } +ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) +{ + /* do nothing for now */ + + ZEND_VM_NEXT_OPCODE(); +} + ZEND_VM_EXPORT_HELPER(zend_do_fcall, zend_do_fcall_common_helper) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 2096c44e6699..c6c3af65db6c 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1152,6 +1152,13 @@ static int ZEND_FASTCALL ZEND_USER_OPCODE_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS } } +static int ZEND_FASTCALL ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + /* do nothing for now */ + + ZEND_VM_NEXT_OPCODE(); +} + static int ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -40782,6 +40789,31 @@ void zend_init_opcodes_handlers(void) ZEND_JMP_SET_VAR_SPEC_CV_HANDLER, ZEND_JMP_SET_VAR_SPEC_CV_HANDLER, ZEND_JMP_SET_VAR_SPEC_CV_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, ZEND_NULL_HANDLER }; zend_opcode_handlers = (opcode_handler_t*)labels; diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 680778c2a287..9e24dee11d6e 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -159,3 +159,4 @@ #define ZEND_SEPARATE 156 #define ZEND_QM_ASSIGN_VAR 157 #define ZEND_JMP_SET_VAR 158 +#define ZEND_SUSPEND_AND_RETURN_GENERATOR 159 From ca59e5464de78dae1f7c874a6bcc4f7ea2948b1d Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 20 May 2012 00:03:27 +0200 Subject: [PATCH 07/70] Add empty Generator class --- Zend/Makefile.am | 3 ++- Zend/Zend.dsp | 4 ++++ Zend/ZendTS.dsp | 4 ++++ Zend/zend_default_classes.c | 2 ++ Zend/zend_generators.c | 47 +++++++++++++++++++++++++++++++++++++ Zend/zend_generators.h | 40 +++++++++++++++++++++++++++++++ configure.in | 2 +- win32/build/config.w32 | 2 +- 8 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 Zend/zend_generators.c create mode 100644 Zend/zend_generators.h diff --git a/Zend/Makefile.am b/Zend/Makefile.am index 5ec4590fefe1..e5757fac56d0 100644 --- a/Zend/Makefile.am +++ b/Zend/Makefile.am @@ -17,7 +17,8 @@ libZend_la_SOURCES=\ zend_objects_API.c zend_ts_hash.c zend_stream.c \ zend_default_classes.c \ zend_iterators.c zend_interfaces.c zend_exceptions.c \ - zend_strtod.c zend_closures.c zend_float.c zend_string.c zend_signal.c + zend_strtod.c zend_closures.c zend_float.c zend_string.c zend_signal.c \ + zend_generators.c libZend_la_LDFLAGS = libZend_la_LIBADD = @ZEND_EXTRA_LIBS@ diff --git a/Zend/Zend.dsp b/Zend/Zend.dsp index ebe01978c4e1..23ebd4532b5e 100644 --- a/Zend/Zend.dsp +++ b/Zend/Zend.dsp @@ -159,6 +159,10 @@ SOURCE=.\zend_float.c # End Source File # Begin Source File +SOURCE=.\zend_generators.c +# End Source File +# Begin Source File + SOURCE=.\zend_hash.c # End Source File # Begin Source File diff --git a/Zend/ZendTS.dsp b/Zend/ZendTS.dsp index 3494cd4e17ff..3be2c58bed6f 100644 --- a/Zend/ZendTS.dsp +++ b/Zend/ZendTS.dsp @@ -185,6 +185,10 @@ SOURCE=.\zend_extensions.c # End Source File # Begin Source File +SOURCE=.\zend_generators.c +# End Source File +# Begin Source File + SOURCE=.\zend_hash.c # End Source File # Begin Source File diff --git a/Zend/zend_default_classes.c b/Zend/zend_default_classes.c index 73a765811e85..bcc43ea7db14 100644 --- a/Zend/zend_default_classes.c +++ b/Zend/zend_default_classes.c @@ -25,6 +25,7 @@ #include "zend_interfaces.h" #include "zend_exceptions.h" #include "zend_closures.h" +#include "zend_generators.h" ZEND_API void zend_register_default_classes(TSRMLS_D) @@ -33,6 +34,7 @@ ZEND_API void zend_register_default_classes(TSRMLS_D) zend_register_default_exception(TSRMLS_C); zend_register_iterator_wrapper(TSRMLS_C); zend_register_closure_ce(TSRMLS_C); + zend_register_generator_ce(TSRMLS_C); } /* diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c new file mode 100644 index 000000000000..6f98dcb2c1c9 --- /dev/null +++ b/Zend/zend_generators.c @@ -0,0 +1,47 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2012 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Nikita Popov | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#include "zend.h" +#include "zend_API.h" +#include "zend_generators.h" + +ZEND_API zend_class_entry *zend_ce_generator; + +static const zend_function_entry generator_functions[] = { + ZEND_FE_END +}; + +void zend_register_generator_ce(TSRMLS_D) /* {{{ */ +{ + zend_class_entry ce; + + INIT_CLASS_ENTRY(ce, "Generator", generator_functions); + zend_ce_generator = zend_register_internal_class(&ce TSRMLS_CC); + zend_ce_generator->ce_flags |= ZEND_ACC_FINAL_CLASS; +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + */ diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h new file mode 100644 index 000000000000..60899072dffa --- /dev/null +++ b/Zend/zend_generators.h @@ -0,0 +1,40 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) 1998-2012 Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Nikita Popov | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef ZEND_GENERATORS_H +#define ZEND_GENERATORS_H + +BEGIN_EXTERN_C() + +void zend_register_generator_ce(TSRMLS_D); + +extern ZEND_API zend_class_entry *zend_ce_generator; + +END_EXTERN_C() + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * indent-tabs-mode: t + * End: + */ diff --git a/configure.in b/configure.in index 96febdd9000e..e9a0ef5069a0 100644 --- a/configure.in +++ b/configure.in @@ -1472,7 +1472,7 @@ PHP_ADD_SOURCES(Zend, \ zend_list.c zend_indent.c zend_builtin_functions.c zend_sprintf.c \ zend_ini.c zend_qsort.c zend_multibyte.c zend_ts_hash.c zend_stream.c \ zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c zend_gc.c \ - zend_closures.c zend_float.c zend_string.c zend_signal.c) + zend_closures.c zend_float.c zend_string.c zend_signal.c zend_generators.c) if test -r "$abs_srcdir/Zend/zend_objects.c"; then PHP_ADD_SOURCES(Zend, zend_objects.c zend_object_handlers.c zend_objects_API.c zend_default_classes.c) diff --git a/win32/build/config.w32 b/win32/build/config.w32 index 1a4b834be425..fae3cc66c302 100644 --- a/win32/build/config.w32 +++ b/win32/build/config.w32 @@ -359,7 +359,7 @@ ADD_SOURCES("Zend", "zend_language_parser.c zend_language_scanner.c \ zend_stream.c zend_iterators.c zend_interfaces.c zend_objects.c \ zend_object_handlers.c zend_objects_API.c \ zend_default_classes.c zend_execute.c zend_strtod.c zend_gc.c zend_closures.c \ - zend_float.c zend_string.c"); + zend_float.c zend_string.c zend_generators.c"); if (VCVERS == 1200) { AC_DEFINE('ZEND_DVAL_TO_LVAL_CAST_OK', 1); From 40b7533576b4f2ec4ba872d027179d92db71d844 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 20 May 2012 14:19:16 +0200 Subject: [PATCH 08/70] Add some boilerplate code for Generator class The Generator class now uses a zend_generator struct, so it'll be able to store additional info. This commit also ensures that Generator cannot be directly instantiated and extended. The error tests are now in a separate folder from the (yet-to-come) functional tests. --- .../errors/generator_extend_error.phpt | 10 ++++ .../errors/generator_instantiate_error.phpt | 10 ++++ .../yield_in_normal_function_error.phpt | 0 .../yield_outside_function_error.phpt | 0 Zend/zend_generators.c | 47 +++++++++++++++++++ 5 files changed, 67 insertions(+) create mode 100644 Zend/tests/generators/errors/generator_extend_error.phpt create mode 100644 Zend/tests/generators/errors/generator_instantiate_error.phpt rename Zend/tests/generators/{ => errors}/yield_in_normal_function_error.phpt (100%) rename Zend/tests/generators/{ => errors}/yield_outside_function_error.phpt (100%) diff --git a/Zend/tests/generators/errors/generator_extend_error.phpt b/Zend/tests/generators/errors/generator_extend_error.phpt new file mode 100644 index 000000000000..550f16ae03b9 --- /dev/null +++ b/Zend/tests/generators/errors/generator_extend_error.phpt @@ -0,0 +1,10 @@ +--TEST-- +The Generator class cannot be extended +--FILE-- + +--EXPECTF-- +Fatal error: Class ExtendedGenerator may not inherit from final class (Generator) in %s on line %d diff --git a/Zend/tests/generators/errors/generator_instantiate_error.phpt b/Zend/tests/generators/errors/generator_instantiate_error.phpt new file mode 100644 index 000000000000..f8941c087af4 --- /dev/null +++ b/Zend/tests/generators/errors/generator_instantiate_error.phpt @@ -0,0 +1,10 @@ +--TEST-- +It's not possible to directly instantiate the Generator class +--FILE-- + +--EXPECTF-- +Catchable fatal error: The "Generator" class is reserved for internal use and cannot be manually instantiated in %s on line %d diff --git a/Zend/tests/generators/yield_in_normal_function_error.phpt b/Zend/tests/generators/errors/yield_in_normal_function_error.phpt similarity index 100% rename from Zend/tests/generators/yield_in_normal_function_error.phpt rename to Zend/tests/generators/errors/yield_in_normal_function_error.phpt diff --git a/Zend/tests/generators/yield_outside_function_error.phpt b/Zend/tests/generators/errors/yield_outside_function_error.phpt similarity index 100% rename from Zend/tests/generators/yield_outside_function_error.phpt rename to Zend/tests/generators/errors/yield_outside_function_error.phpt diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 6f98dcb2c1c9..21581db2c038 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -23,6 +23,49 @@ #include "zend_generators.h" ZEND_API zend_class_entry *zend_ce_generator; +static zend_object_handlers zend_generator_handlers; + +typedef struct _zend_generator { + zend_object std; + /* nothing more for now */ +} zend_generator; + +static void zend_generator_free_storage(zend_generator *generator TSRMLS_DC) /* {{{ */ +{ + zend_object_std_dtor(&generator->std TSRMLS_CC); + + efree(generator); +} +/* }}} */ + +static zend_object_value zend_generator_create(zend_class_entry *class_type TSRMLS_DC) /* {{{ */ +{ + zend_generator *generator; + zend_object_value object; + + generator = emalloc(sizeof(zend_generator)); + memset(generator, 0, sizeof(zend_generator)); + + zend_object_std_init(&generator->std, class_type TSRMLS_CC); + + object.handle = zend_objects_store_put(generator, NULL, + (zend_objects_free_object_storage_t) zend_generator_free_storage, + NULL /* no clone handler for now */ + TSRMLS_CC + ); + object.handlers = &zend_generator_handlers; + + return object; +} +/* }}} */ + +static zend_function *zend_generator_get_constructor(zval *object TSRMLS_DC) /* {{{ */ +{ + zend_error(E_RECOVERABLE_ERROR, "The \"Generator\" class is reserved for internal use and cannot be manually instantiated"); + + return NULL; +} +/* }}} */ static const zend_function_entry generator_functions[] = { ZEND_FE_END @@ -35,6 +78,10 @@ void zend_register_generator_ce(TSRMLS_D) /* {{{ */ INIT_CLASS_ENTRY(ce, "Generator", generator_functions); zend_ce_generator = zend_register_internal_class(&ce TSRMLS_CC); zend_ce_generator->ce_flags |= ZEND_ACC_FINAL_CLASS; + zend_ce_generator->create_object = zend_generator_create; + + memcpy(&zend_generator_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + zend_generator_handlers.get_constructor = zend_generator_get_constructor; } /* }}} */ From 46fa26ab853b766c9222e49dbe7555de360b42f4 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 20 May 2012 14:45:01 +0200 Subject: [PATCH 09/70] Make generator functions return a Generator object Right now generator functions simply immediately return a new Generator object (no suspension yet). --- .../generators/generator_returns_generator.phpt | 16 ++++++++++++++++ Zend/zend_execute.c | 1 + Zend/zend_vm_def.h | 12 ++++++++++-- Zend/zend_vm_execute.h | 12 ++++++++++-- 4 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 Zend/tests/generators/generator_returns_generator.phpt diff --git a/Zend/tests/generators/generator_returns_generator.phpt b/Zend/tests/generators/generator_returns_generator.phpt new file mode 100644 index 000000000000..a3e2b29468ff --- /dev/null +++ b/Zend/tests/generators/generator_returns_generator.phpt @@ -0,0 +1,16 @@ +--TEST-- +A generator function returns a Generator object +--FILE-- + +--EXPECT-- +bool(true) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index d72fc7369aff..d82abfc61384 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -35,6 +35,7 @@ #include "zend_exceptions.h" #include "zend_interfaces.h" #include "zend_closures.h" +#include "zend_generators.h" #include "zend_vm.h" #include "zend_dtrace.h" diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 9dc7e0842cf2..13ae824c48b8 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5197,9 +5197,17 @@ ZEND_VM_HANDLER(156, ZEND_SEPARATE, VAR, UNUSED) ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) { - /* do nothing for now */ + if (EG(return_value_ptr_ptr)) { + zval *return_value; - ZEND_VM_NEXT_OPCODE(); + ALLOC_INIT_ZVAL(return_value); + object_init_ex(return_value, zend_ce_generator); + + *EG(return_value_ptr_ptr) = return_value; + } + + /* for now we just do a normal return without suspension */ + ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper); } ZEND_VM_EXPORT_HELPER(zend_do_fcall, zend_do_fcall_common_helper) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index c6c3af65db6c..12338117f564 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1154,9 +1154,17 @@ static int ZEND_FASTCALL ZEND_USER_OPCODE_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS static int ZEND_FASTCALL ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - /* do nothing for now */ + if (EG(return_value_ptr_ptr)) { + zval *return_value; - ZEND_VM_NEXT_OPCODE(); + ALLOC_INIT_ZVAL(return_value); + object_init_ex(return_value, zend_ce_generator); + + *EG(return_value_ptr_ptr) = return_value; + } + + /* for now we just do a normal return without suspension */ + return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } static int ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) From 5e763d9420cbccbd8ee4f14a263b2439e6c5ae88 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 22 May 2012 23:14:36 +0200 Subject: [PATCH 10/70] Allocate execute_data using malloc for generators Generators need to switch the execute_data very often. If the execute_data is allocated on the VM stack this operation would require to always copy the structure (which is quite large). That's why the execution context is allocated on the heap instead (only for generators obviously). --- Zend/zend_vm_def.h | 8 +++++++- Zend/zend_vm_execute.h | 44 +++++++++++++++++++++++++++++++++++----- Zend/zend_vm_execute.skl | 36 ++++++++++++++++++++++++++++---- 3 files changed, 78 insertions(+), 10 deletions(-) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 13ae824c48b8..20b5633b8ae7 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2530,7 +2530,13 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY) nested = EX(nested); - zend_vm_stack_free(execute_data TSRMLS_CC); + /* For generators the execute_data is stored on the heap, for everything + * else it is stored on the VM stack. */ + if (op_array->fn_flags & ZEND_ACC_GENERATOR) { + efree(execute_data); + } else { + zend_vm_stack_free(execute_data TSRMLS_CC); + } if (nested) { execute_data = EG(current_execute_data); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 12338117f564..d351067cb763 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -342,11 +342,15 @@ static opcode_handler_t zend_vm_get_opcode_handler(zend_uchar opcode, zend_op* o ZEND_API void execute(zend_op_array *op_array TSRMLS_DC) { DCL_OPLINE + zend_execute_data *execute_data; + size_t execute_data_size; + zend_bool nested = 0; zend_bool original_in_execution = EG(in_execution); + if (EG(exception)) { return; } @@ -354,11 +358,35 @@ ZEND_API void execute(zend_op_array *op_array TSRMLS_DC) EG(in_execution) = 1; zend_vm_enter: - /* Initialize execute_data */ - execute_data = (zend_execute_data *)zend_vm_stack_alloc( + /* + * When allocating the execute_data, memory for compiled variables and + * temporary variables is also allocated after the actual zend_execute_data + * struct. op_array->last_var specifies the number of compiled variables and + * op_array->T is the number of temporary variables. If there is no symbol + * table, then twice as much memory is allocated for compiled variables. + * In that case the first half contains zval**s and the second half the + * actual zval*s (which would otherwise be in the symbol table). + */ + execute_data_size = ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data)) + - ZEND_MM_ALIGNED_SIZE(sizeof(zval**) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)) + - ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T TSRMLS_CC); + ZEND_MM_ALIGNED_SIZE(sizeof(zval **) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)) + + ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T + ; + + /* + * Normally the execute_data is allocated on the VM stack (because it does + * not actually do any allocation and thus is faster). For generators + * though this behavior would be suboptimal, because the (rather large) + * structure would have to be copied back and forth every time execution is + * suspended or resumed. That's why for generators the execution context + * is allocated using emalloc, thus allowing to save and restore it simply + * by replacing a pointer. + */ + if (op_array->fn_flags & ZEND_ACC_GENERATOR) { + execute_data = emalloc(execute_data_size); + } else { + execute_data = zend_vm_stack_alloc(execute_data_size TSRMLS_CC); + } EX(CVs) = (zval***)((char*)execute_data + ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data))); memset(EX(CVs), 0, sizeof(zval**) * op_array->last_var); @@ -477,7 +505,13 @@ static int ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS) nested = EX(nested); - zend_vm_stack_free(execute_data TSRMLS_CC); + /* For generators the execute_data is stored on the heap, for everything + * else it is stored on the VM stack. */ + if (op_array->fn_flags & ZEND_ACC_GENERATOR) { + efree(execute_data); + } else { + zend_vm_stack_free(execute_data TSRMLS_CC); + } if (nested) { execute_data = EG(current_execute_data); diff --git a/Zend/zend_vm_execute.skl b/Zend/zend_vm_execute.skl index 426f689795fc..359e05200894 100644 --- a/Zend/zend_vm_execute.skl +++ b/Zend/zend_vm_execute.skl @@ -3,9 +3,13 @@ ZEND_API void {%EXECUTOR_NAME%}(zend_op_array *op_array TSRMLS_DC) { DCL_OPLINE + zend_execute_data *execute_data; + size_t execute_data_size; + zend_bool nested = 0; zend_bool original_in_execution = EG(in_execution); + {%HELPER_VARS%} {%INTERNAL_LABELS%} @@ -17,11 +21,35 @@ ZEND_API void {%EXECUTOR_NAME%}(zend_op_array *op_array TSRMLS_DC) EG(in_execution) = 1; zend_vm_enter: - /* Initialize execute_data */ - execute_data = (zend_execute_data *)zend_vm_stack_alloc( + /* + * When allocating the execute_data, memory for compiled variables and + * temporary variables is also allocated after the actual zend_execute_data + * struct. op_array->last_var specifies the number of compiled variables and + * op_array->T is the number of temporary variables. If there is no symbol + * table, then twice as much memory is allocated for compiled variables. + * In that case the first half contains zval**s and the second half the + * actual zval*s (which would otherwise be in the symbol table). + */ + execute_data_size = ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data)) + - ZEND_MM_ALIGNED_SIZE(sizeof(zval**) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)) + - ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T TSRMLS_CC); + ZEND_MM_ALIGNED_SIZE(sizeof(zval **) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)) + + ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T + ; + + /* + * Normally the execute_data is allocated on the VM stack (because it does + * not actually do any allocation and thus is faster). For generators + * though this behavior would be suboptimal, because the (rather large) + * structure would have to be copied back and forth every time execution is + * suspended or resumed. That's why for generators the execution context + * is allocated using emalloc, thus allowing to save and restore it simply + * by replacing a pointer. + */ + if (op_array->fn_flags & ZEND_ACC_GENERATOR) { + execute_data = emalloc(execute_data_size); + } else { + execute_data = zend_vm_stack_alloc(execute_data_size TSRMLS_CC); + } EX(CVs) = (zval***)((char*)execute_data + ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data))); memset(EX(CVs), 0, sizeof(zval**) * op_array->last_var); From 9ce9a7e639bbb6c1c4bb34d542d2ac4e42e9457e Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 23 May 2012 14:20:25 +0200 Subject: [PATCH 11/70] Add initial code for suspending execution This is just some initial code, which is still quite broken (and needs to be moved so it can be reused.) --- Zend/zend_generators.c | 28 ++++++++++++++++++++++----- Zend/zend_generators.h | 7 +++++++ Zend/zend_vm_def.h | 43 ++++++++++++++++++++++++++++++++++++++++-- Zend/zend_vm_execute.h | 43 ++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 112 insertions(+), 9 deletions(-) diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 21581db2c038..d9ddd75ffbe7 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -25,15 +25,33 @@ ZEND_API zend_class_entry *zend_ce_generator; static zend_object_handlers zend_generator_handlers; -typedef struct _zend_generator { - zend_object std; - /* nothing more for now */ -} zend_generator; - static void zend_generator_free_storage(zend_generator *generator TSRMLS_DC) /* {{{ */ { zend_object_std_dtor(&generator->std TSRMLS_CC); + if (generator->execute_data) { + zend_execute_data *execute_data = generator->execute_data; + + if (!execute_data->symbol_table) { + int i; + for (i = 0; i < execute_data->op_array->last_var; ++i) { + if (execute_data->CVs[i]) { + zval_ptr_dtor(execute_data->CVs[i]); + } + } + } else { + if (EG(symtable_cache_ptr) >= EG(symtable_cache_limit)) { + zend_hash_destroy(execute_data->symbol_table); + FREE_HASHTABLE(execute_data->symbol_table); + } else { + zend_hash_clean(execute_data->symbol_table); + *(++EG(symtable_cache_ptr)) = execute_data->symbol_table; + } + } + + efree(execute_data); + } + efree(generator); } /* }}} */ diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index 60899072dffa..c3b8f455f65b 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -27,6 +27,13 @@ void zend_register_generator_ce(TSRMLS_D); extern ZEND_API zend_class_entry *zend_ce_generator; +typedef struct _zend_generator { + zend_object std; + + /* The suspended execution context. */ + zend_execute_data *execute_data; +} zend_generator; + END_EXTERN_C() #endif diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 20b5633b8ae7..f4d16be7f976 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5203,17 +5203,56 @@ ZEND_VM_HANDLER(156, ZEND_SEPARATE, VAR, UNUSED) ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) { + zend_bool nested; + if (EG(return_value_ptr_ptr)) { zval *return_value; + zend_generator *generator; ALLOC_INIT_ZVAL(return_value); object_init_ex(return_value, zend_ce_generator); *EG(return_value_ptr_ptr) = return_value; + + /* back up the execution context */ + generator = (zend_generator *) zend_object_store_get_object(return_value TSRMLS_CC); + generator->execute_data = execute_data; } - /* for now we just do a normal return without suspension */ - ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper); + /* restore the previous execution context */ + EG(current_execute_data) = EX(prev_execute_data); + nested = EX(nested); + + /* if there is no return value pointer we are responsible for freeing the + * execution data */ + if (!EG(return_value_ptr_ptr)) { + /* something has to be done in here, not sure yet what exactly */ + } + + EG(opline_ptr) = NULL; + if (nested) { + /* so we can use EX() again */ + execute_data = EG(current_execute_data); + + EG(opline_ptr) = &EX(opline); + EG(active_op_array) = EX(op_array); + EG(return_value_ptr_ptr) = EX(original_return_value); + EG(active_symbol_table) = EX(symbol_table); + EG(This) = EX(current_this); + EG(scope) = EX(current_scope); + EG(called_scope) = EX(current_called_scope); + + EX(function_state).function = (zend_function *) EX(op_array); + EX(function_state).arguments = NULL; + + EX(object) = EX(current_object); + EX(called_scope) = DECODE_CTOR(EX(called_scope)); + + zend_vm_stack_clear_multiple(TSRMLS_C); + } + + ZEND_VM_INC_OPCODE(); + ZEND_VM_LEAVE(); } ZEND_VM_EXPORT_HELPER(zend_do_fcall, zend_do_fcall_common_helper) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index d351067cb763..8316a0afb157 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1188,17 +1188,56 @@ static int ZEND_FASTCALL ZEND_USER_OPCODE_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS static int ZEND_FASTCALL ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { + zend_bool nested; + if (EG(return_value_ptr_ptr)) { zval *return_value; + zend_generator *generator; ALLOC_INIT_ZVAL(return_value); object_init_ex(return_value, zend_ce_generator); *EG(return_value_ptr_ptr) = return_value; + + /* back up the execution context */ + generator = (zend_generator *) zend_object_store_get_object(return_value TSRMLS_CC); + generator->execute_data = execute_data; } - /* for now we just do a normal return without suspension */ - return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + /* restore the previous execution context */ + EG(current_execute_data) = EX(prev_execute_data); + nested = EX(nested); + + /* if there is no return value pointer we are responsible for freeing the + * execution data */ + if (!EG(return_value_ptr_ptr)) { + /* something has to be done in here, not sure yet what exactly */ + } + + EG(opline_ptr) = NULL; + if (nested) { + /* so we can use EX() again */ + execute_data = EG(current_execute_data); + + EG(opline_ptr) = &EX(opline); + EG(active_op_array) = EX(op_array); + EG(return_value_ptr_ptr) = EX(original_return_value); + EG(active_symbol_table) = EX(symbol_table); + EG(This) = EX(current_this); + EG(scope) = EX(current_scope); + EG(called_scope) = EX(current_called_scope); + + EX(function_state).function = (zend_function *) EX(op_array); + EX(function_state).arguments = NULL; + + EX(object) = EX(current_object); + EX(called_scope) = DECODE_CTOR(EX(called_scope)); + + zend_vm_stack_clear_multiple(TSRMLS_C); + } + + ZEND_VM_INC_OPCODE(); + ZEND_VM_LEAVE(); } static int ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) From 2c5ecb4fea3580dfe7e89be7b236b1cacbaf80de Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 23 May 2012 16:07:15 +0200 Subject: [PATCH 12/70] Add dummy Iterator implementation This simply adds dummy rewind/valid/current/key/next methods to Generator. --- Zend/zend_generators.c | 61 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index d9ddd75ffbe7..00af65538aa6 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -20,6 +20,7 @@ #include "zend.h" #include "zend_API.h" +#include "zend_interfaces.h" #include "zend_generators.h" ZEND_API zend_class_entry *zend_ce_generator; @@ -85,7 +86,65 @@ static zend_function *zend_generator_get_constructor(zval *object TSRMLS_DC) /* } /* }}} */ +/* {{{ proto void Generator::rewind() + * Rewind the generator */ +ZEND_METHOD(Generator, rewind) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } +} +/* }}} */ + +/* {{{ proto bool Generator::valid() + * Check whether the generator is valid */ +ZEND_METHOD(Generator, valid) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } +} +/* }}} */ + +/* {{{ proto mixed Generator::current() + * Get the current value */ +ZEND_METHOD(Generator, current) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } +} +/* }}} */ + +/* {{{ proto mixed Generator::key() + * Get the current key */ +ZEND_METHOD(Generator, key) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } +} +/* }}} */ + +/* {{{ proto void Generator::next() + * Advances the generator */ +ZEND_METHOD(Generator, next) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } +} +/* }}} */ + +ZEND_BEGIN_ARG_INFO(arginfo_generator_void, 0) +ZEND_END_ARG_INFO() + static const zend_function_entry generator_functions[] = { + ZEND_ME(Generator, rewind, arginfo_generator_void, ZEND_ACC_PUBLIC) + ZEND_ME(Generator, valid, arginfo_generator_void, ZEND_ACC_PUBLIC) + ZEND_ME(Generator, current, arginfo_generator_void, ZEND_ACC_PUBLIC) + ZEND_ME(Generator, key, arginfo_generator_void, ZEND_ACC_PUBLIC) + ZEND_ME(Generator, next, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_FE_END }; @@ -98,6 +157,8 @@ void zend_register_generator_ce(TSRMLS_D) /* {{{ */ zend_ce_generator->ce_flags |= ZEND_ACC_FINAL_CLASS; zend_ce_generator->create_object = zend_generator_create; + zend_class_implements(zend_ce_generator TSRMLS_CC, 1, zend_ce_iterator); + memcpy(&zend_generator_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); zend_generator_handlers.get_constructor = zend_generator_get_constructor; } From ececcbce0e37a306afc1a039f52188b6c243fecc Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 23 May 2012 20:34:17 +0200 Subject: [PATCH 13/70] Allow calling zend_vm_gen from everywhere Before one could only call it with cwd=Zend. --- Zend/zend_vm_gen.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php index 3163000e4723..a6314edd0c4f 100644 --- a/Zend/zend_vm_gen.php +++ b/Zend/zend_vm_gen.php @@ -1182,7 +1182,7 @@ function gen_vm($def, $skel) { // Generate opcode #defines (zend_vm_opcodes.h) $code_len = strlen((string)$max_opcode); - $f = fopen("zend_vm_opcodes.h", "w+") or die("ERROR: Cannot create zend_vm_opcodes.h\n"); + $f = fopen(__DIR__ . "/zend_vm_opcodes.h", "w+") or die("ERROR: Cannot create zend_vm_opcodes.h\n"); // Insert header out($f, $GLOBALS['header_text']); @@ -1196,8 +1196,8 @@ function gen_vm($def, $skel) { echo "zend_vm_opcodes.h generated successfully.\n"; // Generate zend_vm_execute.h - $f = fopen("zend_vm_execute.h", "w+") or die("ERROR: Cannot create zend_vm_execute.h\n"); - $executor_file = realpath("zend_vm_execute.h"); + $f = fopen(__DIR__ . "/zend_vm_execute.h", "w+") or die("ERROR: Cannot create zend_vm_execute.h\n"); + $executor_file = realpath(__DIR__ . "/zend_vm_execute.h"); // Insert header out($f, $GLOBALS['header_text']); @@ -1440,6 +1440,6 @@ function usage() { define("ZEND_VM_LINES", 0); } -gen_vm("zend_vm_def.h", "zend_vm_execute.skl"); +gen_vm(__DIR__ . "/zend_vm_def.h", __DIR__ . "/zend_vm_execute.skl"); ?> From f627be52540738e124da7cb1566d7f60a2b6a48b Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 26 May 2012 17:53:13 +0200 Subject: [PATCH 14/70] Add support for executing a zend_execute_data This adds another function execute_ex(), which accepts a zend_execute_data struct to run (contrary to execute(), which accepts a zend_op_array from which it initialized the execute_data). This needs a bit more cleanup. --- Zend/zend_execute.h | 1 + Zend/zend_generators.c | 45 ++++++++++++++++++++++++ Zend/zend_generators.h | 2 ++ Zend/zend_vm_execute.h | 74 +++++++++++++++++++++------------------ Zend/zend_vm_execute.skl | 75 ++++++++++++++++++++++------------------ Zend/zend_vm_gen.php | 3 +- 6 files changed, 132 insertions(+), 68 deletions(-) diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 7d427388bdad..75a7eaa8a1ab 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -56,6 +56,7 @@ void init_executor(TSRMLS_D); void shutdown_executor(TSRMLS_D); void shutdown_destructors(TSRMLS_D); ZEND_API void execute(zend_op_array *op_array TSRMLS_DC); +ZEND_API void execute_ex(zend_execute_data *execute_data TSRMLS_DC); ZEND_API void execute_internal(zend_execute_data *execute_data_ptr, int return_value_used TSRMLS_DC); ZEND_API int zend_is_true(zval *op); #define safe_free_zval_ptr(p) safe_free_zval_ptr_rel(p ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 00af65538aa6..de1271fdf157 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -86,13 +86,34 @@ static zend_function *zend_generator_get_constructor(zval *object TSRMLS_DC) /* } /* }}} */ +static void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */ +{ + generator->execute_data->opline++; + execute_ex(generator->execute_data TSRMLS_CC); +} +/* }}} */ + +static void zend_generator_ensure_initialized(zend_generator *generator TSRMLS_DC) /* {{{ */ +{ + if (!generator->value) { + zend_generator_resume(generator TSRMLS_CC); + } +} +/* }}} */ + /* {{{ proto void Generator::rewind() * Rewind the generator */ ZEND_METHOD(Generator, rewind) { + zend_generator *generator; + if (zend_parse_parameters_none() == FAILURE) { return; } + + generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); + + zend_generator_ensure_initialized(generator TSRMLS_CC); } /* }}} */ @@ -100,9 +121,15 @@ ZEND_METHOD(Generator, rewind) * Check whether the generator is valid */ ZEND_METHOD(Generator, valid) { + zend_generator *generator; + if (zend_parse_parameters_none() == FAILURE) { return; } + + generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); + + zend_generator_ensure_initialized(generator TSRMLS_CC); } /* }}} */ @@ -110,9 +137,15 @@ ZEND_METHOD(Generator, valid) * Get the current value */ ZEND_METHOD(Generator, current) { + zend_generator *generator; + if (zend_parse_parameters_none() == FAILURE) { return; } + + generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); + + zend_generator_ensure_initialized(generator TSRMLS_CC); } /* }}} */ @@ -120,9 +153,15 @@ ZEND_METHOD(Generator, current) * Get the current key */ ZEND_METHOD(Generator, key) { + zend_generator *generator; + if (zend_parse_parameters_none() == FAILURE) { return; } + + generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); + + zend_generator_ensure_initialized(generator TSRMLS_CC); } /* }}} */ @@ -130,9 +169,15 @@ ZEND_METHOD(Generator, key) * Advances the generator */ ZEND_METHOD(Generator, next) { + zend_generator *generator; + if (zend_parse_parameters_none() == FAILURE) { return; } + + generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); + + zend_generator_ensure_initialized(generator TSRMLS_CC); } /* }}} */ diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index c3b8f455f65b..1422b71896eb 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -32,6 +32,8 @@ typedef struct _zend_generator { /* The suspended execution context. */ zend_execute_data *execute_data; + /* Current value */ + zval *value; } zend_generator; END_EXTERN_C() diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 8316a0afb157..c83a4dbb38c1 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -339,25 +339,9 @@ static opcode_handler_t zend_vm_get_opcode_handler(zend_uchar opcode, zend_op* o #define EX_Ts() EX(Ts) -ZEND_API void execute(zend_op_array *op_array TSRMLS_DC) -{ - DCL_OPLINE - +static zend_execute_data *zend_create_execute_data_from_op_array(zend_op_array *op_array, zend_bool nested TSRMLS_DC) { zend_execute_data *execute_data; - size_t execute_data_size; - - zend_bool nested = 0; - zend_bool original_in_execution = EG(in_execution); - - - - if (EG(exception)) { - return; - } - - EG(in_execution) = 1; -zend_vm_enter: /* * When allocating the execute_data, memory for compiled variables and * temporary variables is also allocated after the actual zend_execute_data @@ -367,11 +351,10 @@ ZEND_API void execute(zend_op_array *op_array TSRMLS_DC) * In that case the first half contains zval**s and the second half the * actual zval*s (which would otherwise be in the symbol table). */ - execute_data_size = - ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data)) + - ZEND_MM_ALIGNED_SIZE(sizeof(zval **) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)) + - ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T - ; + size_t execute_data_size = ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data)); + size_t CVs_size = ZEND_MM_ALIGNED_SIZE(sizeof(zval **) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)); + size_t Ts_size = ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T; + size_t total_size = execute_data_size + CVs_size + Ts_size; /* * Normally the execute_data is allocated on the VM stack (because it does @@ -383,14 +366,16 @@ ZEND_API void execute(zend_op_array *op_array TSRMLS_DC) * by replacing a pointer. */ if (op_array->fn_flags & ZEND_ACC_GENERATOR) { - execute_data = emalloc(execute_data_size); + execute_data = emalloc(total_size); } else { - execute_data = zend_vm_stack_alloc(execute_data_size TSRMLS_CC); + execute_data = zend_vm_stack_alloc(total_size TSRMLS_CC); } - EX(CVs) = (zval***)((char*)execute_data + ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data))); - memset(EX(CVs), 0, sizeof(zval**) * op_array->last_var); - EX(Ts) = (temp_variable *)(((char*)EX(CVs)) + ZEND_MM_ALIGNED_SIZE(sizeof(zval**) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2))); + EX(CVs) = (zval ***) ((char *) execute_data + execute_data_size); + memset(EX(CVs), 0, sizeof(zval **) * op_array->last_var); + + EX(Ts) = (temp_variable *) ((char *) EX(CVs) + CVs_size); + EX(fbc) = NULL; EX(called_scope) = NULL; EX(object) = NULL; @@ -400,9 +385,6 @@ ZEND_API void execute(zend_op_array *op_array TSRMLS_DC) EX(prev_execute_data) = EG(current_execute_data); EG(current_execute_data) = execute_data; EX(nested) = nested; - nested = 1; - - LOAD_REGS(); if (!op_array->run_time_cache && op_array->last_cache_slot) { op_array->run_time_cache = ecalloc(op_array->last_cache_slot, sizeof(void*)); @@ -422,11 +404,31 @@ ZEND_API void execute(zend_op_array *op_array TSRMLS_DC) EX(opline) = UNEXPECTED((op_array->fn_flags & ZEND_ACC_INTERACTIVE) != 0) && EG(start_op) ? EG(start_op) : op_array->opcodes; EG(opline_ptr) = &EX(opline); - LOAD_OPLINE(); EX(function_state).function = (zend_function *) op_array; EX(function_state).arguments = NULL; + return execute_data; +} + +ZEND_API void execute_ex(zend_execute_data *execute_data TSRMLS_DC) +{ + DCL_OPLINE + + zend_bool original_in_execution = EG(in_execution); + + + + if (EG(exception)) { + return; + } + + EG(in_execution) = 1; + +zend_vm_enter: + LOAD_REGS(); + LOAD_OPLINE(); + while (1) { int ret; #ifdef ZEND_WIN32 @@ -441,8 +443,7 @@ ZEND_API void execute(zend_op_array *op_array TSRMLS_DC) EG(in_execution) = original_in_execution; return; case 2: - op_array = EG(active_op_array); - goto zend_vm_enter; + execute_data = zend_create_execute_data_from_op_array(EG(active_op_array), 1 TSRMLS_CC); case 3: execute_data = EG(current_execute_data); default: @@ -454,6 +455,13 @@ ZEND_API void execute(zend_op_array *op_array TSRMLS_DC) zend_error_noreturn(E_ERROR, "Arrived at end of main loop which shouldn't happen"); } +ZEND_API void execute(zend_op_array *op_array TSRMLS_DC) +{ + zend_execute_data *execute_data = zend_create_execute_data_from_op_array(op_array, 0 TSRMLS_CC); + + execute_ex(execute_data TSRMLS_CC); +} + static int ZEND_FASTCALL ZEND_JMP_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE diff --git a/Zend/zend_vm_execute.skl b/Zend/zend_vm_execute.skl index 359e05200894..d251e55344e2 100644 --- a/Zend/zend_vm_execute.skl +++ b/Zend/zend_vm_execute.skl @@ -1,26 +1,8 @@ {%DEFINES%} -ZEND_API void {%EXECUTOR_NAME%}(zend_op_array *op_array TSRMLS_DC) -{ - DCL_OPLINE - +static zend_execute_data *zend_create_execute_data_from_op_array(zend_op_array *op_array, zend_bool nested TSRMLS_DC) { zend_execute_data *execute_data; - size_t execute_data_size; - - zend_bool nested = 0; - zend_bool original_in_execution = EG(in_execution); - - {%HELPER_VARS%} - - {%INTERNAL_LABELS%} - - if (EG(exception)) { - return; - } - - EG(in_execution) = 1; -zend_vm_enter: /* * When allocating the execute_data, memory for compiled variables and * temporary variables is also allocated after the actual zend_execute_data @@ -30,11 +12,10 @@ zend_vm_enter: * In that case the first half contains zval**s and the second half the * actual zval*s (which would otherwise be in the symbol table). */ - execute_data_size = - ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data)) + - ZEND_MM_ALIGNED_SIZE(sizeof(zval **) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)) + - ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T - ; + size_t execute_data_size = ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data)); + size_t CVs_size = ZEND_MM_ALIGNED_SIZE(sizeof(zval **) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2)); + size_t Ts_size = ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T; + size_t total_size = execute_data_size + CVs_size + Ts_size; /* * Normally the execute_data is allocated on the VM stack (because it does @@ -46,14 +27,16 @@ zend_vm_enter: * by replacing a pointer. */ if (op_array->fn_flags & ZEND_ACC_GENERATOR) { - execute_data = emalloc(execute_data_size); + execute_data = emalloc(total_size); } else { - execute_data = zend_vm_stack_alloc(execute_data_size TSRMLS_CC); + execute_data = zend_vm_stack_alloc(total_size TSRMLS_CC); } - EX(CVs) = (zval***)((char*)execute_data + ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data))); - memset(EX(CVs), 0, sizeof(zval**) * op_array->last_var); - EX(Ts) = (temp_variable *)(((char*)EX(CVs)) + ZEND_MM_ALIGNED_SIZE(sizeof(zval**) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2))); + EX(CVs) = (zval ***) ((char *) execute_data + execute_data_size); + memset(EX(CVs), 0, sizeof(zval **) * op_array->last_var); + + EX(Ts) = (temp_variable *) ((char *) EX(CVs) + CVs_size); + EX(fbc) = NULL; EX(called_scope) = NULL; EX(object) = NULL; @@ -63,9 +46,6 @@ zend_vm_enter: EX(prev_execute_data) = EG(current_execute_data); EG(current_execute_data) = execute_data; EX(nested) = nested; - nested = 1; - - LOAD_REGS(); if (!op_array->run_time_cache && op_array->last_cache_slot) { op_array->run_time_cache = ecalloc(op_array->last_cache_slot, sizeof(void*)); @@ -85,11 +65,33 @@ zend_vm_enter: EX(opline) = UNEXPECTED((op_array->fn_flags & ZEND_ACC_INTERACTIVE) != 0) && EG(start_op) ? EG(start_op) : op_array->opcodes; EG(opline_ptr) = &EX(opline); - LOAD_OPLINE(); EX(function_state).function = (zend_function *) op_array; EX(function_state).arguments = NULL; + return execute_data; +} + +ZEND_API void {%EXECUTOR_NAME%}_ex(zend_execute_data *execute_data TSRMLS_DC) +{ + DCL_OPLINE + + zend_bool original_in_execution = EG(in_execution); + + {%HELPER_VARS%} + + {%INTERNAL_LABELS%} + + if (EG(exception)) { + return; + } + + EG(in_execution) = 1; + +zend_vm_enter: + LOAD_REGS(); + LOAD_OPLINE(); + while (1) { {%ZEND_VM_CONTINUE_LABEL%} #ifdef ZEND_WIN32 @@ -106,6 +108,13 @@ zend_vm_enter: zend_error_noreturn(E_ERROR, "Arrived at end of main loop which shouldn't happen"); } +ZEND_API void {%EXECUTOR_NAME%}(zend_op_array *op_array TSRMLS_DC) +{ + zend_execute_data *execute_data = zend_create_execute_data_from_op_array(op_array, 0 TSRMLS_CC); + + {%EXECUTOR_NAME%}_ex(execute_data TSRMLS_CC); +} + {%EXTERNAL_EXECUTOR%} void {%INITIALIZER_NAME%}(void) diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php index a6314edd0c4f..b7fd874264fc 100644 --- a/Zend/zend_vm_gen.php +++ b/Zend/zend_vm_gen.php @@ -976,8 +976,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name, $m[1]."\t\tEG(in_execution) = original_in_execution;\n". $m[1]."\t\treturn;\n". $m[1]."\tcase 2:\n" . - $m[1]."\t\top_array = EG(active_op_array);\n". - $m[1]."\t\tgoto zend_vm_enter;\n". + $m[1]."\t\texecute_data = zend_create_execute_data_from_op_array(EG(active_op_array), 1 TSRMLS_CC);\n". $m[1]."\tcase 3:\n" . $m[1]."\t\texecute_data = EG(current_execute_data);\n". $m[1]."\tdefault:\n". From 1a99d1c8874936f7e232840a580d7bc588d63a6c Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 26 May 2012 19:40:29 +0200 Subject: [PATCH 15/70] Add way to pass generator object to opcode handlers The generator zval is put into the return_value_ptr_ptr. --- Zend/zend_generators.c | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index de1271fdf157..1ea910b0eba2 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -86,17 +86,23 @@ static zend_function *zend_generator_get_constructor(zval *object TSRMLS_DC) /* } /* }}} */ -static void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */ +static void zend_generator_resume(zval *object, zend_generator *generator TSRMLS_DC) /* {{{ */ { + /* Go to next opcode (we don't want to run the last one again) */ generator->execute_data->opline++; + + /* We (mis) use the return_value_ptr_ptr to provide the generator object + * to the executor. This way YIELD will be able to set the yielded value */ + EG(return_value_ptr_ptr) = &object; + execute_ex(generator->execute_data TSRMLS_CC); } /* }}} */ -static void zend_generator_ensure_initialized(zend_generator *generator TSRMLS_DC) /* {{{ */ +static void zend_generator_ensure_initialized(zval *object, zend_generator *generator TSRMLS_DC) /* {{{ */ { if (!generator->value) { - zend_generator_resume(generator TSRMLS_CC); + zend_generator_resume(object, generator TSRMLS_CC); } } /* }}} */ @@ -105,15 +111,17 @@ static void zend_generator_ensure_initialized(zend_generator *generator TSRMLS_D * Rewind the generator */ ZEND_METHOD(Generator, rewind) { + zval *object; zend_generator *generator; if (zend_parse_parameters_none() == FAILURE) { return; } - generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); + object = getThis(); + generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); - zend_generator_ensure_initialized(generator TSRMLS_CC); + zend_generator_ensure_initialized(object, generator TSRMLS_CC); } /* }}} */ @@ -121,15 +129,17 @@ ZEND_METHOD(Generator, rewind) * Check whether the generator is valid */ ZEND_METHOD(Generator, valid) { + zval *object; zend_generator *generator; if (zend_parse_parameters_none() == FAILURE) { return; } - generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); + object = getThis(); + generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); - zend_generator_ensure_initialized(generator TSRMLS_CC); + zend_generator_ensure_initialized(object, generator TSRMLS_CC); } /* }}} */ @@ -137,15 +147,17 @@ ZEND_METHOD(Generator, valid) * Get the current value */ ZEND_METHOD(Generator, current) { + zval *object; zend_generator *generator; if (zend_parse_parameters_none() == FAILURE) { return; } - generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); + object = getThis(); + generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); - zend_generator_ensure_initialized(generator TSRMLS_CC); + zend_generator_ensure_initialized(object, generator TSRMLS_CC); } /* }}} */ @@ -153,15 +165,17 @@ ZEND_METHOD(Generator, current) * Get the current key */ ZEND_METHOD(Generator, key) { + zval *object; zend_generator *generator; if (zend_parse_parameters_none() == FAILURE) { return; } - generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); + object = getThis(); + generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); - zend_generator_ensure_initialized(generator TSRMLS_CC); + zend_generator_ensure_initialized(object, generator TSRMLS_CC); } /* }}} */ @@ -169,15 +183,16 @@ ZEND_METHOD(Generator, key) * Advances the generator */ ZEND_METHOD(Generator, next) { + zval *object; zend_generator *generator; if (zend_parse_parameters_none() == FAILURE) { return; } - generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); + generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); - zend_generator_ensure_initialized(generator TSRMLS_CC); + zend_generator_ensure_initialized(object, generator TSRMLS_CC); } /* }}} */ From fafce58683e74a397023cc1077aae210109b40b6 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 26 May 2012 22:44:53 +0200 Subject: [PATCH 16/70] Add YIELD opcode implementation --- Zend/zend_compile.c | 8 +- Zend/zend_generators.c | 23 ++++- Zend/zend_vm_def.h | 41 +++++++++ Zend/zend_vm_execute.h | 186 +++++++++++++++++++++++++++++++++++++++++ Zend/zend_vm_opcodes.h | 1 + 5 files changed, 256 insertions(+), 3 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index f0802efd587b..b0e6ee20660f 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2660,11 +2660,17 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */ void zend_do_yield(znode *expr TSRMLS_DC) /* {{{ */ { + zend_op *opline; + if ((CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) == 0) { zend_error(E_COMPILE_ERROR, "The \"yield\" statement can only be used inside a generator function"); } - /* do nothing for now */ + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + + opline->opcode = ZEND_YIELD; + SET_NODE(opline->op1, expr); + SET_UNUSED(opline->op2); } /* }}} */ diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 1ea910b0eba2..8161fc793514 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -53,6 +53,10 @@ static void zend_generator_free_storage(zend_generator *generator TSRMLS_DC) /* efree(execute_data); } + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + efree(generator); } /* }}} */ @@ -88,14 +92,28 @@ static zend_function *zend_generator_get_constructor(zval *object TSRMLS_DC) /* static void zend_generator_resume(zval *object, zend_generator *generator TSRMLS_DC) /* {{{ */ { - /* Go to next opcode (we don't want to run the last one again) */ - generator->execute_data->opline++; + /* Backup executor globals */ + zval **original_return_value_ptr_ptr = EG(return_value_ptr_ptr); + zend_op **original_opline_ptr = EG(opline_ptr); + zend_op_array *original_active_op_array = EG(active_op_array); /* We (mis) use the return_value_ptr_ptr to provide the generator object * to the executor. This way YIELD will be able to set the yielded value */ EG(return_value_ptr_ptr) = &object; + EG(opline_ptr) = &generator->execute_data->opline; + EG(active_op_array) = generator->execute_data->op_array; + + /* Go to next opcode (we don't want to run the last one again) */ + generator->execute_data->opline++; + + /* Resume execution */ execute_ex(generator->execute_data TSRMLS_CC); + + /* Restore executor globals */ + EG(return_value_ptr_ptr) = original_return_value_ptr_ptr; + EG(opline_ptr) = original_opline_ptr; + EG(active_op_array) = original_active_op_array; } /* }}} */ @@ -190,6 +208,7 @@ ZEND_METHOD(Generator, next) return; } + object = getThis(); generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); zend_generator_ensure_initialized(object, generator TSRMLS_CC); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index f4d16be7f976..b2fc0517e5fe 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5255,4 +5255,45 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) ZEND_VM_LEAVE(); } +ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV, ANY) +{ + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + { + USE_OPLINE + zend_free_op free_op1; + zval *value = GET_OP1_ZVAL_PTR(BP_VAR_R); + + /* Consts, temporary variables and references need copying */ + if (OP1_TYPE == IS_CONST || OP1_TYPE == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!IS_OP1_TMP_FREE()) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + generator->value = value; + Z_ADDREF_P(value); + } + + FREE_OP1_IF_VAR(); + } + + ZEND_VM_RETURN(); +} + ZEND_VM_EXPORT_HELPER(zend_do_fcall, zend_do_fcall_common_helper) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index c83a4dbb38c1..775b0ec06455 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3030,6 +3030,46 @@ static int ZEND_FASTCALL ZEND_QM_ASSIGN_VAR_SPEC_CONST_HANDLER(ZEND_OPCODE_HAND ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + { + USE_OPLINE + + zval *value = opline->op1.zv; + + /* Consts, temporary variables and references need copying */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + generator->value = value; + Z_ADDREF_P(value); + } + + } + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -7616,6 +7656,46 @@ static int ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_A ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + { + USE_OPLINE + zend_free_op free_op1; + zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!1) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + generator->value = value; + Z_ADDREF_P(value); + } + + } + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -12279,6 +12359,47 @@ static int ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_A ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + { + USE_OPLINE + zend_free_op free_op1; + zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + generator->value = value; + Z_ADDREF_P(value); + } + + if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + } + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -28113,6 +28234,46 @@ static int ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_AR ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + { + USE_OPLINE + + zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + generator->value = value; + Z_ADDREF_P(value); + } + + } + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -40903,6 +41064,31 @@ void zend_init_opcodes_handlers(void) ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, + ZEND_YIELD_SPEC_CONST_HANDLER, + ZEND_YIELD_SPEC_CONST_HANDLER, + ZEND_YIELD_SPEC_CONST_HANDLER, + ZEND_YIELD_SPEC_CONST_HANDLER, + ZEND_YIELD_SPEC_CONST_HANDLER, + ZEND_YIELD_SPEC_TMP_HANDLER, + ZEND_YIELD_SPEC_TMP_HANDLER, + ZEND_YIELD_SPEC_TMP_HANDLER, + ZEND_YIELD_SPEC_TMP_HANDLER, + ZEND_YIELD_SPEC_TMP_HANDLER, + ZEND_YIELD_SPEC_VAR_HANDLER, + ZEND_YIELD_SPEC_VAR_HANDLER, + ZEND_YIELD_SPEC_VAR_HANDLER, + ZEND_YIELD_SPEC_VAR_HANDLER, + ZEND_YIELD_SPEC_VAR_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_YIELD_SPEC_CV_HANDLER, + ZEND_YIELD_SPEC_CV_HANDLER, + ZEND_YIELD_SPEC_CV_HANDLER, + ZEND_YIELD_SPEC_CV_HANDLER, + ZEND_YIELD_SPEC_CV_HANDLER, ZEND_NULL_HANDLER }; zend_opcode_handlers = (opcode_handler_t*)labels; diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 9e24dee11d6e..53e00a1743c3 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -160,3 +160,4 @@ #define ZEND_QM_ASSIGN_VAR 157 #define ZEND_JMP_SET_VAR 158 #define ZEND_SUSPEND_AND_RETURN_GENERATOR 159 +#define ZEND_YIELD 160 From 5bb3a995c2ad1a41c22a88e17dc46fef22ab1849 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 26 May 2012 23:07:05 +0200 Subject: [PATCH 17/70] Implement return for generators For generators ZEND_RETURN directly calls ZEND_VM_RETURN(), thus passing execution back to the caller (zend_generator_resume). This commit also adds a check that only return; is used in generators and not return $value;. --- .../errors/generator_cannot_return_error.phpt | 12 +++++++++++ Zend/zend_compile.c | 4 ++++ Zend/zend_generators.c | 11 ++++++++++ Zend/zend_vm_def.h | 5 +++++ Zend/zend_vm_execute.h | 20 +++++++++++++++++++ 5 files changed, 52 insertions(+) create mode 100644 Zend/tests/generators/errors/generator_cannot_return_error.phpt diff --git a/Zend/tests/generators/errors/generator_cannot_return_error.phpt b/Zend/tests/generators/errors/generator_cannot_return_error.phpt new file mode 100644 index 000000000000..070e3d5d5c08 --- /dev/null +++ b/Zend/tests/generators/errors/generator_cannot_return_error.phpt @@ -0,0 +1,12 @@ +--TEST-- +Generators cannot return values +--FILE-- + +--EXPECTF-- +Fatal error: Generators cannot return values using "return" in %s on line %d diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index b0e6ee20660f..30fecf1e2e19 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2615,6 +2615,10 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */ zend_op *opline; int start_op_number, end_op_number; + if ((CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) && expr != NULL) { + zend_error(E_COMPILE_ERROR, "Generators cannot return values using \"return\""); + } + if (do_end_vparse) { if ((CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) && !zend_is_function_or_method_call(expr)) { zend_do_end_variable_parse(expr, BP_VAR_W, 0 TSRMLS_CC); diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 8161fc793514..78cd0c29883e 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -140,6 +140,9 @@ ZEND_METHOD(Generator, rewind) generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); zend_generator_ensure_initialized(object, generator TSRMLS_CC); + + /* Generators aren't rewindable, so rewind() only has to make sure that + * the generator is initialized, nothing more */ } /* }}} */ @@ -158,6 +161,8 @@ ZEND_METHOD(Generator, valid) generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); zend_generator_ensure_initialized(object, generator TSRMLS_CC); + + RETURN_BOOL(generator->value != NULL); } /* }}} */ @@ -176,6 +181,10 @@ ZEND_METHOD(Generator, current) generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); zend_generator_ensure_initialized(object, generator TSRMLS_CC); + + if (generator->value) { + RETURN_ZVAL(generator->value, 1, 1); + } } /* }}} */ @@ -212,6 +221,8 @@ ZEND_METHOD(Generator, next) generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); zend_generator_ensure_initialized(object, generator TSRMLS_CC); + + zend_generator_resume(object, generator TSRMLS_CC); } /* }}} */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index b2fc0517e5fe..b944be0241b2 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2840,6 +2840,11 @@ ZEND_VM_HANDLER(62, ZEND_RETURN, CONST|TMP|VAR|CV, ANY) zval *retval_ptr; zend_free_op free_op1; + /* For generators return means to simply stop executing */ + if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { + ZEND_VM_RETURN(); + } + SAVE_OPLINE(); retval_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 775b0ec06455..d3fa75c2e76c 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -2321,6 +2321,11 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARG zval *retval_ptr; + /* For generators return means to simply stop executing */ + if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { + ZEND_VM_RETURN(); + } + SAVE_OPLINE(); retval_ptr = opline->op1.zv; @@ -6894,6 +6899,11 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) zval *retval_ptr; zend_free_op free_op1; + /* For generators return means to simply stop executing */ + if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { + ZEND_VM_RETURN(); + } + SAVE_OPLINE(); retval_ptr = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); @@ -11372,6 +11382,11 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) zval *retval_ptr; zend_free_op free_op1; + /* For generators return means to simply stop executing */ + if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { + ZEND_VM_RETURN(); + } + SAVE_OPLINE(); retval_ptr = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); @@ -27406,6 +27421,11 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) zval *retval_ptr; + /* For generators return means to simply stop executing */ + if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { + ZEND_VM_RETURN(); + } + SAVE_OPLINE(); retval_ptr = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); From d49d3971e630121a3cc11196b88f7afa2b1e2f26 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 26 May 2012 23:59:22 +0200 Subject: [PATCH 18/70] Close generator on return --- Zend/zend_generators.c | 19 ++++++++++++++++--- Zend/zend_generators.h | 9 +++++---- Zend/zend_vm_def.h | 8 +++++++- Zend/zend_vm_execute.h | 32 ++++++++++++++++++++++++++++---- 4 files changed, 56 insertions(+), 12 deletions(-) diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 78cd0c29883e..a6b165277426 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -26,10 +26,8 @@ ZEND_API zend_class_entry *zend_ce_generator; static zend_object_handlers zend_generator_handlers; -static void zend_generator_free_storage(zend_generator *generator TSRMLS_DC) /* {{{ */ +void zend_generator_close(zend_generator *generator TSRMLS_DC) /* {{{ */ { - zend_object_std_dtor(&generator->std TSRMLS_CC); - if (generator->execute_data) { zend_execute_data *execute_data = generator->execute_data; @@ -51,12 +49,22 @@ static void zend_generator_free_storage(zend_generator *generator TSRMLS_DC) /* } efree(execute_data); + generator->execute_data = NULL; } if (generator->value) { zval_ptr_dtor(&generator->value); + generator->value = NULL; } +} +/* }}} */ + +static void zend_generator_free_storage(zend_generator *generator TSRMLS_DC) /* {{{ */ +{ + zend_generator_close(generator TSRMLS_CC); + + zend_object_std_dtor(&generator->std TSRMLS_CC); efree(generator); } /* }}} */ @@ -92,6 +100,11 @@ static zend_function *zend_generator_get_constructor(zval *object TSRMLS_DC) /* static void zend_generator_resume(zval *object, zend_generator *generator TSRMLS_DC) /* {{{ */ { + /* The generator is already closed, thus can't resume */ + if (!generator->execute_data) { + return; + } + /* Backup executor globals */ zval **original_return_value_ptr_ptr = EG(return_value_ptr_ptr); zend_op **original_opline_ptr = EG(opline_ptr); diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index 1422b71896eb..4becddf17c93 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -23,10 +23,6 @@ BEGIN_EXTERN_C() -void zend_register_generator_ce(TSRMLS_D); - -extern ZEND_API zend_class_entry *zend_ce_generator; - typedef struct _zend_generator { zend_object std; @@ -36,6 +32,11 @@ typedef struct _zend_generator { zval *value; } zend_generator; +extern ZEND_API zend_class_entry *zend_ce_generator; + +void zend_register_generator_ce(TSRMLS_D); +void zend_generator_close(zend_generator *generator TSRMLS_DC); + END_EXTERN_C() #endif diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index b944be0241b2..781bd9fdd1c4 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2840,8 +2840,14 @@ ZEND_VM_HANDLER(62, ZEND_RETURN, CONST|TMP|VAR|CV, ANY) zval *retval_ptr; zend_free_op free_op1; - /* For generators return means to simply stop executing */ if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Close the generator to free up resources. */ + zend_generator_close(generator TSRMLS_CC); + + /* Pass execution back to generator handling code */ ZEND_VM_RETURN(); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index d3fa75c2e76c..1eac10512d7c 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -2321,8 +2321,14 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARG zval *retval_ptr; - /* For generators return means to simply stop executing */ if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Close the generator to free up resources. */ + zend_generator_close(generator TSRMLS_CC); + + /* Pass execution back to generator handling code */ ZEND_VM_RETURN(); } @@ -6899,8 +6905,14 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) zval *retval_ptr; zend_free_op free_op1; - /* For generators return means to simply stop executing */ if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Close the generator to free up resources. */ + zend_generator_close(generator TSRMLS_CC); + + /* Pass execution back to generator handling code */ ZEND_VM_RETURN(); } @@ -11382,8 +11394,14 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) zval *retval_ptr; zend_free_op free_op1; - /* For generators return means to simply stop executing */ if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Close the generator to free up resources. */ + zend_generator_close(generator TSRMLS_CC); + + /* Pass execution back to generator handling code */ ZEND_VM_RETURN(); } @@ -27421,8 +27439,14 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) zval *retval_ptr; - /* For generators return means to simply stop executing */ if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Close the generator to free up resources. */ + zend_generator_close(generator TSRMLS_CC); + + /* Pass execution back to generator handling code */ ZEND_VM_RETURN(); } From cbfa96cad59d679502eca002a1e920065f11f871 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 27 May 2012 00:25:12 +0200 Subject: [PATCH 19/70] Remove wrong dtor call --- Zend/zend_generators.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index a6b165277426..36a9727722d5 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -196,7 +196,7 @@ ZEND_METHOD(Generator, current) zend_generator_ensure_initialized(object, generator TSRMLS_CC); if (generator->value) { - RETURN_ZVAL(generator->value, 1, 1); + RETURN_ZVAL(generator->value, 1, 0); } } /* }}} */ From 39d3d5ec138119acf614fb285e92c3bcda7a2555 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 27 May 2012 00:50:27 +0200 Subject: [PATCH 20/70] Add first real generator test The test implements an xrange() function (the generator version of range()). --- Zend/tests/generators/xrange.phpt | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Zend/tests/generators/xrange.phpt diff --git a/Zend/tests/generators/xrange.phpt b/Zend/tests/generators/xrange.phpt new file mode 100644 index 000000000000..685c6b3b5704 --- /dev/null +++ b/Zend/tests/generators/xrange.phpt @@ -0,0 +1,23 @@ +--TEST-- +Simple generator xrange() test +--FILE-- + +--EXPECT-- +int(10) +int(12) +int(14) +int(16) +int(18) +int(20) From 247bb737d552508829c5de7770ed0b15c3d8f7fd Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 27 May 2012 03:50:31 +0200 Subject: [PATCH 21/70] Add support for generator methods --- Zend/zend_generators.c | 63 +++++++++++++++++++++++++++--------------- Zend/zend_vm_def.h | 5 ++++ Zend/zend_vm_execute.h | 5 ++++ 3 files changed, 51 insertions(+), 22 deletions(-) diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 36a9727722d5..ae706f19a579 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -48,6 +48,10 @@ void zend_generator_close(zend_generator *generator TSRMLS_DC) /* {{{ */ } } + if (execute_data->current_this) { + zval_ptr_dtor(&execute_data->current_this); + } + efree(execute_data); generator->execute_data = NULL; } @@ -105,28 +109,43 @@ static void zend_generator_resume(zval *object, zend_generator *generator TSRMLS return; } - /* Backup executor globals */ - zval **original_return_value_ptr_ptr = EG(return_value_ptr_ptr); - zend_op **original_opline_ptr = EG(opline_ptr); - zend_op_array *original_active_op_array = EG(active_op_array); - - /* We (mis) use the return_value_ptr_ptr to provide the generator object - * to the executor. This way YIELD will be able to set the yielded value */ - EG(return_value_ptr_ptr) = &object; - - EG(opline_ptr) = &generator->execute_data->opline; - EG(active_op_array) = generator->execute_data->op_array; - - /* Go to next opcode (we don't want to run the last one again) */ - generator->execute_data->opline++; - - /* Resume execution */ - execute_ex(generator->execute_data TSRMLS_CC); - - /* Restore executor globals */ - EG(return_value_ptr_ptr) = original_return_value_ptr_ptr; - EG(opline_ptr) = original_opline_ptr; - EG(active_op_array) = original_active_op_array; + { + /* Backup executor globals */ + zval **original_return_value_ptr_ptr = EG(return_value_ptr_ptr); + zend_op **original_opline_ptr = EG(opline_ptr); + zend_op_array *original_active_op_array = EG(active_op_array); + HashTable *original_active_symbol_table = EG(active_symbol_table); + zval *original_This = EG(This); + zend_class_entry *original_scope = EG(scope); + zend_class_entry *original_called_scope = EG(called_scope); + + /* We (mis)use the return_value_ptr_ptr to provide the generator object + * to the executor, so YIELD will be able to set the yielded value */ + EG(return_value_ptr_ptr) = &object; + + /* Set executor globals */ + EG(opline_ptr) = &generator->execute_data->opline; + EG(active_op_array) = generator->execute_data->op_array; + EG(active_symbol_table) = generator->execute_data->symbol_table; + EG(This) = generator->execute_data->current_this; + EG(scope) = generator->execute_data->current_scope; + EG(called_scope) = generator->execute_data->current_called_scope; + + /* Go to next opcode (we don't want to run the last one again) */ + generator->execute_data->opline++; + + /* Resume execution */ + execute_ex(generator->execute_data TSRMLS_CC); + + /* Restore executor globals */ + EG(return_value_ptr_ptr) = original_return_value_ptr_ptr; + EG(opline_ptr) = original_opline_ptr; + EG(active_op_array) = original_active_op_array; + EG(active_symbol_table) = original_active_symbol_table; + EG(This) = original_This; + EG(scope) = original_scope; + EG(called_scope) = original_called_scope; + } } /* }}} */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 781bd9fdd1c4..42397ed278d5 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5225,6 +5225,11 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) *EG(return_value_ptr_ptr) = return_value; + /* back up some executor globals */ + EX(current_this) = EG(This); + EX(current_scope) = EG(scope); + EX(current_called_scope) = EG(called_scope); + /* back up the execution context */ generator = (zend_generator *) zend_object_store_get_object(return_value TSRMLS_CC); generator->execute_data = execute_data; diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 1eac10512d7c..fa802c652b64 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1207,6 +1207,11 @@ static int ZEND_FASTCALL ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER(ZEND_OP *EG(return_value_ptr_ptr) = return_value; + /* back up some executor globals */ + EX(current_this) = EG(This); + EX(current_scope) = EG(scope); + EX(current_called_scope) = EG(called_scope); + /* back up the execution context */ generator = (zend_generator *) zend_object_store_get_object(return_value TSRMLS_CC); generator->execute_data = execute_data; From 64a643a4e3c1b45d3a9b2ad24537f7aab6296f7d Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 27 May 2012 17:14:20 +0200 Subject: [PATCH 22/70] Free loop variables If the generator is closed before it has finished running, it may happen that some FREE or SWITCH_FREE opcodes haven't been executed and memory is leaked. This fixes it by walking the brk_cont_array and manually freeing the variables. --- .../generators/no_foreach_var_leaks.phpt | 19 +++++++++ Zend/zend_generators.c | 40 ++++++++++++++++++- Zend/zend_generators.h | 2 +- Zend/zend_vm_def.h | 2 +- Zend/zend_vm_execute.h | 8 ++-- 5 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 Zend/tests/generators/no_foreach_var_leaks.phpt diff --git a/Zend/tests/generators/no_foreach_var_leaks.phpt b/Zend/tests/generators/no_foreach_var_leaks.phpt new file mode 100644 index 000000000000..36ab91bb15a6 --- /dev/null +++ b/Zend/tests/generators/no_foreach_var_leaks.phpt @@ -0,0 +1,19 @@ +--TEST-- +foreach() (and other) variables aren't leaked on premature close +--FILE-- +current()); + +// generator is closed here, without running SWITCH_FREE + +?> +--EXPECT-- +string(3) "Foo" diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index ae706f19a579..12116a0f109d 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -26,7 +26,7 @@ ZEND_API zend_class_entry *zend_ce_generator; static zend_object_handlers zend_generator_handlers; -void zend_generator_close(zend_generator *generator TSRMLS_DC) /* {{{ */ +void zend_generator_close(zend_generator *generator, zend_bool finished_execution TSRMLS_DC) /* {{{ */ { if (generator->execute_data) { zend_execute_data *execute_data = generator->execute_data; @@ -52,6 +52,42 @@ void zend_generator_close(zend_generator *generator TSRMLS_DC) /* {{{ */ zval_ptr_dtor(&execute_data->current_this); } + /* If the generator is closed before it can finish execution (reach + * a return statement) we have to free loop variables manually, as + * we don't know whether the SWITCH_FREE / FREE opcodes have run */ + if (!finished_execution) { + zend_op_array *op_array = execute_data->op_array; + zend_uint op_num = execute_data->opline - op_array->opcodes; + + int i; + for (i = 0; i < op_array->last_brk_cont; ++i) { + zend_brk_cont_element *brk_cont = op_array->brk_cont_array + i; + + if (brk_cont->start < 0) { + continue; + } else if (brk_cont->start > op_num) { + break; + } else if (brk_cont->brk > op_num) { + zend_op *brk_opline = op_array->opcodes + brk_cont->brk; + + switch (brk_opline->opcode) { + case ZEND_SWITCH_FREE: + { + temp_variable *var = (temp_variable *) ((char *) execute_data->Ts + brk_opline->op1.var); + zval_ptr_dtor(&var->var.ptr); + } + break; + case ZEND_FREE: + { + temp_variable *var = (temp_variable *) ((char *) execute_data->Ts + brk_opline->op1.var); + zval_dtor(&var->tmp_var); + } + break; + } + } + } + } + efree(execute_data); generator->execute_data = NULL; } @@ -66,7 +102,7 @@ void zend_generator_close(zend_generator *generator TSRMLS_DC) /* {{{ */ static void zend_generator_free_storage(zend_generator *generator TSRMLS_DC) /* {{{ */ { - zend_generator_close(generator TSRMLS_CC); + zend_generator_close(generator, 0 TSRMLS_CC); zend_object_std_dtor(&generator->std TSRMLS_CC); efree(generator); diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index 4becddf17c93..192e9e75c95f 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -35,7 +35,7 @@ typedef struct _zend_generator { extern ZEND_API zend_class_entry *zend_ce_generator; void zend_register_generator_ce(TSRMLS_D); -void zend_generator_close(zend_generator *generator TSRMLS_DC); +void zend_generator_close(zend_generator *generator, zend_bool finished_execution TSRMLS_DC); END_EXTERN_C() diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 42397ed278d5..440b36d04a43 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2845,7 +2845,7 @@ ZEND_VM_HANDLER(62, ZEND_RETURN, CONST|TMP|VAR|CV, ANY) zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); /* Close the generator to free up resources. */ - zend_generator_close(generator TSRMLS_CC); + zend_generator_close(generator, 1 TSRMLS_CC); /* Pass execution back to generator handling code */ ZEND_VM_RETURN(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index fa802c652b64..8f06afa9e14d 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -2331,7 +2331,7 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARG zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); /* Close the generator to free up resources. */ - zend_generator_close(generator TSRMLS_CC); + zend_generator_close(generator, 1 TSRMLS_CC); /* Pass execution back to generator handling code */ ZEND_VM_RETURN(); @@ -6915,7 +6915,7 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); /* Close the generator to free up resources. */ - zend_generator_close(generator TSRMLS_CC); + zend_generator_close(generator, 1 TSRMLS_CC); /* Pass execution back to generator handling code */ ZEND_VM_RETURN(); @@ -11404,7 +11404,7 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); /* Close the generator to free up resources. */ - zend_generator_close(generator TSRMLS_CC); + zend_generator_close(generator, 1 TSRMLS_CC); /* Pass execution back to generator handling code */ ZEND_VM_RETURN(); @@ -27449,7 +27449,7 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); /* Close the generator to free up resources. */ - zend_generator_close(generator TSRMLS_CC); + zend_generator_close(generator, 1 TSRMLS_CC); /* Pass execution back to generator handling code */ ZEND_VM_RETURN(); From 9f52c5c22647f0648dbb350c551417b1ad070359 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 27 May 2012 22:48:21 +0200 Subject: [PATCH 23/70] Fix generator creation when execute_data is not nested This happens primarily when the generator is invoked from some internal place like a dynamic function call. --- Zend/tests/generators/dynamic_call.phpt | 19 +++++++++++++++++++ Zend/zend_vm_def.h | 6 ++++-- Zend/zend_vm_execute.h | 6 ++++-- 3 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 Zend/tests/generators/dynamic_call.phpt diff --git a/Zend/tests/generators/dynamic_call.phpt b/Zend/tests/generators/dynamic_call.phpt new file mode 100644 index 000000000000..7b4b0c3e59ad --- /dev/null +++ b/Zend/tests/generators/dynamic_call.phpt @@ -0,0 +1,19 @@ +--TEST-- +It's possible to invoke a generator dynamically +--FILE-- + +--EXPECT-- +string(3) "bar" +string(3) "foo" diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 440b36d04a43..0b06c9217c33 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5265,10 +5265,12 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) EX(called_scope) = DECODE_CTOR(EX(called_scope)); zend_vm_stack_clear_multiple(TSRMLS_C); + + ZEND_VM_INC_OPCODE(); + ZEND_VM_LEAVE(); } - ZEND_VM_INC_OPCODE(); - ZEND_VM_LEAVE(); + ZEND_VM_RETURN(); } ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV, ANY) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 8f06afa9e14d..6879d9dcbb09 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1247,10 +1247,12 @@ static int ZEND_FASTCALL ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER(ZEND_OP EX(called_scope) = DECODE_CTOR(EX(called_scope)); zend_vm_stack_clear_multiple(TSRMLS_C); + + ZEND_VM_INC_OPCODE(); + ZEND_VM_LEAVE(); } - ZEND_VM_INC_OPCODE(); - ZEND_VM_LEAVE(); + ZEND_VM_RETURN(); } static int ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) From bcc7d976f31a572160a52c15f415deea3497ab68 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 28 May 2012 00:24:58 +0200 Subject: [PATCH 24/70] Set EG(current_execute_data) This fixes several issues. In particular it makes method generators work properly and also allows generators using a symbol table. --- Zend/tests/generators/generator_method.phpt | 29 +++++++++++++++++++++ Zend/zend_generators.c | 3 +++ 2 files changed, 32 insertions(+) create mode 100644 Zend/tests/generators/generator_method.phpt diff --git a/Zend/tests/generators/generator_method.phpt b/Zend/tests/generators/generator_method.phpt new file mode 100644 index 000000000000..b64476dc897a --- /dev/null +++ b/Zend/tests/generators/generator_method.phpt @@ -0,0 +1,29 @@ +--TEST-- +Methods can be generators +--FILE-- +data = $data; + } + + public function *getIterator() { + foreach ($this->data as $value) { + yield $value; + } + } +} + +$test = new Test(['foo', 'bar', 'baz']); +foreach ($test as $value) { + var_dump($value); +} + +?> +--EXPECT-- +string(3) "foo" +string(3) "bar" +string(3) "baz" diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 12116a0f109d..00d637ca6d8e 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -148,6 +148,7 @@ static void zend_generator_resume(zval *object, zend_generator *generator TSRMLS { /* Backup executor globals */ zval **original_return_value_ptr_ptr = EG(return_value_ptr_ptr); + zend_execute_data *original_execute_data = EG(current_execute_data); zend_op **original_opline_ptr = EG(opline_ptr); zend_op_array *original_active_op_array = EG(active_op_array); HashTable *original_active_symbol_table = EG(active_symbol_table); @@ -160,6 +161,7 @@ static void zend_generator_resume(zval *object, zend_generator *generator TSRMLS EG(return_value_ptr_ptr) = &object; /* Set executor globals */ + EG(current_execute_data) = generator->execute_data; EG(opline_ptr) = &generator->execute_data->opline; EG(active_op_array) = generator->execute_data->op_array; EG(active_symbol_table) = generator->execute_data->symbol_table; @@ -175,6 +177,7 @@ static void zend_generator_resume(zval *object, zend_generator *generator TSRMLS /* Restore executor globals */ EG(return_value_ptr_ptr) = original_return_value_ptr_ptr; + EG(current_execute_data) = original_execute_data; EG(opline_ptr) = original_opline_ptr; EG(active_op_array) = original_active_op_array; EG(active_symbol_table) = original_active_symbol_table; From 4aab08b64c2318775be6d8e629747ac66a108485 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 28 May 2012 06:43:18 +0200 Subject: [PATCH 25/70] Properly free resources when generator return value not used To keep things clean two new functions are introduced: zend_clean_and_cache_symbol_table(HashTable *symbol_table) zend_free_compiled_variables(zval ***CVs, int num) --- .../tests/generators/unused_return_value.phpt | 13 +++++++ Zend/zend_execute.c | 25 +++++++++++++ Zend/zend_execute.h | 3 ++ Zend/zend_generators.c | 7 +--- Zend/zend_vm_def.h | 36 +++++-------------- Zend/zend_vm_execute.h | 36 +++++-------------- 6 files changed, 60 insertions(+), 60 deletions(-) create mode 100644 Zend/tests/generators/unused_return_value.phpt diff --git a/Zend/tests/generators/unused_return_value.phpt b/Zend/tests/generators/unused_return_value.phpt new file mode 100644 index 000000000000..8d6deefb95b5 --- /dev/null +++ b/Zend/tests/generators/unused_return_value.phpt @@ -0,0 +1,13 @@ +--TEST-- +There shouldn't be any leaks when the genertor's return value isn't used +--FILE-- + +===DONE=== +--EXPECT-- +===DONE=== diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index d82abfc61384..9031fb5557c7 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1538,6 +1538,31 @@ ZEND_API zval **zend_get_zval_ptr_ptr(int op_type, const znode_op *node, const t return get_zval_ptr_ptr(op_type, node, Ts, should_free, type); } +void zend_clean_and_cache_symbol_table(HashTable *symbol_table) /* {{{ */ +{ + if (EG(symtable_cache_ptr) >= EG(symtable_cache_limit)) { + zend_hash_destroy(symbol_table); + FREE_HASHTABLE(symbol_table); + } else { + /* clean before putting into the cache, since clean + could call dtors, which could use cached hash */ + zend_hash_clean(symbol_table); + *(++EG(symtable_cache_ptr)) = symbol_table; + } +} +/* }}} */ + +void zend_free_compiled_variables(zval ***CVs, int num) /* {{{ */ +{ + int i; + for (i = 0; i < num; ++i) { + if (CVs[i]) { + zval_ptr_dtor(CVs[i]); + } + } +} +/* }}} */ + /* * Local variables: * tab-width: 4 diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 75a7eaa8a1ab..6dfd607d1bbb 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -432,6 +432,9 @@ ZEND_API zval **zend_get_zval_ptr_ptr(int op_type, const znode_op *node, const t ZEND_API int zend_do_fcall(ZEND_OPCODE_HANDLER_ARGS); +void zend_clean_and_cache_symbol_table(HashTable *symbol_table); +void zend_free_compiled_variables(zval ***CVs, int num); + #define CACHED_PTR(num) \ EG(active_op_array)->run_time_cache[(num)] diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 00d637ca6d8e..e4b070498b58 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -32,12 +32,7 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio zend_execute_data *execute_data = generator->execute_data; if (!execute_data->symbol_table) { - int i; - for (i = 0; i < execute_data->op_array->last_var; ++i) { - if (execute_data->CVs[i]) { - zval_ptr_dtor(execute_data->CVs[i]); - } - } + zend_free_compiled_variables(execute_data->CVs, execute_data->op_array->last_var); } else { if (EG(symtable_cache_ptr) >= EG(symtable_cache_limit)) { zend_hash_destroy(execute_data->symbol_table); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 0b06c9217c33..04b65950dc9a 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2514,14 +2514,7 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY) EG(current_execute_data) = EX(prev_execute_data); EG(opline_ptr) = NULL; if (!EG(active_symbol_table)) { - zval ***cv = EX_CVs(); - zval ***end = cv + op_array->last_var; - while (cv != end) { - if (*cv) { - zval_ptr_dtor(*cv); - } - cv++; - } + zend_free_compiled_variables(EX_CVs(), op_array->last_var); } if ((op_array->fn_flags & ZEND_ACC_CLOSURE) && op_array->prototype) { @@ -2579,15 +2572,7 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY) EG(active_op_array) = EX(op_array); EG(return_value_ptr_ptr) = EX(original_return_value); if (EG(active_symbol_table)) { - if (EG(symtable_cache_ptr)>=EG(symtable_cache_limit)) { - zend_hash_destroy(EG(active_symbol_table)); - FREE_HASHTABLE(EG(active_symbol_table)); - } else { - /* clean before putting into the cache, since clean - could call dtors, which could use cached hash */ - zend_hash_clean(EG(active_symbol_table)); - *(++EG(symtable_cache_ptr)) = EG(active_symbol_table); - } + zend_clean_and_cache_symbol_table(EG(active_symbol_table)); } EG(active_symbol_table) = EX(symbol_table); @@ -2732,15 +2717,7 @@ ZEND_VM_HELPER(zend_do_fcall_common_helper, ANY, ANY) EG(active_op_array) = EX(op_array); EG(return_value_ptr_ptr) = EX(original_return_value); if (EG(active_symbol_table)) { - if (EG(symtable_cache_ptr)>=EG(symtable_cache_limit)) { - zend_hash_destroy(EG(active_symbol_table)); - FREE_HASHTABLE(EG(active_symbol_table)); - } else { - /* clean before putting into the cache, since clean - could call dtors, which could use cached hash */ - zend_hash_clean(EG(active_symbol_table)); - *(++EG(symtable_cache_ptr)) = EG(active_symbol_table); - } + zend_clean_and_cache_symbol_table(EG(active_symbol_table)); } EG(active_symbol_table) = EX(symbol_table); } else { /* ZEND_OVERLOADED_FUNCTION */ @@ -5242,7 +5219,12 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) /* if there is no return value pointer we are responsible for freeing the * execution data */ if (!EG(return_value_ptr_ptr)) { - /* something has to be done in here, not sure yet what exactly */ + if (!EG(active_symbol_table)) { + zend_free_compiled_variables(EX_CVs(), execute_data->op_array->last_var); + } else { + zend_clean_and_cache_symbol_table(EG(active_symbol_table)); + } + efree(execute_data); } EG(opline_ptr) = NULL; diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 6879d9dcbb09..c195148c9760 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -497,14 +497,7 @@ static int ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS) EG(current_execute_data) = EX(prev_execute_data); EG(opline_ptr) = NULL; if (!EG(active_symbol_table)) { - zval ***cv = EX_CVs(); - zval ***end = cv + op_array->last_var; - while (cv != end) { - if (*cv) { - zval_ptr_dtor(*cv); - } - cv++; - } + zend_free_compiled_variables(EX_CVs(), op_array->last_var); } if ((op_array->fn_flags & ZEND_ACC_CLOSURE) && op_array->prototype) { @@ -562,15 +555,7 @@ static int ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS) EG(active_op_array) = EX(op_array); EG(return_value_ptr_ptr) = EX(original_return_value); if (EG(active_symbol_table)) { - if (EG(symtable_cache_ptr)>=EG(symtable_cache_limit)) { - zend_hash_destroy(EG(active_symbol_table)); - FREE_HASHTABLE(EG(active_symbol_table)); - } else { - /* clean before putting into the cache, since clean - could call dtors, which could use cached hash */ - zend_hash_clean(EG(active_symbol_table)); - *(++EG(symtable_cache_ptr)) = EG(active_symbol_table); - } + zend_clean_and_cache_symbol_table(EG(active_symbol_table)); } EG(active_symbol_table) = EX(symbol_table); @@ -715,15 +700,7 @@ static int ZEND_FASTCALL zend_do_fcall_common_helper_SPEC(ZEND_OPCODE_HANDLER_AR EG(active_op_array) = EX(op_array); EG(return_value_ptr_ptr) = EX(original_return_value); if (EG(active_symbol_table)) { - if (EG(symtable_cache_ptr)>=EG(symtable_cache_limit)) { - zend_hash_destroy(EG(active_symbol_table)); - FREE_HASHTABLE(EG(active_symbol_table)); - } else { - /* clean before putting into the cache, since clean - could call dtors, which could use cached hash */ - zend_hash_clean(EG(active_symbol_table)); - *(++EG(symtable_cache_ptr)) = EG(active_symbol_table); - } + zend_clean_and_cache_symbol_table(EG(active_symbol_table)); } EG(active_symbol_table) = EX(symbol_table); } else { /* ZEND_OVERLOADED_FUNCTION */ @@ -1224,7 +1201,12 @@ static int ZEND_FASTCALL ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER(ZEND_OP /* if there is no return value pointer we are responsible for freeing the * execution data */ if (!EG(return_value_ptr_ptr)) { - /* something has to be done in here, not sure yet what exactly */ + if (!EG(active_symbol_table)) { + zend_free_compiled_variables(EX_CVs(), execute_data->op_array->last_var); + } else { + zend_clean_and_cache_symbol_table(EG(active_symbol_table)); + } + efree(execute_data); } EG(opline_ptr) = NULL; From b770b221e0b3036708deb9e22dacf296402787f0 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 29 May 2012 02:31:56 +0200 Subject: [PATCH 26/70] Make the GOTO and SWITCH VMs work again --- Zend/zend_vm_def.h | 7 +++++++ Zend/zend_vm_execute.h | 28 ++++++++++++++++++++++++---- Zend/zend_vm_execute.skl | 7 +++---- Zend/zend_vm_gen.php | 12 +++++++----- 4 files changed, 41 insertions(+), 13 deletions(-) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 04b65950dc9a..47d5ec409884 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5203,6 +5203,7 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) *EG(return_value_ptr_ptr) = return_value; /* back up some executor globals */ + SAVE_OPLINE(); EX(current_this) = EG(This); EX(current_scope) = EG(scope); EX(current_called_scope) = EG(called_scope); @@ -5248,6 +5249,8 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) zend_vm_stack_clear_multiple(TSRMLS_C); + LOAD_REGS(); + LOAD_OPLINE(); ZEND_VM_INC_OPCODE(); ZEND_VM_LEAVE(); } @@ -5293,6 +5296,10 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV, ANY) FREE_OP1_IF_VAR(); } + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + ZEND_VM_RETURN(); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index c195148c9760..bd22768a3a21 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -393,10 +393,10 @@ static zend_execute_data *zend_create_execute_data_from_op_array(zend_op_array * if (op_array->this_var != -1 && EG(This)) { Z_ADDREF_P(EG(This)); /* For $this pointer */ if (!EG(active_symbol_table)) { - EX_CV(op_array->this_var) = (zval**)EX_CVs() + (op_array->last_var + op_array->this_var); - *EX_CV(op_array->this_var) = EG(This); + EX(CVs)[op_array->this_var] = (zval **) EX(CVs) + op_array->last_var + op_array->this_var; + *EX(CVs)[op_array->this_var] = EG(This); } else { - if (zend_hash_add(EG(active_symbol_table), "this", sizeof("this"), &EG(This), sizeof(zval *), (void**)&EX_CV(op_array->this_var))==FAILURE) { + if (zend_hash_add(EG(active_symbol_table), "this", sizeof("this"), &EG(This), sizeof(zval *), (void **) &EX(CVs)[op_array->this_var])==FAILURE) { Z_DELREF_P(EG(This)); } } @@ -425,7 +425,6 @@ ZEND_API void execute_ex(zend_execute_data *execute_data TSRMLS_DC) EG(in_execution) = 1; -zend_vm_enter: LOAD_REGS(); LOAD_OPLINE(); @@ -444,8 +443,10 @@ ZEND_API void execute_ex(zend_execute_data *execute_data TSRMLS_DC) return; case 2: execute_data = zend_create_execute_data_from_op_array(EG(active_op_array), 1 TSRMLS_CC); + break; case 3: execute_data = EG(current_execute_data); + break; default: break; } @@ -1185,6 +1186,7 @@ static int ZEND_FASTCALL ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER(ZEND_OP *EG(return_value_ptr_ptr) = return_value; /* back up some executor globals */ + SAVE_OPLINE(); EX(current_this) = EG(This); EX(current_scope) = EG(scope); EX(current_called_scope) = EG(called_scope); @@ -1230,6 +1232,8 @@ static int ZEND_FASTCALL ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER(ZEND_OP zend_vm_stack_clear_multiple(TSRMLS_C); + LOAD_REGS(); + LOAD_OPLINE(); ZEND_VM_INC_OPCODE(); ZEND_VM_LEAVE(); } @@ -3067,6 +3071,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS } + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + ZEND_VM_RETURN(); } @@ -7704,6 +7712,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) } + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + ZEND_VM_RETURN(); } @@ -12419,6 +12431,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; } + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + ZEND_VM_RETURN(); } @@ -28304,6 +28320,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) } + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + ZEND_VM_RETURN(); } diff --git a/Zend/zend_vm_execute.skl b/Zend/zend_vm_execute.skl index d251e55344e2..e77c7cb3c63b 100644 --- a/Zend/zend_vm_execute.skl +++ b/Zend/zend_vm_execute.skl @@ -54,10 +54,10 @@ static zend_execute_data *zend_create_execute_data_from_op_array(zend_op_array * if (op_array->this_var != -1 && EG(This)) { Z_ADDREF_P(EG(This)); /* For $this pointer */ if (!EG(active_symbol_table)) { - EX_CV(op_array->this_var) = (zval**)EX_CVs() + (op_array->last_var + op_array->this_var); - *EX_CV(op_array->this_var) = EG(This); + EX(CVs)[op_array->this_var] = (zval **) EX(CVs) + op_array->last_var + op_array->this_var; + *EX(CVs)[op_array->this_var] = EG(This); } else { - if (zend_hash_add(EG(active_symbol_table), "this", sizeof("this"), &EG(This), sizeof(zval *), (void**)&EX_CV(op_array->this_var))==FAILURE) { + if (zend_hash_add(EG(active_symbol_table), "this", sizeof("this"), &EG(This), sizeof(zval *), (void **) &EX(CVs)[op_array->this_var])==FAILURE) { Z_DELREF_P(EG(This)); } } @@ -88,7 +88,6 @@ ZEND_API void {%EXECUTOR_NAME%}_ex(zend_execute_data *execute_data TSRMLS_DC) EG(in_execution) = 1; -zend_vm_enter: LOAD_REGS(); LOAD_OPLINE(); diff --git a/Zend/zend_vm_gen.php b/Zend/zend_vm_gen.php index b7fd874264fc..ee2b0b2f2da2 100644 --- a/Zend/zend_vm_gen.php +++ b/Zend/zend_vm_gen.php @@ -856,7 +856,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name, out($f,"#define LOAD_REGS() do {Ts = EX(Ts); CVs = EX(CVs);} while (0)\n"); out($f,"#define ZEND_VM_CONTINUE() goto zend_vm_continue\n"); out($f,"#define ZEND_VM_RETURN() EG(in_execution) = original_in_execution; return\n"); - out($f,"#define ZEND_VM_ENTER() op_array = EG(active_op_array); goto zend_vm_enter\n"); + out($f,"#define ZEND_VM_ENTER() execute_data = zend_create_execute_data_from_op_array(EG(active_op_array), 1 TSRMLS_CC); LOAD_REGS(); LOAD_OPLINE(); ZEND_VM_CONTINUE()\n"); out($f,"#define ZEND_VM_LEAVE() ZEND_VM_CONTINUE()\n"); out($f,"#define ZEND_VM_DISPATCH(opcode, opline) dispatch_handler = zend_vm_get_opcode_handler(opcode, opline); goto zend_vm_dispatch;\n\n"); out($f,"#define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_INTERNAL execute_data TSRMLS_CC\n"); @@ -892,7 +892,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name, out($f,"#define LOAD_REGS() do {Ts = EX(Ts); CVs = EX(CVs);} while (0)\n"); out($f,"#define ZEND_VM_CONTINUE() goto *(void**)(OPLINE->handler)\n"); out($f,"#define ZEND_VM_RETURN() EG(in_execution) = original_in_execution; return\n"); - out($f,"#define ZEND_VM_ENTER() op_array = EG(active_op_array); goto zend_vm_enter\n"); + out($f,"#define ZEND_VM_ENTER() execute_data = zend_create_execute_data_from_op_array(EG(active_op_array), 1 TSRMLS_CC); LOAD_REGS(); LOAD_OPLINE(); ZEND_VM_CONTINUE()\n"); out($f,"#define ZEND_VM_LEAVE() ZEND_VM_CONTINUE()\n"); out($f,"#define ZEND_VM_DISPATCH(opcode, opline) goto *(void**)(zend_vm_get_opcode_handler(opcode, opline));\n\n"); out($f,"#define ZEND_OPCODE_HANDLER_ARGS_PASSTHRU_INTERNAL execute_data TSRMLS_CC\n"); @@ -932,7 +932,7 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name, // Emit array of labels of opcode handlers and code for // zend_opcode_handlers initialization $prolog = $m[1]; - out($f,$prolog."if (op_array == NULL) {\n"); + out($f,$prolog."if (execute_data == NULL) {\n"); out($f,$prolog."\tstatic const opcode_handler_t labels[] = {\n"); gen_labels($f, $spec, $kind, $prolog."\t\t"); out($f,$prolog."\t};\n"); @@ -977,8 +977,10 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name, $m[1]."\t\treturn;\n". $m[1]."\tcase 2:\n" . $m[1]."\t\texecute_data = zend_create_execute_data_from_op_array(EG(active_op_array), 1 TSRMLS_CC);\n". + $m[1]."\t\tbreak;\n" . $m[1]."\tcase 3:\n" . $m[1]."\t\texecute_data = EG(current_execute_data);\n". + $m[1]."\t\tbreak;\n" . $m[1]."\tdefault:\n". $m[1]."\t\tbreak;\n". $m[1]."}".$m[3]."\n"); @@ -1005,9 +1007,9 @@ function gen_executor($f, $skl, $spec, $kind, $executor_name, $initializer_name, $prolog = $m[1]; if ($kind == ZEND_VM_KIND_GOTO) { // Labels are defined in the executor itself, so we call it - // with op_array NULL and it sets zend_opcode_handlers array + // with execute_data NULL and it sets zend_opcode_handlers array out($f,$prolog."TSRMLS_FETCH();\n"); - out($f,$prolog."zend_execute(NULL TSRMLS_CC);\n"); + out($f,$prolog.$executor_name."_ex(NULL TSRMLS_CC);\n"); } else { if ($old) { // Reserving space for user-defined opcodes From 3600914ced52eb4f6db10410ba887c8e2a2acfe1 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 29 May 2012 17:34:33 +0200 Subject: [PATCH 27/70] Add support for $generator->send() Yield now is an expression and the return value is the value passed to $generator->send(). By default (i.e. if ->next() is called) the value is NULL. Unlike in Python ->send() can be run without priming the generator with a ->next() call first. --- Zend/tests/generators/generator_send.phpt | 22 ++++++++++++ Zend/zend_compile.c | 6 +++- Zend/zend_compile.h | 2 +- Zend/zend_generators.c | 32 +++++++++++++++++ Zend/zend_generators.h | 2 ++ Zend/zend_language_parser.y | 5 +-- Zend/zend_vm_def.h | 11 +++++- Zend/zend_vm_execute.h | 44 ++++++++++++++++++++--- 8 files changed, 115 insertions(+), 9 deletions(-) create mode 100644 Zend/tests/generators/generator_send.phpt diff --git a/Zend/tests/generators/generator_send.phpt b/Zend/tests/generators/generator_send.phpt new file mode 100644 index 000000000000..11ac37f846e0 --- /dev/null +++ b/Zend/tests/generators/generator_send.phpt @@ -0,0 +1,22 @@ +--TEST-- +Values can be sent back to the generator +--FILE-- +current()); +$gen->send("send bar"); +var_dump($gen->current()); +$gen->send("send foo"); + +?> +--EXPECT-- +string(9) "yield foo" +string(8) "send bar" +string(9) "yield bar" +string(8) "send foo" diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 30fecf1e2e19..37e49014ec66 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2662,7 +2662,7 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */ } /* }}} */ -void zend_do_yield(znode *expr TSRMLS_DC) /* {{{ */ +void zend_do_yield(znode *result, const znode *expr TSRMLS_DC) /* {{{ */ { zend_op *opline; @@ -2675,6 +2675,10 @@ void zend_do_yield(znode *expr TSRMLS_DC) /* {{{ */ opline->opcode = ZEND_YIELD; SET_NODE(opline->op1, expr); SET_UNUSED(opline->op2); + + opline->result_type = IS_VAR; + opline->result.var = get_temporary_variable(CG(active_op_array)); + GET_NODE(result, opline->result); } /* }}} */ diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 5365f9693dbb..953a9f1df02c 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -490,7 +490,7 @@ void zend_do_build_full_name(znode *result, znode *prefix, znode *name, int is_c int zend_do_begin_class_member_function_call(znode *class_name, znode *method_name TSRMLS_DC); void zend_do_end_function_call(znode *function_name, znode *result, const znode *argument_list, int is_method, int is_dynamic_fcall TSRMLS_DC); void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC); -void zend_do_yield(znode *expr TSRMLS_DC); +void zend_do_yield(znode *result, const znode *expr TSRMLS_DC); void zend_do_suspend_if_generator(TSRMLS_D); void zend_do_handle_exception(TSRMLS_D); diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index e4b070498b58..01c9aa319812 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -292,15 +292,47 @@ ZEND_METHOD(Generator, next) } /* }}} */ +/* {{{ proto void Generator::send() + * Sends a value to the generator */ +ZEND_METHOD(Generator, send) +{ + zval *object, *value; + zend_generator *generator; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) { + return; + } + + object = getThis(); + generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); + + zend_generator_ensure_initialized(object, generator TSRMLS_CC); + + /* The sent value was initialized to NULL, so dtor that */ + zval_ptr_dtor(generator->send_target->var.ptr_ptr); + + /* Set new sent value */ + Z_ADDREF_P(value); + generator->send_target->var.ptr = value; + generator->send_target->var.ptr_ptr = &value; + + zend_generator_resume(object, generator TSRMLS_CC); +} + ZEND_BEGIN_ARG_INFO(arginfo_generator_void, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_generator_send, 0, 0, 1) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + static const zend_function_entry generator_functions[] = { ZEND_ME(Generator, rewind, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_ME(Generator, valid, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_ME(Generator, current, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_ME(Generator, key, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_ME(Generator, next, arginfo_generator_void, ZEND_ACC_PUBLIC) + ZEND_ME(Generator, send, arginfo_generator_send, ZEND_ACC_PUBLIC) ZEND_FE_END }; diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index 192e9e75c95f..c9b43b31b657 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -30,6 +30,8 @@ typedef struct _zend_generator { zend_execute_data *execute_data; /* Current value */ zval *value; + /* Variable to put sent value into */ + temp_variable *send_target; } zend_generator; extern ZEND_API zend_class_entry *zend_ce_generator; diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 2045a5e46539..ea8ac41795af 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -68,6 +68,8 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %token T_LOGICAL_AND "and (T_LOGICAL_AND)" %right T_PRINT %token T_PRINT "print (T_PRINT)" +%right T_YIELD +%token T_YIELD "yield (T_YIELD)" %left '=' T_PLUS_EQUAL T_MINUS_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL T_SL_EQUAL T_SR_EQUAL %token T_PLUS_EQUAL "+= (T_PLUS_EQUAL)" %token T_MINUS_EQUAL "-= (T_MINUS_EQUAL)" @@ -158,7 +160,6 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %token T_FUNCTION "function (T_FUNCTION)" %token T_CONST "const (T_CONST)" %token T_RETURN "return (T_RETURN)" -%token T_YIELD "yield (T_YIELD)" %token T_TRY "try (T_TRY)" %token T_CATCH "catch (T_CATCH)" %token T_THROW "throw (T_THROW)" @@ -298,7 +299,6 @@ unticked_statement: | T_RETURN ';' { zend_do_return(NULL, 0 TSRMLS_CC); } | T_RETURN expr_without_variable ';' { zend_do_return(&$2, 0 TSRMLS_CC); } | T_RETURN variable ';' { zend_do_return(&$2, 1 TSRMLS_CC); } - | T_YIELD expr ';' { zend_do_yield(&$2 TSRMLS_CC); } | T_GLOBAL global_var_list ';' | T_STATIC static_var_list ';' | T_ECHO echo_expr_list ';' @@ -801,6 +801,7 @@ expr_without_variable: | combined_scalar { $$ = $1; } | '`' backticks_expr '`' { zend_do_shell_exec(&$$, &$2 TSRMLS_CC); } | T_PRINT expr { zend_do_print(&$$, &$2 TSRMLS_CC); } + | T_YIELD expr { zend_do_yield(&$$, &$2 TSRMLS_CC); } | function is_generator is_reference { zend_do_begin_lambda_function_declaration(&$$, &$1, $2.op_type, $3.op_type, 0 TSRMLS_CC); } '(' parameter_list ')' lexical_vars { zend_do_suspend_if_generator(TSRMLS_C); } '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); $$ = $4; } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 47d5ec409884..8c810cdee81d 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5260,6 +5260,8 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV, ANY) { + USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); @@ -5268,8 +5270,8 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV, ANY) zval_ptr_dtor(&generator->value); } + /* Set the new yielded value */ { - USE_OPLINE zend_free_op free_op1; zval *value = GET_OP1_ZVAL_PTR(BP_VAR_R); @@ -5296,6 +5298,13 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV, ANY) FREE_OP1_IF_VAR(); } + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index bd22768a3a21..fa0773320612 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3036,6 +3036,8 @@ static int ZEND_FASTCALL ZEND_QM_ASSIGN_VAR_SPEC_CONST_HANDLER(ZEND_OPCODE_HAND static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { + USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); @@ -3044,8 +3046,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS zval_ptr_dtor(&generator->value); } + /* Set the new yielded value */ { - USE_OPLINE zval *value = opline->op1.zv; @@ -3071,6 +3073,13 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS } + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -7677,6 +7686,8 @@ static int ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_A static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { + USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); @@ -7685,8 +7696,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) zval_ptr_dtor(&generator->value); } + /* Set the new yielded value */ { - USE_OPLINE zend_free_op free_op1; zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); @@ -7712,6 +7723,13 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) } + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -12395,6 +12413,8 @@ static int ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_A static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { + USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); @@ -12403,8 +12423,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) zval_ptr_dtor(&generator->value); } + /* Set the new yielded value */ { - USE_OPLINE zend_free_op free_op1; zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); @@ -12431,6 +12451,13 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; } + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -28285,6 +28312,8 @@ static int ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_AR static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { + USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); @@ -28293,8 +28322,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) zval_ptr_dtor(&generator->value); } + /* Set the new yielded value */ { - USE_OPLINE zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); @@ -28320,6 +28349,13 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) } + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); From ad525c288ad83df497ed1a0668915cad61d72e26 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 29 May 2012 17:53:11 +0200 Subject: [PATCH 28/70] Allow to use yield without value If the generator is used as a coroutine it often doesn't make sense to yield anything. In this case one can simply receive values using $value = yield; The yield here will simply yield NULL. --- .../tests/generators/yield_without_value.phpt | 27 +++++ Zend/zend_compile.c | 8 +- Zend/zend_language_parser.y | 1 + Zend/zend_vm_def.h | 10 +- Zend/zend_vm_execute.h | 99 ++++++++++++++++--- 5 files changed, 127 insertions(+), 18 deletions(-) create mode 100644 Zend/tests/generators/yield_without_value.phpt diff --git a/Zend/tests/generators/yield_without_value.phpt b/Zend/tests/generators/yield_without_value.phpt new file mode 100644 index 000000000000..dc467a83bd13 --- /dev/null +++ b/Zend/tests/generators/yield_without_value.phpt @@ -0,0 +1,27 @@ +--TEST-- +yield can be used without a value +--FILE-- +current()); +$reciever->send(1); +var_dump($reciever->current()); +$reciever->send(2); +var_dump($reciever->current()); +$reciever->send(3); + +?> +--EXPECT-- +NULL +int(1) +NULL +int(2) +NULL +int(3) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 37e49014ec66..89549bb6edd7 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2673,8 +2673,12 @@ void zend_do_yield(znode *result, const znode *expr TSRMLS_DC) /* {{{ */ opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = ZEND_YIELD; - SET_NODE(opline->op1, expr); - SET_UNUSED(opline->op2); + + if (expr) { + SET_NODE(opline->op1, expr); + } else { + SET_UNUSED(opline->op2); + } opline->result_type = IS_VAR; opline->result.var = get_temporary_variable(CG(active_op_array)); diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index ea8ac41795af..ac22e7f73323 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -801,6 +801,7 @@ expr_without_variable: | combined_scalar { $$ = $1; } | '`' backticks_expr '`' { zend_do_shell_exec(&$$, &$2 TSRMLS_CC); } | T_PRINT expr { zend_do_print(&$$, &$2 TSRMLS_CC); } + | T_YIELD { zend_do_yield(&$$, NULL TSRMLS_CC); } | T_YIELD expr { zend_do_yield(&$$, &$2 TSRMLS_CC); } | function is_generator is_reference { zend_do_begin_lambda_function_declaration(&$$, &$1, $2.op_type, $3.op_type, 0 TSRMLS_CC); } '(' parameter_list ')' lexical_vars { zend_do_suspend_if_generator(TSRMLS_C); } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 8c810cdee81d..67610309a547 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5258,7 +5258,7 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) ZEND_VM_RETURN(); } -ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV, ANY) +ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, ANY) { USE_OPLINE @@ -5271,7 +5271,7 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV, ANY) } /* Set the new yielded value */ - { + if (OP1_TYPE != IS_UNUSED) { zend_free_op free_op1; zval *value = GET_OP1_ZVAL_PTR(BP_VAR_R); @@ -5291,11 +5291,15 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV, ANY) generator->value = copy; } else { - generator->value = value; Z_ADDREF_P(value); + generator->value = value; } FREE_OP1_IF_VAR(); + } else { + /* If no value way specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); } /* If a value is sent it should go into the result var */ diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index fa0773320612..efe0812ca90d 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3047,7 +3047,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS } /* Set the new yielded value */ - { + if (IS_CONST != IS_UNUSED) { zval *value = opline->op1.zv; @@ -3067,10 +3067,14 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS generator->value = copy; } else { - generator->value = value; Z_ADDREF_P(value); + generator->value = value; } + } else { + /* If no value way specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); } /* If a value is sent it should go into the result var */ @@ -7697,7 +7701,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) } /* Set the new yielded value */ - { + if (IS_TMP_VAR != IS_UNUSED) { zend_free_op free_op1; zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); @@ -7717,10 +7721,14 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) generator->value = copy; } else { - generator->value = value; Z_ADDREF_P(value); + generator->value = value; } + } else { + /* If no value way specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); } /* If a value is sent it should go into the result var */ @@ -12424,7 +12432,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) } /* Set the new yielded value */ - { + if (IS_VAR != IS_UNUSED) { zend_free_op free_op1; zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); @@ -12444,11 +12452,15 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) generator->value = copy; } else { - generator->value = value; Z_ADDREF_P(value); + generator->value = value; } if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + } else { + /* If no value way specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); } /* If a value is sent it should go into the result var */ @@ -22080,6 +22092,63 @@ static int ZEND_FASTCALL ZEND_EXIT_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS ZEND_VM_NEXT_OPCODE(); /* Never reached */ } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Set the new yielded value */ + if (IS_UNUSED != IS_UNUSED) { + + zval *value = NULL; + + /* Consts, temporary variables and references need copying */ + if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } else { + /* If no value way specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC), ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -28323,7 +28392,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) } /* Set the new yielded value */ - { + if (IS_CV != IS_UNUSED) { zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); @@ -28343,10 +28412,14 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) generator->value = copy; } else { - generator->value = value; Z_ADDREF_P(value); + generator->value = value; } + } else { + /* If no value way specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); } /* If a value is sent it should go into the result var */ @@ -41168,11 +41241,11 @@ void zend_init_opcodes_handlers(void) ZEND_YIELD_SPEC_VAR_HANDLER, ZEND_YIELD_SPEC_VAR_HANDLER, ZEND_YIELD_SPEC_VAR_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, + ZEND_YIELD_SPEC_UNUSED_HANDLER, + ZEND_YIELD_SPEC_UNUSED_HANDLER, + ZEND_YIELD_SPEC_UNUSED_HANDLER, + ZEND_YIELD_SPEC_UNUSED_HANDLER, + ZEND_YIELD_SPEC_UNUSED_HANDLER, ZEND_YIELD_SPEC_CV_HANDLER, ZEND_YIELD_SPEC_CV_HANDLER, ZEND_YIELD_SPEC_CV_HANDLER, From 12e928314fb270db31adc361ac4993b4f0fe000a Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 29 May 2012 18:01:08 +0200 Subject: [PATCH 29/70] Fix segfault when send()ing to a closed generator --- Zend/tests/generators/send_after_close.phpt | 14 ++++++++++++++ Zend/zend_generators.c | 5 +++++ 2 files changed, 19 insertions(+) create mode 100644 Zend/tests/generators/send_after_close.phpt diff --git a/Zend/tests/generators/send_after_close.phpt b/Zend/tests/generators/send_after_close.phpt new file mode 100644 index 000000000000..6a251b24816e --- /dev/null +++ b/Zend/tests/generators/send_after_close.phpt @@ -0,0 +1,14 @@ +--TEST-- +Calls to send() after close should do nothing +--FILE-- +send("Test"); + +?> +===DONE=== +--EXPECT-- +===DONE=== diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 01c9aa319812..0cf600a922a8 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -308,6 +308,11 @@ ZEND_METHOD(Generator, send) zend_generator_ensure_initialized(object, generator TSRMLS_CC); + /* The generator is already closed, thus can't send anything */ + if (!generator->execute_data) { + return; + } + /* The sent value was initialized to NULL, so dtor that */ zval_ptr_dtor(generator->send_target->var.ptr_ptr); From 72a91d08e7d70d5524feb6cc7c8e32b3bd68f1df Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 29 May 2012 18:11:18 +0200 Subject: [PATCH 30/70] Add $generator->close() method Calling $generator->close() is equivalent to executing a return statement at the current position in the generator. --- Zend/tests/generators/generator_close.phpt | 32 ++++++++++++++++++++++ Zend/zend_generators.c | 17 ++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 Zend/tests/generators/generator_close.phpt diff --git a/Zend/tests/generators/generator_close.phpt b/Zend/tests/generators/generator_close.phpt new file mode 100644 index 000000000000..003eef094dcc --- /dev/null +++ b/Zend/tests/generators/generator_close.phpt @@ -0,0 +1,32 @@ +--TEST-- +Generator can be closed by calling ->close() +--FILE-- +close(); + } +} + +?> +--EXPECT-- +int(0) +int(1) +int(2) +int(3) +int(4) +int(5) +int(6) +int(7) +int(8) +int(9) diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 0cf600a922a8..b2fe8af85468 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -324,6 +324,22 @@ ZEND_METHOD(Generator, send) zend_generator_resume(object, generator TSRMLS_CC); } +/* {{{ proto void Generator::close() + * Closes the generator */ +ZEND_METHOD(Generator, close) +{ + zend_generator *generator; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); + + zend_generator_close(generator, 0); +} +/* }}} */ + ZEND_BEGIN_ARG_INFO(arginfo_generator_void, 0) ZEND_END_ARG_INFO() @@ -338,6 +354,7 @@ static const zend_function_entry generator_functions[] = { ZEND_ME(Generator, key, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_ME(Generator, next, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_ME(Generator, send, arginfo_generator_send, ZEND_ACC_PUBLIC) + ZEND_ME(Generator, close, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_FE_END }; From bc08c2cf9485e20fea0eef7ab149cefdf9a3662e Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 30 May 2012 02:44:06 +0200 Subject: [PATCH 31/70] Add support for yielding keys Keys are yielded using the yield $key => $value syntax. Currently this is implemented as a statement only and not as an expression, because conflicts arise considering nesting and use in arrays: yield yield $a => $b; // could be either yield (yield $a) => $b; // or yield (yield $a => $b); Once I find some way to resolve these conflicts this should be available as an expression too. Also the key yielding code is rather copy-and-past-y for the value yielding code, so that should be factored out. --- .../tests/generators/generator_with_keys.phpt | 26 + Zend/zend_compile.c | 12 +- Zend/zend_compile.h | 2 +- Zend/zend_generators.c | 8 + Zend/zend_generators.h | 2 + Zend/zend_language_parser.y | 5 +- Zend/zend_vm_def.h | 41 +- Zend/zend_vm_execute.h | 2671 +++++++++++++++-- 8 files changed, 2448 insertions(+), 319 deletions(-) create mode 100644 Zend/tests/generators/generator_with_keys.phpt diff --git a/Zend/tests/generators/generator_with_keys.phpt b/Zend/tests/generators/generator_with_keys.phpt new file mode 100644 index 000000000000..43e3e5ecf7a5 --- /dev/null +++ b/Zend/tests/generators/generator_with_keys.phpt @@ -0,0 +1,26 @@ +--TEST-- +Generators can also yield keys +--FILE-- + current($array); + prev($array); + } +} + +$array = [ + 'foo' => 'bar', + 'bar' => 'foo', +]; + +foreach (reverse($array) as $key => $value) { + echo $key, ' => ', $value, "\n"; +} + +?> +--EXPECT-- +bar => foo +foo => bar diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 89549bb6edd7..da61b766622f 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2662,7 +2662,7 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */ } /* }}} */ -void zend_do_yield(znode *result, const znode *expr TSRMLS_DC) /* {{{ */ +void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC) /* {{{ */ { zend_op *opline; @@ -2674,8 +2674,14 @@ void zend_do_yield(znode *result, const znode *expr TSRMLS_DC) /* {{{ */ opline->opcode = ZEND_YIELD; - if (expr) { - SET_NODE(opline->op1, expr); + if (value) { + SET_NODE(opline->op1, value); + } else { + SET_UNUSED(opline->op1); + } + + if (key) { + SET_NODE(opline->op2, key); } else { SET_UNUSED(opline->op2); } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 953a9f1df02c..ec86ed86de0e 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -490,7 +490,7 @@ void zend_do_build_full_name(znode *result, znode *prefix, znode *name, int is_c int zend_do_begin_class_member_function_call(znode *class_name, znode *method_name TSRMLS_DC); void zend_do_end_function_call(znode *function_name, znode *result, const znode *argument_list, int is_method, int is_dynamic_fcall TSRMLS_DC); void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC); -void zend_do_yield(znode *result, const znode *expr TSRMLS_DC); +void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC); void zend_do_suspend_if_generator(TSRMLS_D); void zend_do_handle_exception(TSRMLS_D); diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index b2fe8af85468..d3d4b3f7a81b 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -92,6 +92,10 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio generator->value = NULL; } + if (generator->key) { + zval_ptr_dtor(&generator->key); + generator->key = NULL; + } } /* }}} */ @@ -269,6 +273,10 @@ ZEND_METHOD(Generator, key) generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); zend_generator_ensure_initialized(object, generator TSRMLS_CC); + + if (generator->key) { + RETURN_ZVAL(generator->key, 1, 0); + } } /* }}} */ diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index c9b43b31b657..2fd2c93b6362 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -30,6 +30,8 @@ typedef struct _zend_generator { zend_execute_data *execute_data; /* Current value */ zval *value; + /* Current key */ + zval *key; /* Variable to put sent value into */ temp_variable *send_target; } zend_generator; diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index ac22e7f73323..16c0ea2b2c37 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -299,6 +299,7 @@ unticked_statement: | T_RETURN ';' { zend_do_return(NULL, 0 TSRMLS_CC); } | T_RETURN expr_without_variable ';' { zend_do_return(&$2, 0 TSRMLS_CC); } | T_RETURN variable ';' { zend_do_return(&$2, 1 TSRMLS_CC); } + | T_YIELD expr T_DOUBLE_ARROW expr ';' { zend_do_yield(&$$, &$4, &$2 TSRMLS_CC); } | T_GLOBAL global_var_list ';' | T_STATIC static_var_list ';' | T_ECHO echo_expr_list ';' @@ -801,8 +802,8 @@ expr_without_variable: | combined_scalar { $$ = $1; } | '`' backticks_expr '`' { zend_do_shell_exec(&$$, &$2 TSRMLS_CC); } | T_PRINT expr { zend_do_print(&$$, &$2 TSRMLS_CC); } - | T_YIELD { zend_do_yield(&$$, NULL TSRMLS_CC); } - | T_YIELD expr { zend_do_yield(&$$, &$2 TSRMLS_CC); } + | T_YIELD { zend_do_yield(&$$, NULL, NULL TSRMLS_CC); } + | T_YIELD expr { zend_do_yield(&$$, &$2, NULL TSRMLS_CC); } | function is_generator is_reference { zend_do_begin_lambda_function_declaration(&$$, &$1, $2.op_type, $3.op_type, 0 TSRMLS_CC); } '(' parameter_list ')' lexical_vars { zend_do_suspend_if_generator(TSRMLS_C); } '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); $$ = $4; } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 67610309a547..36b9c93c5258 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5258,7 +5258,7 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) ZEND_VM_RETURN(); } -ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, ANY) +ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSED) { USE_OPLINE @@ -5270,6 +5270,11 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, ANY) zval_ptr_dtor(&generator->value); } + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + /* Set the new yielded value */ if (OP1_TYPE != IS_UNUSED) { zend_free_op free_op1; @@ -5297,11 +5302,43 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, ANY) FREE_OP1_IF_VAR(); } else { - /* If no value way specified yield null */ + /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); generator->value = &EG(uninitialized_zval); } + /* Set the new yielded key */ + if (OP2_TYPE != IS_UNUSED) { + zend_free_op free_op2; + zval *key = GET_OP2_ZVAL_PTR(BP_VAR_R); + + /* Consts, temporary variables and references need copying */ + if (OP2_TYPE == IS_CONST || OP2_TYPE == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!IS_OP1_TMP_FREE()) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + FREE_OP2_IF_VAR(); + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + /* If a value is sent it should go into the result var */ generator->send_target = &EX_T(opline->result.var); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index efe0812ca90d..ac7ff7ff585f 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3034,63 +3034,6 @@ static int ZEND_FASTCALL ZEND_QM_ASSIGN_VAR_SPEC_CONST_HANDLER(ZEND_OPCODE_HAND ZEND_VM_NEXT_OPCODE(); } -static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - USE_OPLINE - - /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); - - /* Destroy the previously yielded value */ - if (generator->value) { - zval_ptr_dtor(&generator->value); - } - - /* Set the new yielded value */ - if (IS_CONST != IS_UNUSED) { - - zval *value = opline->op1.zv; - - /* Consts, temporary variables and references need copying */ - if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; - - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); - - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } - - generator->value = copy; - } else { - Z_ADDREF_P(value); - generator->value = value; - } - - } else { - /* If no value way specified yield null */ - Z_ADDREF(EG(uninitialized_zval)); - generator->value = &EG(uninitialized_zval); - } - - /* If a value is sent it should go into the result var */ - generator->send_target = &EX_T(opline->result.var); - - /* Initialize the sent value to NULL */ - Z_ADDREF(EG(uninitialized_zval)); - AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); - - /* The GOTO VM uses a local opline variable. We need to set the opline - * variable in execute_data so we don't resume at an old position. */ - SAVE_OPLINE(); - - ZEND_VM_RETURN(); -} - static int ZEND_FASTCALL ZEND_ADD_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -4111,6 +4054,99 @@ static int ZEND_FASTCALL ZEND_DECLARE_CONST_SPEC_CONST_CONST_HANDLER(ZEND_OPCOD ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_CONST != IS_UNUSED) { + + zval *value = opline->op1.zv; + + /* Consts, temporary variables and references need copying */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_CONST != IS_UNUSED) { + + zval *key = opline->op2.zv; + + /* Consts, temporary variables and references need copying */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_CONST_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -4651,6 +4687,99 @@ static int ZEND_FASTCALL ZEND_INIT_ARRAY_SPEC_CONST_TMP_HANDLER(ZEND_OPCODE_HAN } } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_CONST != IS_UNUSED) { + + zval *value = opline->op1.zv; + + /* Consts, temporary variables and references need copying */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_TMP_VAR != IS_UNUSED) { + zend_free_op free_op2; + zval *key = _get_zval_ptr_tmp(opline->op2.var, EX_Ts(), &free_op2 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -5516,6 +5645,100 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_CONST_VAR_HANDLER(ZEND_OPC ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_CONST != IS_UNUSED) { + + zval *value = opline->op1.zv; + + /* Consts, temporary variables and references need copying */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_VAR != IS_UNUSED) { + zend_free_op free_op2; + zval *key = _get_zval_ptr_var(opline->op2.var, EX_Ts(), &free_op2 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + if (free_op2.var) {zval_ptr_dtor(&free_op2.var);}; + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_CONST_UNUSED(int type, ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -6075,6 +6298,99 @@ static int ZEND_FASTCALL ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_UNUSED_HANDLER ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_CONST != IS_UNUSED) { + + zval *value = opline->op1.zv; + + /* Consts, temporary variables and references need copying */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_UNUSED != IS_UNUSED) { + + zval *key = NULL; + + /* Consts, temporary variables and references need copying */ + if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -6674,6 +6990,99 @@ static int ZEND_FASTCALL ZEND_INIT_ARRAY_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HAND } } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_CONST != IS_UNUSED) { + + zval *value = opline->op1.zv; + + /* Consts, temporary variables and references need copying */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_CV != IS_UNUSED) { + + zval *key = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op2.var TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_BW_NOT_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -7688,63 +8097,6 @@ static int ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_A ZEND_VM_NEXT_OPCODE(); } -static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - USE_OPLINE - - /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); - - /* Destroy the previously yielded value */ - if (generator->value) { - zval_ptr_dtor(&generator->value); - } - - /* Set the new yielded value */ - if (IS_TMP_VAR != IS_UNUSED) { - zend_free_op free_op1; - zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); - - /* Consts, temporary variables and references need copying */ - if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; - - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); - - /* Temporary variables don't need ctor copying */ - if (!1) { - zval_copy_ctor(copy); - } - - generator->value = copy; - } else { - Z_ADDREF_P(value); - generator->value = value; - } - - } else { - /* If no value way specified yield null */ - Z_ADDREF(EG(uninitialized_zval)); - generator->value = &EG(uninitialized_zval); - } - - /* If a value is sent it should go into the result var */ - generator->send_target = &EX_T(opline->result.var); - - /* Initialize the sent value to NULL */ - Z_ADDREF(EG(uninitialized_zval)); - AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); - - /* The GOTO VM uses a local opline variable. We need to set the opline - * variable in execute_data so we don't resume at an old position. */ - SAVE_OPLINE(); - - ZEND_VM_RETURN(); -} - static int ZEND_FASTCALL ZEND_ADD_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -8633,6 +8985,99 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_TMP_CONST_HANDLER(ZEND_OPC ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_TMP_VAR != IS_UNUSED) { + zend_free_op free_op1; + zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!1) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_CONST != IS_UNUSED) { + + zval *key = opline->op2.zv; + + /* Consts, temporary variables and references need copying */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!1) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_TMP_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -9173,6 +9618,99 @@ static int ZEND_FASTCALL ZEND_INIT_ARRAY_SPEC_TMP_TMP_HANDLER(ZEND_OPCODE_HANDL } } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_TMP_VAR != IS_UNUSED) { + zend_free_op free_op1; + zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!1) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_TMP_VAR != IS_UNUSED) { + zend_free_op free_op2; + zval *key = _get_zval_ptr_tmp(opline->op2.var, EX_Ts(), &free_op2 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!1) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_TMP_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -10038,6 +10576,100 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_TMP_VAR_HANDLER(ZEND_OPCOD ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_TMP_VAR != IS_UNUSED) { + zend_free_op free_op1; + zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!1) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_VAR != IS_UNUSED) { + zend_free_op free_op2; + zval *key = _get_zval_ptr_var(opline->op2.var, EX_Ts(), &free_op2 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!1) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + if (free_op2.var) {zval_ptr_dtor(&free_op2.var);}; + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_TMP_UNUSED(int type, ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -10463,6 +11095,99 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_TMP_UNUSED_HANDLER(ZEND_OP ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_TMP_VAR != IS_UNUSED) { + zend_free_op free_op1; + zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!1) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_UNUSED != IS_UNUSED) { + + zval *key = NULL; + + /* Consts, temporary variables and references need copying */ + if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!1) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -11000,6 +11725,99 @@ static int ZEND_FASTCALL ZEND_INIT_ARRAY_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLE } } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_TMP_VAR != IS_UNUSED) { + zend_free_op free_op1; + zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!1) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_CV != IS_UNUSED) { + + zval *key = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op2.var TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!1) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_BW_NOT_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -12419,64 +13237,6 @@ static int ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_A ZEND_VM_NEXT_OPCODE(); } -static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - USE_OPLINE - - /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); - - /* Destroy the previously yielded value */ - if (generator->value) { - zval_ptr_dtor(&generator->value); - } - - /* Set the new yielded value */ - if (IS_VAR != IS_UNUSED) { - zend_free_op free_op1; - zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); - - /* Consts, temporary variables and references need copying */ - if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; - - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); - - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } - - generator->value = copy; - } else { - Z_ADDREF_P(value); - generator->value = value; - } - - if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; - } else { - /* If no value way specified yield null */ - Z_ADDREF(EG(uninitialized_zval)); - generator->value = &EG(uninitialized_zval); - } - - /* If a value is sent it should go into the result var */ - generator->send_target = &EX_T(opline->result.var); - - /* Initialize the sent value to NULL */ - Z_ADDREF(EG(uninitialized_zval)); - AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); - - /* The GOTO VM uses a local opline variable. We need to set the opline - * variable in execute_data so we don't resume at an old position. */ - SAVE_OPLINE(); - - ZEND_VM_RETURN(); -} - static int ZEND_FASTCALL ZEND_ADD_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -14816,6 +15576,100 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_VAR_CONST_HANDLER(ZEN return zend_isset_isempty_dim_prop_obj_handler_SPEC_VAR_CONST(1, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_VAR != IS_UNUSED) { + zend_free_op free_op1; + zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_CONST != IS_UNUSED) { + + zval *key = opline->op2.zv; + + /* Consts, temporary variables and references need copying */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_VAR_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -16742,6 +17596,100 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_VAR_TMP_HANDLER(ZEND_ return zend_isset_isempty_dim_prop_obj_handler_SPEC_VAR_TMP(1, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_VAR != IS_UNUSED) { + zend_free_op free_op1; + zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_TMP_VAR != IS_UNUSED) { + zend_free_op free_op2; + zval *key = _get_zval_ptr_tmp(opline->op2.var, EX_Ts(), &free_op2 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -19048,6 +19996,101 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_VAR_VAR_HANDLER(ZEND_ return zend_isset_isempty_dim_prop_obj_handler_SPEC_VAR_VAR(1, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_VAR != IS_UNUSED) { + zend_free_op free_op1; + zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_VAR != IS_UNUSED) { + zend_free_op free_op2; + zval *key = _get_zval_ptr_var(opline->op2.var, EX_Ts(), &free_op2 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + if (free_op2.var) {zval_ptr_dtor(&free_op2.var);}; + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC), ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -20032,6 +21075,100 @@ static int ZEND_FASTCALL ZEND_SEPARATE_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HAND ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_VAR != IS_UNUSED) { + zend_free_op free_op1; + zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_UNUSED != IS_UNUSED) { + + zval *key = NULL; + + /* Consts, temporary variables and references need copying */ + if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -22007,6 +23144,100 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_VAR_CV_HANDLER(ZEND_O return zend_isset_isempty_dim_prop_obj_handler_SPEC_VAR_CV(1, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_VAR != IS_UNUSED) { + zend_free_op free_op1; + zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_CV != IS_UNUSED) { + + zval *key = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op2.var TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_CLONE_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -22092,63 +23323,6 @@ static int ZEND_FASTCALL ZEND_EXIT_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS ZEND_VM_NEXT_OPCODE(); /* Never reached */ } -static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - USE_OPLINE - - /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); - - /* Destroy the previously yielded value */ - if (generator->value) { - zval_ptr_dtor(&generator->value); - } - - /* Set the new yielded value */ - if (IS_UNUSED != IS_UNUSED) { - - zval *value = NULL; - - /* Consts, temporary variables and references need copying */ - if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; - - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); - - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } - - generator->value = copy; - } else { - Z_ADDREF_P(value); - generator->value = value; - } - - } else { - /* If no value way specified yield null */ - Z_ADDREF(EG(uninitialized_zval)); - generator->value = &EG(uninitialized_zval); - } - - /* If a value is sent it should go into the result var */ - generator->send_target = &EX_T(opline->result.var); - - /* Initialize the sent value to NULL */ - Z_ADDREF(EG(uninitialized_zval)); - AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); - - /* The GOTO VM uses a local opline variable. We need to set the opline - * variable in execute_data so we don't resume at an old position. */ - SAVE_OPLINE(); - - ZEND_VM_RETURN(); -} - static int ZEND_FASTCALL zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC), ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -23398,6 +24572,99 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UNUSED_CONST_HANDLER( return zend_isset_isempty_dim_prop_obj_handler_SPEC_UNUSED_CONST(1, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_UNUSED != IS_UNUSED) { + + zval *value = NULL; + + /* Consts, temporary variables and references need copying */ + if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_CONST != IS_UNUSED) { + + zval *key = opline->op2.zv; + + /* Consts, temporary variables and references need copying */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMP(int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC), ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -24556,6 +25823,99 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UNUSED_TMP_HANDLER(ZE return zend_isset_isempty_dim_prop_obj_handler_SPEC_UNUSED_TMP(1, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_UNUSED != IS_UNUSED) { + + zval *value = NULL; + + /* Consts, temporary variables and references need copying */ + if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_TMP_VAR != IS_UNUSED) { + zend_free_op free_op2; + zval *key = _get_zval_ptr_tmp(opline->op2.var, EX_Ts(), &free_op2 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL zend_binary_assign_op_obj_helper_SPEC_UNUSED_VAR(int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC), ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -25714,6 +27074,100 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UNUSED_VAR_HANDLER(ZE return zend_isset_isempty_dim_prop_obj_handler_SPEC_UNUSED_VAR(1, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_UNUSED != IS_UNUSED) { + + zval *value = NULL; + + /* Consts, temporary variables and references need copying */ + if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_VAR != IS_UNUSED) { + zend_free_op free_op2; + zval *key = _get_zval_ptr_var(opline->op2.var, EX_Ts(), &free_op2 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + if (free_op2.var) {zval_ptr_dtor(&free_op2.var);}; + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL zend_binary_assign_op_obj_helper_SPEC_UNUSED_UNUSED(int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC), ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -25983,6 +27437,99 @@ static int ZEND_FASTCALL ZEND_INIT_ARRAY_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE } } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_UNUSED != IS_UNUSED) { + + zval *value = NULL; + + /* Consts, temporary variables and references need copying */ + if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_UNUSED != IS_UNUSED) { + + zval *key = NULL; + + /* Consts, temporary variables and references need copying */ + if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC), ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -27138,6 +28685,99 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_UNUSED_CV_HANDLER(ZEN return zend_isset_isempty_dim_prop_obj_handler_SPEC_UNUSED_CV(1, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_UNUSED != IS_UNUSED) { + + zval *value = NULL; + + /* Consts, temporary variables and references need copying */ + if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_CV != IS_UNUSED) { + + zval *key = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op2.var TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_BW_NOT_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -28379,63 +30019,6 @@ static int ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_AR ZEND_VM_NEXT_OPCODE(); } -static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - USE_OPLINE - - /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); - - /* Destroy the previously yielded value */ - if (generator->value) { - zval_ptr_dtor(&generator->value); - } - - /* Set the new yielded value */ - if (IS_CV != IS_UNUSED) { - - zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); - - /* Consts, temporary variables and references need copying */ - if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; - - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); - - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } - - generator->value = copy; - } else { - Z_ADDREF_P(value); - generator->value = value; - } - - } else { - /* If no value way specified yield null */ - Z_ADDREF(EG(uninitialized_zval)); - generator->value = &EG(uninitialized_zval); - } - - /* If a value is sent it should go into the result var */ - generator->send_target = &EX_T(opline->result.var); - - /* Initialize the sent value to NULL */ - Z_ADDREF(EG(uninitialized_zval)); - AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); - - /* The GOTO VM uses a local opline variable. We need to set the opline - * variable in execute_data so we don't resume at an old position. */ - SAVE_OPLINE(); - - ZEND_VM_RETURN(); -} - static int ZEND_FASTCALL ZEND_ADD_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -30554,6 +32137,99 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV_CONST_HANDLER(ZEND return zend_isset_isempty_dim_prop_obj_handler_SPEC_CV_CONST(1, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_CV != IS_UNUSED) { + + zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_CONST != IS_UNUSED) { + + zval *key = opline->op2.zv; + + /* Consts, temporary variables and references need copying */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_CV_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -32351,6 +34027,99 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV_TMP_HANDLER(ZEND_O return zend_isset_isempty_dim_prop_obj_handler_SPEC_CV_TMP(1, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_CV != IS_UNUSED) { + + zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_TMP_VAR != IS_UNUSED) { + zend_free_op free_op2; + zval *key = _get_zval_ptr_tmp(opline->op2.var, EX_Ts(), &free_op2 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -34527,6 +36296,100 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV_VAR_HANDLER(ZEND_O return zend_isset_isempty_dim_prop_obj_handler_SPEC_CV_VAR(1, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_CV != IS_UNUSED) { + + zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_VAR != IS_UNUSED) { + zend_free_op free_op2; + zval *key = _get_zval_ptr_var(opline->op2.var, EX_Ts(), &free_op2 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + if (free_op2.var) {zval_ptr_dtor(&free_op2.var);}; + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC), ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -35372,6 +37235,99 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_CV_UNUSED_HANDLER(ZEND_OPC ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_CV != IS_UNUSED) { + + zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_UNUSED != IS_UNUSED) { + + zval *key = NULL; + + /* Consts, temporary variables and references need copying */ + if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -37217,6 +39173,99 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_CV_CV_HANDLER(ZEND_OP return zend_isset_isempty_dim_prop_obj_handler_SPEC_CV_CV(1, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } +static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + USE_OPLINE + + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Destroy the previously yielded value */ + if (generator->value) { + zval_ptr_dtor(&generator->value); + } + + /* Destroy the previously yielded key */ + if (generator->key) { + zval_ptr_dtor(&generator->key); + } + + /* Set the new yielded value */ + if (IS_CV != IS_UNUSED) { + + zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } else { + /* If no value was specified yield null */ + Z_ADDREF(EG(uninitialized_zval)); + generator->value = &EG(uninitialized_zval); + } + + /* Set the new yielded key */ + if (IS_CV != IS_UNUSED) { + + zval *key = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op2.var TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR + || (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, key); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->key = copy; + } else { + Z_ADDREF_P(key); + generator->key = key; + } + + } else { + /* Setting the key to NULL signals that the auto-increment key + * generation should be used */ + generator->key = NULL; + } + + /* If a value is sent it should go into the result var */ + generator->send_target = &EX_T(opline->result.var); + + /* Initialize the sent value to NULL */ + Z_ADDREF(EG(uninitialized_zval)); + AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + + /* The GOTO VM uses a local opline variable. We need to set the opline + * variable in execute_data so we don't resume at an old position. */ + SAVE_OPLINE(); + + ZEND_VM_RETURN(); +} + static int ZEND_FASTCALL ZEND_NULL_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { zend_error_noreturn(E_ERROR, "Invalid opcode %d/%d/%d.", OPLINE->opcode, OPLINE->op1_type, OPLINE->op2_type); @@ -41226,31 +43275,31 @@ void zend_init_opcodes_handlers(void) ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_YIELD_SPEC_CONST_HANDLER, - ZEND_YIELD_SPEC_CONST_HANDLER, - ZEND_YIELD_SPEC_CONST_HANDLER, - ZEND_YIELD_SPEC_CONST_HANDLER, - ZEND_YIELD_SPEC_CONST_HANDLER, - ZEND_YIELD_SPEC_TMP_HANDLER, - ZEND_YIELD_SPEC_TMP_HANDLER, - ZEND_YIELD_SPEC_TMP_HANDLER, - ZEND_YIELD_SPEC_TMP_HANDLER, - ZEND_YIELD_SPEC_TMP_HANDLER, - ZEND_YIELD_SPEC_VAR_HANDLER, - ZEND_YIELD_SPEC_VAR_HANDLER, - ZEND_YIELD_SPEC_VAR_HANDLER, - ZEND_YIELD_SPEC_VAR_HANDLER, - ZEND_YIELD_SPEC_VAR_HANDLER, - ZEND_YIELD_SPEC_UNUSED_HANDLER, - ZEND_YIELD_SPEC_UNUSED_HANDLER, - ZEND_YIELD_SPEC_UNUSED_HANDLER, - ZEND_YIELD_SPEC_UNUSED_HANDLER, - ZEND_YIELD_SPEC_UNUSED_HANDLER, - ZEND_YIELD_SPEC_CV_HANDLER, - ZEND_YIELD_SPEC_CV_HANDLER, - ZEND_YIELD_SPEC_CV_HANDLER, - ZEND_YIELD_SPEC_CV_HANDLER, - ZEND_YIELD_SPEC_CV_HANDLER, + ZEND_YIELD_SPEC_CONST_CONST_HANDLER, + ZEND_YIELD_SPEC_CONST_TMP_HANDLER, + ZEND_YIELD_SPEC_CONST_VAR_HANDLER, + ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER, + ZEND_YIELD_SPEC_CONST_CV_HANDLER, + ZEND_YIELD_SPEC_TMP_CONST_HANDLER, + ZEND_YIELD_SPEC_TMP_TMP_HANDLER, + ZEND_YIELD_SPEC_TMP_VAR_HANDLER, + ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER, + ZEND_YIELD_SPEC_TMP_CV_HANDLER, + ZEND_YIELD_SPEC_VAR_CONST_HANDLER, + ZEND_YIELD_SPEC_VAR_TMP_HANDLER, + ZEND_YIELD_SPEC_VAR_VAR_HANDLER, + ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER, + ZEND_YIELD_SPEC_VAR_CV_HANDLER, + ZEND_YIELD_SPEC_UNUSED_CONST_HANDLER, + ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER, + ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER, + ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDLER, + ZEND_YIELD_SPEC_UNUSED_CV_HANDLER, + ZEND_YIELD_SPEC_CV_CONST_HANDLER, + ZEND_YIELD_SPEC_CV_TMP_HANDLER, + ZEND_YIELD_SPEC_CV_VAR_HANDLER, + ZEND_YIELD_SPEC_CV_UNUSED_HANDLER, + ZEND_YIELD_SPEC_CV_CV_HANDLER, ZEND_NULL_HANDLER }; zend_opcode_handlers = (opcode_handler_t*)labels; From 879016023566ce162be1d81e973bde9db76dd519 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 30 May 2012 05:05:49 +0200 Subject: [PATCH 32/70] Add auto-increment keys When no key is explicitely yielded PHP will used auto-incrementing keys as a fallback. They behave the same as with arrays, i.e. the key is the successor of the largest previously used integer key. --- .../generators/auto_incrementing_keys.phpt | 22 ++ Zend/zend_generators.c | 3 + Zend/zend_generators.h | 2 + Zend/zend_vm_def.h | 14 +- Zend/zend_vm_execute.h | 350 ++++++++++++++---- 5 files changed, 313 insertions(+), 78 deletions(-) create mode 100644 Zend/tests/generators/auto_incrementing_keys.phpt diff --git a/Zend/tests/generators/auto_incrementing_keys.phpt b/Zend/tests/generators/auto_incrementing_keys.phpt new file mode 100644 index 000000000000..623f2d813d45 --- /dev/null +++ b/Zend/tests/generators/auto_incrementing_keys.phpt @@ -0,0 +1,22 @@ +--TEST-- +Generator keys are auto-incrementing by default +--FILE-- + 'rab'; + yield 'oof'; +} + +foreach (gen() as $k => $v) { + echo $k, ' => ', $v, "\n"; +} + +?> +--EXPECT-- +0 => foo +1 => bar +5 => rab +6 => oof diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index d3d4b3f7a81b..5c211b0e8b80 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -116,6 +116,9 @@ static zend_object_value zend_generator_create(zend_class_entry *class_type TSRM generator = emalloc(sizeof(zend_generator)); memset(generator, 0, sizeof(zend_generator)); + /* The key will be incremented on first use, so it'll start at 0 */ + generator->largest_used_integer_key = -1; + zend_object_std_init(&generator->std, class_type TSRMLS_CC); object.handle = zend_objects_store_put(generator, NULL, diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index 2fd2c93b6362..b73557c71546 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -34,6 +34,8 @@ typedef struct _zend_generator { zval *key; /* Variable to put sent value into */ temp_variable *send_target; + /* Largest used integer key for auto-incrementing keys */ + long largest_used_integer_key; } zend_generator; extern ZEND_API zend_class_entry *zend_ce_generator; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 36b9c93c5258..f1fd64cf814f 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5332,11 +5332,19 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + FREE_OP2_IF_VAR(); } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index ac7ff7ff585f..09c3461b7a3c 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -4127,10 +4127,18 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLE generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -4760,10 +4768,18 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMP_HANDLER(ZEND_OPCODE_HANDLER_ generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -5718,11 +5734,19 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + if (free_op2.var) {zval_ptr_dtor(&free_op2.var);}; } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -6371,10 +6395,18 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDL generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -7063,10 +7095,18 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_A generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -9058,10 +9098,18 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -9691,10 +9739,18 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_TMP_HANDLER(ZEND_OPCODE_HANDLER_AR generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -10649,11 +10705,19 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_VAR_HANDLER(ZEND_OPCODE_HANDLER_AR generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + if (free_op2.var) {zval_ptr_dtor(&free_op2.var);}; } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -11168,10 +11232,18 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCODE_HANDLER generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -11798,10 +11870,18 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARG generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -15650,10 +15730,18 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -17670,10 +17758,18 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_TMP_HANDLER(ZEND_OPCODE_HANDLER_AR generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -20070,11 +20166,19 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_AR generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + if (free_op2.var) {zval_ptr_dtor(&free_op2.var);}; } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -21149,10 +21253,18 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -23218,10 +23330,18 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARG generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -24645,10 +24765,18 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDL generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -25896,10 +26024,18 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER(ZEND_OPCODE_HANDLER generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -27147,11 +27283,19 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER(ZEND_OPCODE_HANDLER generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + if (free_op2.var) {zval_ptr_dtor(&free_op2.var);}; } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -27510,10 +27654,18 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HAND generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -28758,10 +28910,18 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -32210,10 +32370,18 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_A generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -34100,10 +34268,18 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARG generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -36369,11 +36545,19 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + if (free_op2.var) {zval_ptr_dtor(&free_op2.var);}; } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -37308,10 +37492,18 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ @@ -39246,10 +39438,18 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS generator->key = key; } + if (Z_TYPE_P(generator->key) == IS_LONG + && Z_LVAL_P(generator->key) > generator->largest_used_integer_key + ) { + generator->largest_used_integer_key = Z_LVAL_P(generator->key); + } + } else { - /* Setting the key to NULL signals that the auto-increment key - * generation should be used */ - generator->key = NULL; + /* If no key was specified we use auto-increment keys */ + generator->largest_used_integer_key++; + + ALLOC_INIT_ZVAL(generator->key); + ZVAL_LONG(generator->key, generator->largest_used_integer_key); } /* If a value is sent it should go into the result var */ From 0033a525213e68e6ae74b3c3d75fd6ea4beea5d6 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 30 May 2012 16:28:33 +0200 Subject: [PATCH 33/70] Allow throwing exceptions from generators The missing piece is how one can find the next stack frame, which is required for dtor'ing arguments pushed to the stack. As the generator execute_data does not live on the stack one can't use it to figure out the start of the next stack frame. So there must be some other method. --- .../generator_throwing_exception.phpt | 28 +++++++++++ Zend/zend_vm_def.h | 38 ++++++++++++-- Zend/zend_vm_execute.h | 50 +++++++++++++++---- 3 files changed, 100 insertions(+), 16 deletions(-) create mode 100644 Zend/tests/generators/generator_throwing_exception.phpt diff --git a/Zend/tests/generators/generator_throwing_exception.phpt b/Zend/tests/generators/generator_throwing_exception.phpt new file mode 100644 index 000000000000..5df4a811328e --- /dev/null +++ b/Zend/tests/generators/generator_throwing_exception.phpt @@ -0,0 +1,28 @@ +--TEST-- +Generators can throw exceptions +--FILE-- +current()); + +try { + $gen->next(); +} catch (Exception $e) { + echo 'Caught exception with message "', $e->getMessage(), '"', "\n"; +} + +var_dump($gen->current()); + +?> +--EXPECT-- +string(3) "foo" +Caught exception with message "test" +NULL diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index f1fd64cf814f..cbdf701adcb0 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2821,10 +2821,10 @@ ZEND_VM_HANDLER(62, ZEND_RETURN, CONST|TMP|VAR|CV, ANY) /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); - /* Close the generator to free up resources. */ + /* Close the generator to free up resources */ zend_generator_close(generator, 1 TSRMLS_CC); - /* Pass execution back to generator handling code */ + /* Pass execution back to handling code */ ZEND_VM_RETURN(); } @@ -4992,11 +4992,25 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) int i; zend_uint catch_op_num = 0; int catched = 0; - zval restored_error_reporting; + void **stack_frame; - void **stack_frame = (void**)(((char*)EX_Ts()) + - (ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * EX(op_array)->T)); + /* Figure out where the next stack frame (which maybe contains pushed + * arguments that have to be dtor'ed) starts */ + if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { + /* For generators the execution context is not stored on the stack so + * I don't know yet how to figure out where the next stack frame + * starts. For now I'll just use the stack top to ignore argument + * dtor'ing altogether. */ + stack_frame = zend_vm_stack_top(TSRMLS_C); + } else { + /* In all other cases the next stack frame starts after the temporary + * variables section of the current execution context */ + stack_frame = (void **) ((char *) EX_Ts() + + ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * EX(op_array)->T); + } + /* If the exception was thrown during a function call there might be + * arguments pushed to the stack that have to be dtor'ed. */ while (zend_vm_stack_top(TSRMLS_C) != stack_frame) { zval *stack_zval_p = zend_vm_stack_pop(TSRMLS_C); zval_ptr_dtor(&stack_zval_p); @@ -5058,6 +5072,8 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) /* restore previous error_reporting value */ if (!EG(error_reporting) && EX(old_error_reporting) != NULL && Z_LVAL_P(EX(old_error_reporting)) != 0) { + zval restored_error_reporting; + Z_TYPE(restored_error_reporting) = IS_LONG; Z_LVAL(restored_error_reporting) = Z_LVAL_P(EX(old_error_reporting)); convert_to_string(&restored_error_reporting); @@ -5067,6 +5083,18 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) EX(old_error_reporting) = NULL; if (!catched) { + /* For generators skip the leave handler return directly */ + if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Close the generator to free up resources */ + zend_generator_close(generator, 1 TSRMLS_CC); + + /* Pass execution back to handling code */ + ZEND_VM_RETURN(); + } + ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper); } else { ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[catch_op_num]); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 09c3461b7a3c..039504be4519 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1055,11 +1055,25 @@ static int ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER int i; zend_uint catch_op_num = 0; int catched = 0; - zval restored_error_reporting; + void **stack_frame; - void **stack_frame = (void**)(((char*)EX_Ts()) + - (ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * EX(op_array)->T)); + /* Figure out where the next stack frame (which maybe contains pushed + * arguments that have to be dtor'ed) starts */ + if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { + /* For generators the execution context is not stored on the stack so + * I don't know yet how to figure out where the next stack frame + * starts. For now I'll just use the stack top to ignore argument + * dtor'ing altogether. */ + stack_frame = zend_vm_stack_top(TSRMLS_C); + } else { + /* In all other cases the next stack frame starts after the temporary + * variables section of the current execution context */ + stack_frame = (void **) ((char *) EX_Ts() + + ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * EX(op_array)->T); + } + /* If the exception was thrown during a function call there might be + * arguments pushed to the stack that have to be dtor'ed. */ while (zend_vm_stack_top(TSRMLS_C) != stack_frame) { zval *stack_zval_p = zend_vm_stack_pop(TSRMLS_C); zval_ptr_dtor(&stack_zval_p); @@ -1121,6 +1135,8 @@ static int ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER /* restore previous error_reporting value */ if (!EG(error_reporting) && EX(old_error_reporting) != NULL && Z_LVAL_P(EX(old_error_reporting)) != 0) { + zval restored_error_reporting; + Z_TYPE(restored_error_reporting) = IS_LONG; Z_LVAL(restored_error_reporting) = Z_LVAL_P(EX(old_error_reporting)); convert_to_string(&restored_error_reporting); @@ -1130,6 +1146,18 @@ static int ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER EX(old_error_reporting) = NULL; if (!catched) { + /* For generators skip the leave handler return directly */ + if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* Close the generator to free up resources */ + zend_generator_close(generator, 1 TSRMLS_CC); + + /* Pass execution back to handling code */ + ZEND_VM_RETURN(); + } + return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } else { ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[catch_op_num]); @@ -2318,10 +2346,10 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARG /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); - /* Close the generator to free up resources. */ + /* Close the generator to free up resources */ zend_generator_close(generator, 1 TSRMLS_CC); - /* Pass execution back to generator handling code */ + /* Pass execution back to handling code */ ZEND_VM_RETURN(); } @@ -7368,10 +7396,10 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); - /* Close the generator to free up resources. */ + /* Close the generator to free up resources */ zend_generator_close(generator, 1 TSRMLS_CC); - /* Pass execution back to generator handling code */ + /* Pass execution back to handling code */ ZEND_VM_RETURN(); } @@ -12323,10 +12351,10 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); - /* Close the generator to free up resources. */ + /* Close the generator to free up resources */ zend_generator_close(generator, 1 TSRMLS_CC); - /* Pass execution back to generator handling code */ + /* Pass execution back to handling code */ ZEND_VM_RETURN(); } @@ -29344,10 +29372,10 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); - /* Close the generator to free up resources. */ + /* Close the generator to free up resources */ zend_generator_close(generator, 1 TSRMLS_CC); - /* Pass execution back to generator handling code */ + /* Pass execution back to handling code */ ZEND_VM_RETURN(); } From ee89e228f6f684555dd219d8a46d173cfed3230a Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 31 May 2012 00:00:30 +0200 Subject: [PATCH 34/70] Allow yielding during function calls During function calls arguments are pushed onto the stack. Now these are backed up on yield and restored on resume. This requires memcpy'ing them, but there doesn't seem to be any better way to do it. Also this fixes the issue with exceptions thrown during function calls. --- ...nerator_throwing_during_function_call.phpt | 32 ++++++++++++++++++ .../yield_during_function_call.phpt | 15 +++++++++ Zend/zend_generators.c | 33 +++++++++++++++++++ Zend/zend_generators.h | 10 ++++++ Zend/zend_vm_def.h | 11 ++++--- Zend/zend_vm_execute.h | 11 ++++--- 6 files changed, 102 insertions(+), 10 deletions(-) create mode 100644 Zend/tests/generators/generator_throwing_during_function_call.phpt create mode 100644 Zend/tests/generators/yield_during_function_call.phpt diff --git a/Zend/tests/generators/generator_throwing_during_function_call.phpt b/Zend/tests/generators/generator_throwing_during_function_call.phpt new file mode 100644 index 000000000000..5e40ef948370 --- /dev/null +++ b/Zend/tests/generators/generator_throwing_during_function_call.phpt @@ -0,0 +1,32 @@ +--TEST-- +Stack is cleaned up properly when an exception is thrown during a function call +--FILE-- +current()); + +try { + $gen->next(); +} catch (Exception $e) { + echo 'Caught exception with message "', $e->getMessage(), '"', "\n"; +} + +var_dump($gen->current()); + +?> +--EXPECT-- +string(3) "foo" +Caught exception with message "test" +NULL diff --git a/Zend/tests/generators/yield_during_function_call.phpt b/Zend/tests/generators/yield_during_function_call.phpt new file mode 100644 index 000000000000..9727b8fd33e0 --- /dev/null +++ b/Zend/tests/generators/yield_during_function_call.phpt @@ -0,0 +1,15 @@ +--TEST-- +"yield" can occur during a function call +--FILE-- +send(10); + +?> +--EXPECT-- +string(10) "xxxxxxxxxx" diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 5c211b0e8b80..4754d7595d9b 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -83,6 +83,19 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio } } + /* Clear any backed up stack arguments */ + if (generator->backed_up_stack) { + zval **zvals = (zval **) generator->backed_up_stack; + size_t zval_num = generator->backed_up_stack_size / sizeof(zval *); + int i; + + for (i = 0; i < zval_num; i++) { + zval_ptr_dtor(&zvals[i]); + } + + efree(generator->backed_up_stack); + } + efree(execute_data); generator->execute_data = NULL; } @@ -158,6 +171,17 @@ static void zend_generator_resume(zval *object, zend_generator *generator TSRMLS zend_class_entry *original_scope = EG(scope); zend_class_entry *original_called_scope = EG(called_scope); + /* Remember the current stack position so we can back up pushed args */ + generator->original_stack_top = zend_vm_stack_top(TSRMLS_C); + + /* If there is a backed up stack copy it to the VM stack */ + if (generator->backed_up_stack) { + void *stack = zend_vm_stack_alloc(generator->backed_up_stack_size TSRMLS_CC); + memcpy(stack, generator->backed_up_stack, generator->backed_up_stack_size); + efree(generator->backed_up_stack); + generator->backed_up_stack = NULL; + } + /* We (mis)use the return_value_ptr_ptr to provide the generator object * to the executor, so YIELD will be able to set the yielded value */ EG(return_value_ptr_ptr) = &object; @@ -186,6 +210,15 @@ static void zend_generator_resume(zval *object, zend_generator *generator TSRMLS EG(This) = original_This; EG(scope) = original_scope; EG(called_scope) = original_called_scope; + + /* The stack top before and after the execution differ, i.e. there are + * arguments pushed to the stack. */ + if (generator->original_stack_top != zend_vm_stack_top(TSRMLS_C)) { + generator->backed_up_stack_size = (zend_vm_stack_top(TSRMLS_C) - generator->original_stack_top) * sizeof(void *); + generator->backed_up_stack = emalloc(generator->backed_up_stack_size); + memcpy(generator->backed_up_stack, generator->original_stack_top, generator->backed_up_stack_size); + zend_vm_stack_free(generator->original_stack_top TSRMLS_CC); + } } } /* }}} */ diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index b73557c71546..89193bbe6e46 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -28,6 +28,16 @@ typedef struct _zend_generator { /* The suspended execution context. */ zend_execute_data *execute_data; + + /* If the execution is suspended during a function call there may be + * arguments pushed to the stack, so it has to be backed up. */ + void *backed_up_stack; + size_t backed_up_stack_size; + + /* The original stack top before resuming the generator. This is required + * for proper cleanup during exception handling. */ + void **original_stack_top; + /* Current value */ zval *value; /* Current key */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index cbdf701adcb0..de63d8b5e22b 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -4997,11 +4997,12 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) /* Figure out where the next stack frame (which maybe contains pushed * arguments that have to be dtor'ed) starts */ if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { - /* For generators the execution context is not stored on the stack so - * I don't know yet how to figure out where the next stack frame - * starts. For now I'll just use the stack top to ignore argument - * dtor'ing altogether. */ - stack_frame = zend_vm_stack_top(TSRMLS_C); + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* For generators the next stack frame is conveniently stored in the + * generator object. */ + stack_frame = generator->original_stack_top; } else { /* In all other cases the next stack frame starts after the temporary * variables section of the current execution context */ diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 039504be4519..98d849bbd688 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1060,11 +1060,12 @@ static int ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER /* Figure out where the next stack frame (which maybe contains pushed * arguments that have to be dtor'ed) starts */ if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { - /* For generators the execution context is not stored on the stack so - * I don't know yet how to figure out where the next stack frame - * starts. For now I'll just use the stack top to ignore argument - * dtor'ing altogether. */ - stack_frame = zend_vm_stack_top(TSRMLS_C); + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + + /* For generators the next stack frame is conveniently stored in the + * generator object. */ + stack_frame = generator->original_stack_top; } else { /* In all other cases the next stack frame starts after the temporary * variables section of the current execution context */ From 1477be9aa88689f7e547a24258dc4d63637fd5b0 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 31 May 2012 20:03:18 +0200 Subject: [PATCH 35/70] Make $generator->send() return the current value This makes the API easier to use (and is consistent with Python and JS). --- .../generators/send_returns_current.phpt | 20 +++++++++++++++++++ Zend/zend_generators.c | 6 +++++- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/generators/send_returns_current.phpt diff --git a/Zend/tests/generators/send_returns_current.phpt b/Zend/tests/generators/send_returns_current.phpt new file mode 100644 index 000000000000..d3a4afd53a37 --- /dev/null +++ b/Zend/tests/generators/send_returns_current.phpt @@ -0,0 +1,20 @@ +--TEST-- +$generator->send() returns the yielded value +--FILE-- +send('foo')); +var_dump($gen->send('bar')); + +?> +--EXPECT-- +string(3) "oof" +string(3) "rab" diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 4754d7595d9b..5b58e2176046 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -336,7 +336,7 @@ ZEND_METHOD(Generator, next) } /* }}} */ -/* {{{ proto void Generator::send() +/* {{{ proto mixed Generator::send() * Sends a value to the generator */ ZEND_METHOD(Generator, send) { @@ -366,6 +366,10 @@ ZEND_METHOD(Generator, send) generator->send_target->var.ptr_ptr = &value; zend_generator_resume(object, generator TSRMLS_CC); + + if (generator->value) { + RETURN_ZVAL(generator->value, 1, 0); + } } /* {{{ proto void Generator::close() From 6117f4c7c0abe3721c2371600a97641003de21a7 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 2 Jun 2012 20:40:58 +0200 Subject: [PATCH 36/70] Add cloning support for generators Generators can now be cloned. I'm pretty sure that my current code does not yet cover all the edge cases of cloning the execution context, so there are probably a few bugs in there :) --- Zend/tests/generators/clone.phpt | 32 ++++ Zend/tests/generators/clone_with_foreach.phpt | 33 ++++ Zend/tests/generators/clone_with_stack.phpt | 18 +++ .../generators/clone_with_symbol_table.phpt | 27 ++++ Zend/zend_generators.c | 142 ++++++++++++++++-- 5 files changed, 243 insertions(+), 9 deletions(-) create mode 100644 Zend/tests/generators/clone.phpt create mode 100644 Zend/tests/generators/clone_with_foreach.phpt create mode 100644 Zend/tests/generators/clone_with_stack.phpt create mode 100644 Zend/tests/generators/clone_with_symbol_table.phpt diff --git a/Zend/tests/generators/clone.phpt b/Zend/tests/generators/clone.phpt new file mode 100644 index 000000000000..94c4c6364e7d --- /dev/null +++ b/Zend/tests/generators/clone.phpt @@ -0,0 +1,32 @@ +--TEST-- +Generators can be cloned +--FILE-- +current()); +$g1->next(); + +$g2 = clone $g1; +var_dump($g2->current()); +$g2->next(); + +var_dump($g2->current()); +var_dump($g1->current()); + +$g1->next(); +var_dump($g1->current()); + +?> +--EXPECT-- +int(0) +int(1) +int(2) +int(1) +int(2) diff --git a/Zend/tests/generators/clone_with_foreach.phpt b/Zend/tests/generators/clone_with_foreach.phpt new file mode 100644 index 000000000000..42cf3348ea21 --- /dev/null +++ b/Zend/tests/generators/clone_with_foreach.phpt @@ -0,0 +1,33 @@ +--TEST-- +Cloning a generator with a foreach loop properly adds a ref for the loop var +--FILE-- +current()); + +$g2 = clone $g1; +var_dump($g2->current()); + +$g1->next(); +$g2->next(); +var_dump($g1->current()); +var_dump($g2->current()); + +$g1->close(); +$g2->next(); +var_dump($g2->current()); + +?> +--EXPECT-- +int(1) +int(1) +int(2) +int(2) +int(3) diff --git a/Zend/tests/generators/clone_with_stack.phpt b/Zend/tests/generators/clone_with_stack.phpt new file mode 100644 index 000000000000..79bca31bb62f --- /dev/null +++ b/Zend/tests/generators/clone_with_stack.phpt @@ -0,0 +1,18 @@ +--TEST-- +A generator with an active stack can be cloned +--FILE-- +rewind(); +$g2 = clone $g1; +$g1->close(); +$g2->send(10); + +?> +--EXPECT-- +string(10) "xxxxxxxxxx" diff --git a/Zend/tests/generators/clone_with_symbol_table.phpt b/Zend/tests/generators/clone_with_symbol_table.phpt new file mode 100644 index 000000000000..2a4cf10aabad --- /dev/null +++ b/Zend/tests/generators/clone_with_symbol_table.phpt @@ -0,0 +1,27 @@ +--TEST-- +A generator using a symbol table can be cloned +--FILE-- + 'bar']); + + // interrupt + yield; + + var_dump($foo); +} + +$g1 = gen(); +$g1->rewind(); +$g2 = clone $g1; +$g1->close(); +$g2->next(); + +?> +--EXPECT-- +string(3) "bar" diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 5b58e2176046..01b93ef7fc02 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -34,13 +34,7 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio if (!execute_data->symbol_table) { zend_free_compiled_variables(execute_data->CVs, execute_data->op_array->last_var); } else { - if (EG(symtable_cache_ptr) >= EG(symtable_cache_limit)) { - zend_hash_destroy(execute_data->symbol_table); - FREE_HASHTABLE(execute_data->symbol_table); - } else { - zend_hash_clean(execute_data->symbol_table); - *(++EG(symtable_cache_ptr)) = execute_data->symbol_table; - } + zend_clean_and_cache_symbol_table(execute_data->symbol_table); } if (execute_data->current_this) { @@ -121,6 +115,134 @@ static void zend_generator_free_storage(zend_generator *generator TSRMLS_DC) /* } /* }}} */ +static void zend_generator_clone_storage(zend_generator *orig, zend_generator **clone_ptr) /* {{{ */ +{ + zend_generator *clone = emalloc(sizeof(zend_generator)); + memcpy(clone, orig, sizeof(zend_generator)); + + if (orig->execute_data) { + /* Create a few shorter aliases to the old execution data */ + zend_execute_data *execute_data = orig->execute_data; + zend_op_array *op_array = execute_data->op_array; + HashTable *symbol_table = execute_data->symbol_table; + + /* Alloc separate execution context, as well as separate sections for + * compiled variables and temporary variables */ + size_t execute_data_size = ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data)); + size_t CVs_size = ZEND_MM_ALIGNED_SIZE(sizeof(zval **) * op_array->last_var * (symbol_table ? 1 : 2)); + size_t Ts_size = ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T; + size_t total_size = execute_data_size + CVs_size + Ts_size; + + clone->execute_data = emalloc(total_size); + + /* Copy the zend_execute_data struct */ + memcpy(clone->execute_data, execute_data, execute_data_size); + + /* Set the pointers to the memory segments for the compiled and + * temporary variables (which are located after the execute_data) */ + clone->execute_data->CVs = (zval ***) ((char *) clone->execute_data + execute_data_size); + clone->execute_data->Ts = (temp_variable *) ((char *) clone->execute_data->CVs + CVs_size); + + /* Zero out the compiled variables section */ + memset(clone->execute_data->CVs, 0, sizeof(zval **) * op_array->last_var); + + if (!symbol_table) { + int i; + + /* Copy compiled variables */ + for (i = 0; i < op_array->last_var; i++) { + if (execute_data->CVs[i]) { + clone->execute_data->CVs[i] = (zval **) clone->execute_data->CVs + op_array->last_var + i; + *clone->execute_data->CVs[i] = (zval *) orig->execute_data->CVs[op_array->last_var + i]; + Z_ADDREF_PP(clone->execute_data->CVs[i]); + } + } + } else { + /* Copy symbol table */ + ALLOC_HASHTABLE(clone->execute_data->symbol_table); + zend_hash_init(clone->execute_data->symbol_table, zend_hash_num_elements(symbol_table), NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(clone->execute_data->symbol_table, symbol_table, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + + /* Update zval** pointers for compiled variables */ + { + int i; + for (i = 0; i < op_array->last_var; i++) { + if (zend_hash_quick_find(clone->execute_data->symbol_table, op_array->vars[i].name, op_array->vars[i].name_len + 1, op_array->vars[i].hash_value, (void **) &clone->execute_data->CVs[i]) == FAILURE) { + clone->execute_data->CVs[i] = NULL; + } + } + } + } + + /* Copy the temporary variables */ + memcpy(clone->execute_data->Ts, orig->execute_data->Ts, Ts_size); + + /* Add references to loop variables */ + { + zend_uint op_num = execute_data->opline - op_array->opcodes; + + int i; + for (i = 0; i < op_array->last_brk_cont; ++i) { + zend_brk_cont_element *brk_cont = op_array->brk_cont_array + i; + + if (brk_cont->start < 0) { + continue; + } else if (brk_cont->start > op_num) { + break; + } else if (brk_cont->brk > op_num) { + zend_op *brk_opline = op_array->opcodes + brk_cont->brk; + + if (brk_opline->opcode == ZEND_SWITCH_FREE) { + temp_variable *var = (temp_variable *) ((char *) execute_data->Ts + brk_opline->op1.var); + + Z_ADDREF_P(var->var.ptr); + } + } + } + } + + if (orig->backed_up_stack) { + /* Copy backed up stack */ + clone->backed_up_stack = emalloc(orig->backed_up_stack_size); + memcpy(clone->backed_up_stack, orig->backed_up_stack, orig->backed_up_stack_size); + + /* Add refs to stack variables */ + { + zval **zvals = (zval **) orig->backed_up_stack; + size_t zval_num = orig->backed_up_stack_size / sizeof(zval *); + int i; + + for (i = 0; i < zval_num; i++) { + Z_ADDREF_P(zvals[i]); + } + } + } + + /* Update the send_target to use the temporary variable with the same + * offset as the original generator, but in out temporary variable + * memory segment. */ + if (orig->send_target) { + size_t offset = (char *) orig->send_target - (char *) execute_data->Ts; + clone->send_target = (temp_variable *) ( + (char *) clone->execute_data->Ts + offset + ); + Z_ADDREF_P(clone->send_target->var.ptr); + } + } + + /* The value and key are known not to be references, so simply add refs */ + if (orig->value) { + Z_ADDREF_P(orig->value); + } + + if (orig->key) { + Z_ADDREF_P(orig->key); + } + + *clone_ptr = clone; +} +/* }}} */ + static zend_object_value zend_generator_create(zend_class_entry *class_type TSRMLS_DC) /* {{{ */ { zend_generator *generator; @@ -136,7 +258,7 @@ static zend_object_value zend_generator_create(zend_class_entry *class_type TSRM object.handle = zend_objects_store_put(generator, NULL, (zend_objects_free_object_storage_t) zend_generator_free_storage, - NULL /* no clone handler for now */ + (zend_objects_store_clone_t) zend_generator_clone_storage TSRMLS_CC ); object.handlers = &zend_generator_handlers; @@ -358,7 +480,8 @@ ZEND_METHOD(Generator, send) } /* The sent value was initialized to NULL, so dtor that */ - zval_ptr_dtor(generator->send_target->var.ptr_ptr); + zval_ptr_dtor(&generator->send_target->var.ptr); + /* Set new sent value */ Z_ADDREF_P(value); @@ -419,6 +542,7 @@ void zend_register_generator_ce(TSRMLS_D) /* {{{ */ memcpy(&zend_generator_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); zend_generator_handlers.get_constructor = zend_generator_get_constructor; + zend_generator_handlers.clone_obj = zend_objects_store_clone_obj; } /* }}} */ From 7b3bfa5784cf36647f21a72ceb9741e40927a5b6 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 3 Jun 2012 02:00:11 +0200 Subject: [PATCH 37/70] Improve backtraces from generators The current situation is still not perfect, as the generator function itself does not appear in the stack trace. This makes sense in some way, but it would probably be more helpful if it would show up (with the bound arguments) after the $generator->xyz() call. This could be misleading too though as the function is not *really* called there. --- Zend/tests/generators/backtrace.phpt | 25 +++++++++++++++++++++++++ Zend/zend_generators.c | 4 ++++ 2 files changed, 29 insertions(+) create mode 100644 Zend/tests/generators/backtrace.phpt diff --git a/Zend/tests/generators/backtrace.phpt b/Zend/tests/generators/backtrace.phpt new file mode 100644 index 000000000000..cbf8de1114e3 --- /dev/null +++ b/Zend/tests/generators/backtrace.phpt @@ -0,0 +1,25 @@ +--TEST-- +Printing the stack trace in a generator +--FILE-- +rewind(); // trigger run +} + +$gen = f2(); +f3($gen); + +?> +--EXPECTF-- +#0 f1() called at [%s:%d] +#1 Generator->rewind() called at [%s:%d] +#2 f3(Generator Object ()) called at [%s:%d] diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 01b93ef7fc02..df204fa41372 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -317,6 +317,10 @@ static void zend_generator_resume(zval *object, zend_generator *generator TSRMLS EG(scope) = generator->execute_data->current_scope; EG(called_scope) = generator->execute_data->current_called_scope; + /* Set prev_execute_data to the current execute_data to get halfways + * reasonable backtraces */ + generator->execute_data->prev_execute_data = original_execute_data; + /* Go to next opcode (we don't want to run the last one again) */ generator->execute_data->opline++; From bf82f46ea9028faa3830525d2462effe7d08600d Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 3 Jun 2012 02:16:29 +0200 Subject: [PATCH 38/70] Properly handle yield during method calls --- .../generators/yield_during_method_call.phpt | 35 +++++++++++++++++++ Zend/zend_generators.c | 8 +++++ 2 files changed, 43 insertions(+) create mode 100644 Zend/tests/generators/yield_during_method_call.phpt diff --git a/Zend/tests/generators/yield_during_method_call.phpt b/Zend/tests/generators/yield_during_method_call.phpt new file mode 100644 index 000000000000..da987ab5b733 --- /dev/null +++ b/Zend/tests/generators/yield_during_method_call.phpt @@ -0,0 +1,35 @@ +--TEST-- +Yield can be used during a method call +--FILE-- +b(yield); +} + +$gen = gen(); +$gen->send('foo'); + +// test resource cleanup +$gen = gen(); +$gen->rewind(); +$gen->close(); + +// test cloning +$g1 = gen(); +$g1->rewind(); +$g2 = clone $g1; +$g1->close(); +$g2->send('bar'); + +?> +--EXPECT-- +foo +bar diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index df204fa41372..a3277e6eebdd 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -41,6 +41,10 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio zval_ptr_dtor(&execute_data->current_this); } + if (execute_data->object) { + zval_ptr_dtor(&execute_data->object); + } + /* If the generator is closed before it can finish execution (reach * a return statement) we have to free loop variables manually, as * we don't know whether the SWITCH_FREE / FREE opcodes have run */ @@ -228,6 +232,10 @@ static void zend_generator_clone_storage(zend_generator *orig, zend_generator ** ); Z_ADDREF_P(clone->send_target->var.ptr); } + + if (execute_data->object) { + Z_ADDREF_P(execute_data->object); + } } /* The value and key are known not to be references, so simply add refs */ From 40760ecb90d1b024c76862e33d13d40137650af7 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 3 Jun 2012 02:40:03 +0200 Subject: [PATCH 39/70] Fix cloning of generator methods Forgot to add a reference to the this variable --- Zend/tests/generators/clone_with_this.phpt | 24 ++++++++++++++++++++++ Zend/zend_generators.c | 4 ++++ 2 files changed, 28 insertions(+) create mode 100644 Zend/tests/generators/clone_with_this.phpt diff --git a/Zend/tests/generators/clone_with_this.phpt b/Zend/tests/generators/clone_with_this.phpt new file mode 100644 index 000000000000..b0f28be3c9cd --- /dev/null +++ b/Zend/tests/generators/clone_with_this.phpt @@ -0,0 +1,24 @@ +--TEST-- +Cloning a generator method (with this) +--FILE-- +foo = 'bar'; + yield; // interrupt + var_dump($this->foo); + } +} + +$g1 = (new Test)->gen(); +$g1->rewind(); // goto yield +$g2 = clone $g1; +$g1->close(); +$g2->next(); + +?> +--EXPECT-- +string(3) "bar" diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index a3277e6eebdd..b5642ddcc7ad 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -233,6 +233,10 @@ static void zend_generator_clone_storage(zend_generator *orig, zend_generator ** Z_ADDREF_P(clone->send_target->var.ptr); } + if (execute_data->current_this) { + Z_ADDREF_P(execute_data->current_this); + } + if (execute_data->object) { Z_ADDREF_P(execute_data->object); } From f169b26dd7a4047996ab1284e649fda0cfb1a10b Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 9 Jun 2012 00:40:47 +0200 Subject: [PATCH 40/70] Fix backtraces and func_get_args() To make the generator function show up in backtraces one has to insert an additional execute_data into the chain, as prev_execute_data->function_state is used to determine the called function. Adding the additional stack frame is also required for func_get_args(), as the arguments are fetched from there too. The arguments have to be copied in order to keep them around. Due to the way they are saved doing so is quite ugly, so I added another function zend_copy_arguments to zend_execute.c which handles this. --- Zend/tests/generators/backtrace.phpt | 9 ++-- Zend/zend_execute.c | 19 ++++++++ Zend/zend_execute.h | 1 + Zend/zend_generators.c | 44 ++++++++++++++++-- Zend/zend_vm_def.h | 68 +++++++++++++++++----------- Zend/zend_vm_execute.h | 66 +++++++++++++++++---------- 6 files changed, 149 insertions(+), 58 deletions(-) diff --git a/Zend/tests/generators/backtrace.phpt b/Zend/tests/generators/backtrace.phpt index cbf8de1114e3..77976f932446 100644 --- a/Zend/tests/generators/backtrace.phpt +++ b/Zend/tests/generators/backtrace.phpt @@ -7,7 +7,7 @@ function f1() { debug_print_backtrace(); } -function *f2() { +function *f2($arg1, $arg2) { f1(); } @@ -15,11 +15,12 @@ function f3($gen) { $gen->rewind(); // trigger run } -$gen = f2(); +$gen = f2('foo', 'bar'); f3($gen); ?> --EXPECTF-- #0 f1() called at [%s:%d] -#1 Generator->rewind() called at [%s:%d] -#2 f3(Generator Object ()) called at [%s:%d] +#1 f2(foo, bar) called at [%s:%d] +#2 Generator->rewind() called at [%s:%d] +#3 f3(Generator Object ()) called at [%s:%d] diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 9031fb5557c7..c7ef212887e9 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1563,6 +1563,25 @@ void zend_free_compiled_variables(zval ***CVs, int num) /* {{{ */ } /* }}} */ +void** zend_copy_arguments(void **arguments_end) /* {{{ */ +{ + int arguments_count = (int) (zend_uintptr_t) *arguments_end; + size_t arguments_size = (arguments_count + 1) * sizeof(void **); + void **arguments_start = arguments_end - arguments_count; + void **copied_arguments_start = emalloc(arguments_size); + void **copied_arguments_end = copied_arguments_start + arguments_count; + int i; + + memcpy(copied_arguments_start, arguments_start, arguments_size); + + for (i = 0; i < arguments_count; i++) { + Z_ADDREF_P((zval *) arguments_start[i]); + } + + return copied_arguments_end; +} +/* }}} */ + /* * Local variables: * tab-width: 4 diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 6dfd607d1bbb..48f46bb8a448 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -434,6 +434,7 @@ ZEND_API int zend_do_fcall(ZEND_OPCODE_HANDLER_ARGS); void zend_clean_and_cache_symbol_table(HashTable *symbol_table); void zend_free_compiled_variables(zval ***CVs, int num); +void **zend_copy_arguments(void **arguments_end); #define CACHED_PTR(num) \ EG(active_op_array)->run_time_cache[(num)] diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index b5642ddcc7ad..6efa7106609b 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -94,6 +94,28 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio efree(generator->backed_up_stack); } + /* We have added an additional stack frame in prev_execute_data, so we + * have to free it. It also contains the arguments passed to the + * generator (for func_get_args) so those have to be freed too. */ + { + zend_execute_data *prev_execute_data = execute_data->prev_execute_data; + void **arguments = prev_execute_data->function_state.arguments; + + if (arguments) { + int arguments_count = (int) (zend_uintptr_t) *arguments; + zval **arguments_start = (zval **) (arguments - arguments_count); + int i; + + for (i = 0; i < arguments_count; ++i) { + zval_ptr_dtor(arguments_start + i); + } + + efree(arguments_start); + } + + efree(prev_execute_data); + } + efree(execute_data); generator->execute_data = NULL; } @@ -240,6 +262,18 @@ static void zend_generator_clone_storage(zend_generator *orig, zend_generator ** if (execute_data->object) { Z_ADDREF_P(execute_data->object); } + + /* Prev execute data contains an additional stack frame (for proper) + * backtraces) which has to be copied. */ + clone->execute_data->prev_execute_data = emalloc(sizeof(zend_execute_data)); + memcpy(clone->execute_data->prev_execute_data, execute_data->prev_execute_data, sizeof(zend_execute_data)); + + /* It also contains the arguments passed to the generator, which also + * have to be copied */ + if (execute_data->prev_execute_data->function_state.arguments) { + clone->execute_data->prev_execute_data->function_state.arguments + = zend_copy_arguments(execute_data->prev_execute_data->function_state.arguments); + } } /* The value and key are known not to be references, so simply add refs */ @@ -329,9 +363,13 @@ static void zend_generator_resume(zval *object, zend_generator *generator TSRMLS EG(scope) = generator->execute_data->current_scope; EG(called_scope) = generator->execute_data->current_called_scope; - /* Set prev_execute_data to the current execute_data to get halfways - * reasonable backtraces */ - generator->execute_data->prev_execute_data = original_execute_data; + /* We want the backtrace to look as if the generator function was + * called from whatever method we are current running (e.g. next()). + * The first prev_execute_data contains an additional stack frame, + * which makes the generator function show up in the backtrace and + * makes the arguments available to func_get_args(). So we have to + * set the prev_execute_data of that prev_execute_data :) */ + generator->execute_data->prev_execute_data->prev_execute_data = original_execute_data; /* Go to next opcode (we don't want to run the last one again) */ generator->execute_data->opline++; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index de63d8b5e22b..27afc49fa38f 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5220,7 +5220,8 @@ ZEND_VM_HANDLER(156, ZEND_SEPARATE, VAR, UNUSED) ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) { - zend_bool nested; + zend_bool nested = EX(nested); + zend_execute_data *prev_execute_data = EX(prev_execute_data); if (EG(return_value_ptr_ptr)) { zval *return_value; @@ -5240,11 +5241,23 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) /* back up the execution context */ generator = (zend_generator *) zend_object_store_get_object(return_value TSRMLS_CC); generator->execute_data = execute_data; + + /* We have to add another stack frame so the generator function shows + * up in backtraces and func_get_all() can access the function + * arguments. */ + EX(prev_execute_data) = emalloc(sizeof(zend_execute_data)); + if (prev_execute_data) { + memcpy(EX(prev_execute_data), prev_execute_data, sizeof(zend_execute_data)); + EX(prev_execute_data)->function_state.arguments = zend_copy_arguments(prev_execute_data->function_state.arguments); + } else { + memset(EX(prev_execute_data), 0, sizeof(zend_execute_data)); + EX(prev_execute_data)->function_state.function = (zend_function *) EX(op_array); + EX(prev_execute_data)->function_state.arguments = NULL; + } } /* restore the previous execution context */ - EG(current_execute_data) = EX(prev_execute_data); - nested = EX(nested); + EG(current_execute_data) = prev_execute_data; /* if there is no return value pointer we are responsible for freeing the * execution data */ @@ -5257,34 +5270,37 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) efree(execute_data); } - EG(opline_ptr) = NULL; - if (nested) { - /* so we can use EX() again */ - execute_data = EG(current_execute_data); - EG(opline_ptr) = &EX(opline); - EG(active_op_array) = EX(op_array); - EG(return_value_ptr_ptr) = EX(original_return_value); - EG(active_symbol_table) = EX(symbol_table); - EG(This) = EX(current_this); - EG(scope) = EX(current_scope); - EG(called_scope) = EX(current_called_scope); - - EX(function_state).function = (zend_function *) EX(op_array); - EX(function_state).arguments = NULL; + /* Happens whenever the function is invoked using call_user_function, + * e.g. when doing a dynamic function call using call_user_func(). */ + if (!nested) { + EG(opline_ptr) = NULL; + ZEND_VM_RETURN(); + } - EX(object) = EX(current_object); - EX(called_scope) = DECODE_CTOR(EX(called_scope)); + /* Bring back the previous execution context */ + execute_data = EG(current_execute_data); - zend_vm_stack_clear_multiple(TSRMLS_C); + EG(opline_ptr) = &EX(opline); + EG(active_op_array) = EX(op_array); + EG(return_value_ptr_ptr) = EX(original_return_value); + EG(active_symbol_table) = EX(symbol_table); + EG(This) = EX(current_this); + EG(scope) = EX(current_scope); + EG(called_scope) = EX(current_called_scope); + + EX(function_state).function = (zend_function *) EX(op_array); + EX(function_state).arguments = NULL; - LOAD_REGS(); - LOAD_OPLINE(); - ZEND_VM_INC_OPCODE(); - ZEND_VM_LEAVE(); - } + EX(object) = EX(current_object); + EX(called_scope) = DECODE_CTOR(EX(called_scope)); - ZEND_VM_RETURN(); + zend_vm_stack_clear_multiple(TSRMLS_C); + + LOAD_REGS(); + LOAD_OPLINE(); + ZEND_VM_INC_OPCODE(); + ZEND_VM_LEAVE(); } ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSED) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 98d849bbd688..987e4ceee033 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1203,7 +1203,8 @@ static int ZEND_FASTCALL ZEND_USER_OPCODE_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS static int ZEND_FASTCALL ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { - zend_bool nested; + zend_bool nested = EX(nested); + zend_execute_data *prev_execute_data = EX(prev_execute_data); if (EG(return_value_ptr_ptr)) { zval *return_value; @@ -1223,11 +1224,23 @@ static int ZEND_FASTCALL ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER(ZEND_OP /* back up the execution context */ generator = (zend_generator *) zend_object_store_get_object(return_value TSRMLS_CC); generator->execute_data = execute_data; + + /* We have to add another stack frame so the generator function shows + * up in backtraces and func_get_all() can access the function + * arguments. */ + EX(prev_execute_data) = emalloc(sizeof(zend_execute_data)); + if (prev_execute_data) { + memcpy(EX(prev_execute_data), prev_execute_data, sizeof(zend_execute_data)); + EX(prev_execute_data)->function_state.arguments = zend_copy_arguments(prev_execute_data->function_state.arguments); + } else { + memset(EX(prev_execute_data), 0, sizeof(zend_execute_data)); + EX(prev_execute_data)->function_state.function = (zend_function *) EX(op_array); + EX(prev_execute_data)->function_state.arguments = NULL; + } } /* restore the previous execution context */ - EG(current_execute_data) = EX(prev_execute_data); - nested = EX(nested); + EG(current_execute_data) = prev_execute_data; /* if there is no return value pointer we are responsible for freeing the * execution data */ @@ -1240,34 +1253,37 @@ static int ZEND_FASTCALL ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER(ZEND_OP efree(execute_data); } - EG(opline_ptr) = NULL; - if (nested) { - /* so we can use EX() again */ - execute_data = EG(current_execute_data); - EG(opline_ptr) = &EX(opline); - EG(active_op_array) = EX(op_array); - EG(return_value_ptr_ptr) = EX(original_return_value); - EG(active_symbol_table) = EX(symbol_table); - EG(This) = EX(current_this); - EG(scope) = EX(current_scope); - EG(called_scope) = EX(current_called_scope); + /* Happens whenever the function is invoked using call_user_function, + * e.g. when doing a dynamic function call using call_user_func(). */ + if (!nested) { + EG(opline_ptr) = NULL; + ZEND_VM_RETURN(); + } - EX(function_state).function = (zend_function *) EX(op_array); - EX(function_state).arguments = NULL; + /* Bring back the previous execution context */ + execute_data = EG(current_execute_data); - EX(object) = EX(current_object); - EX(called_scope) = DECODE_CTOR(EX(called_scope)); + EG(opline_ptr) = &EX(opline); + EG(active_op_array) = EX(op_array); + EG(return_value_ptr_ptr) = EX(original_return_value); + EG(active_symbol_table) = EX(symbol_table); + EG(This) = EX(current_this); + EG(scope) = EX(current_scope); + EG(called_scope) = EX(current_called_scope); - zend_vm_stack_clear_multiple(TSRMLS_C); + EX(function_state).function = (zend_function *) EX(op_array); + EX(function_state).arguments = NULL; - LOAD_REGS(); - LOAD_OPLINE(); - ZEND_VM_INC_OPCODE(); - ZEND_VM_LEAVE(); - } + EX(object) = EX(current_object); + EX(called_scope) = DECODE_CTOR(EX(called_scope)); - ZEND_VM_RETURN(); + zend_vm_stack_clear_multiple(TSRMLS_C); + + LOAD_REGS(); + LOAD_OPLINE(); + ZEND_VM_INC_OPCODE(); + ZEND_VM_LEAVE(); } static int ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) From d939d2dee58bf894ae9af631bdee173243157068 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 11 Jun 2012 23:59:46 +0200 Subject: [PATCH 41/70] Add sceleton for yield* expression This does not yet actually implement any delegation. --- .../yield_in_normal_function_error.phpt | 2 +- .../errors/yield_outside_function_error.phpt | 2 +- Zend/zend_compile.c | 23 +++++++++- Zend/zend_compile.h | 1 + Zend/zend_generators.c | 3 +- Zend/zend_language_parser.y | 1 + Zend/zend_language_scanner.c | 2 +- Zend/zend_language_scanner_defs.h | 2 +- Zend/zend_vm_def.h | 5 +++ Zend/zend_vm_execute.h | 45 +++++++++++++++++++ Zend/zend_vm_opcodes.h | 1 + 11 files changed, 80 insertions(+), 7 deletions(-) diff --git a/Zend/tests/generators/errors/yield_in_normal_function_error.phpt b/Zend/tests/generators/errors/yield_in_normal_function_error.phpt index 802510d29ccc..4670a4154285 100644 --- a/Zend/tests/generators/errors/yield_in_normal_function_error.phpt +++ b/Zend/tests/generators/errors/yield_in_normal_function_error.phpt @@ -9,4 +9,4 @@ function foo() { ?> --EXPECTF-- -Fatal error: The "yield" statement can only be used inside a generator function in %s on line %d +Fatal error: The "yield" expression can only be used inside a generator function in %s on line %d diff --git a/Zend/tests/generators/errors/yield_outside_function_error.phpt b/Zend/tests/generators/errors/yield_outside_function_error.phpt index fd7169d5f2e8..5f47e7536656 100644 --- a/Zend/tests/generators/errors/yield_outside_function_error.phpt +++ b/Zend/tests/generators/errors/yield_outside_function_error.phpt @@ -7,4 +7,4 @@ yield "Test"; ?> --EXPECTF-- -Fatal error: The "yield" statement can only be used inside a generator function in %s on line %d +Fatal error: The "yield" expression can only be used inside a generator function in %s on line %d diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index da61b766622f..35ff2bbf5223 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2667,7 +2667,7 @@ void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC zend_op *opline; if ((CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) == 0) { - zend_error(E_COMPILE_ERROR, "The \"yield\" statement can only be used inside a generator function"); + zend_error(E_COMPILE_ERROR, "The \"yield\" expression can only be used inside a generator function"); } opline = get_next_op(CG(active_op_array) TSRMLS_CC); @@ -2692,6 +2692,27 @@ void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC } /* }}} */ +void zend_do_delegate_yield(znode *result, const znode *value TSRMLS_DC) /* {{{ */ +{ + zend_op *opline; + + if ((CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) == 0) { + zend_error(E_COMPILE_ERROR, "The \"yield*\" expression can only be used inside a generator function"); + } + + opline = get_next_op(CG(active_op_array) TSRMLS_CC); + + opline->opcode = ZEND_DELEGATE_YIELD; + + SET_NODE(opline->op1, value); + SET_UNUSED(opline->op2); + + opline->result_type = IS_VAR; + opline->result.var = get_temporary_variable(CG(active_op_array)); + GET_NODE(result, opline->result); +} +/* }}} */ + void zend_do_suspend_if_generator(TSRMLS_D) /* {{{ */ { zend_op *opline; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index ec86ed86de0e..d0587d1faa4d 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -491,6 +491,7 @@ int zend_do_begin_class_member_function_call(znode *class_name, znode *method_na void zend_do_end_function_call(znode *function_name, znode *result, const znode *argument_list, int is_method, int is_dynamic_fcall TSRMLS_DC); void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC); void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC); +void zend_do_delegate_yield(znode *result, const znode *value TSRMLS_DC); void zend_do_suspend_if_generator(TSRMLS_D); void zend_do_handle_exception(TSRMLS_D); diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 6efa7106609b..f8374b8a65e5 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -245,7 +245,7 @@ static void zend_generator_clone_storage(zend_generator *orig, zend_generator ** } /* Update the send_target to use the temporary variable with the same - * offset as the original generator, but in out temporary variable + * offset as the original generator, but in our temporary variable * memory segment. */ if (orig->send_target) { size_t offset = (char *) orig->send_target - (char *) execute_data->Ts; @@ -536,7 +536,6 @@ ZEND_METHOD(Generator, send) /* The sent value was initialized to NULL, so dtor that */ zval_ptr_dtor(&generator->send_target->var.ptr); - /* Set new sent value */ Z_ADDREF_P(value); generator->send_target->var.ptr = value; diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 16c0ea2b2c37..02aa694c6eb6 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -804,6 +804,7 @@ expr_without_variable: | T_PRINT expr { zend_do_print(&$$, &$2 TSRMLS_CC); } | T_YIELD { zend_do_yield(&$$, NULL, NULL TSRMLS_CC); } | T_YIELD expr { zend_do_yield(&$$, &$2, NULL TSRMLS_CC); } + | T_YIELD '*' expr { zend_do_delegate_yield(&$$, &$3 TSRMLS_CC); } | function is_generator is_reference { zend_do_begin_lambda_function_declaration(&$$, &$1, $2.op_type, $3.op_type, 0 TSRMLS_CC); } '(' parameter_list ')' lexical_vars { zend_do_suspend_if_generator(TSRMLS_C); } '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); $$ = $4; } diff --git a/Zend/zend_language_scanner.c b/Zend/zend_language_scanner.c index a388e96a262b..e20fc19581a2 100644 --- a/Zend/zend_language_scanner.c +++ b/Zend/zend_language_scanner.c @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 on Tue May 15 13:07:23 2012 */ +/* Generated by re2c 0.13.5 on Sat Jun 16 12:57:03 2012 */ #line 1 "Zend/zend_language_scanner.l" /* +----------------------------------------------------------------------+ diff --git a/Zend/zend_language_scanner_defs.h b/Zend/zend_language_scanner_defs.h index 981c342e19c0..869d54349d99 100644 --- a/Zend/zend_language_scanner_defs.h +++ b/Zend/zend_language_scanner_defs.h @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 on Tue May 15 13:07:23 2012 */ +/* Generated by re2c 0.13.5 on Sat Jun 16 12:57:03 2012 */ #line 3 "Zend/zend_language_scanner_defs.h" enum YYCONDTYPE { diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 27afc49fa38f..e5d8a7624a9a 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5406,4 +5406,9 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE ZEND_VM_RETURN(); } +ZEND_VM_HANDLER(161, ZEND_DELEGATE_YIELD, CONST|TMP|VAR|CV, ANY) +{ + ZEND_VM_NEXT_OPCODE(); +} + ZEND_VM_EXPORT_HELPER(zend_do_fcall, zend_do_fcall_common_helper) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 987e4ceee033..6f1a885bb65b 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3079,6 +3079,11 @@ static int ZEND_FASTCALL ZEND_QM_ASSIGN_VAR_SPEC_CONST_HANDLER(ZEND_OPCODE_HAND ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_DELEGATE_YIELD_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + ZEND_VM_NEXT_OPCODE(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -8182,6 +8187,11 @@ static int ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_A ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_DELEGATE_YIELD_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + ZEND_VM_NEXT_OPCODE(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -13362,6 +13372,11 @@ static int ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_A ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_DELEGATE_YIELD_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + ZEND_VM_NEXT_OPCODE(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -30224,6 +30239,11 @@ static int ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_AR ZEND_VM_NEXT_OPCODE(); } +static int ZEND_FASTCALL ZEND_DELEGATE_YIELD_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + ZEND_VM_NEXT_OPCODE(); +} + static int ZEND_FASTCALL ZEND_ADD_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -43545,6 +43565,31 @@ void zend_init_opcodes_handlers(void) ZEND_YIELD_SPEC_CV_VAR_HANDLER, ZEND_YIELD_SPEC_CV_UNUSED_HANDLER, ZEND_YIELD_SPEC_CV_CV_HANDLER, + ZEND_DELEGATE_YIELD_SPEC_CONST_HANDLER, + ZEND_DELEGATE_YIELD_SPEC_CONST_HANDLER, + ZEND_DELEGATE_YIELD_SPEC_CONST_HANDLER, + ZEND_DELEGATE_YIELD_SPEC_CONST_HANDLER, + ZEND_DELEGATE_YIELD_SPEC_CONST_HANDLER, + ZEND_DELEGATE_YIELD_SPEC_TMP_HANDLER, + ZEND_DELEGATE_YIELD_SPEC_TMP_HANDLER, + ZEND_DELEGATE_YIELD_SPEC_TMP_HANDLER, + ZEND_DELEGATE_YIELD_SPEC_TMP_HANDLER, + ZEND_DELEGATE_YIELD_SPEC_TMP_HANDLER, + ZEND_DELEGATE_YIELD_SPEC_VAR_HANDLER, + ZEND_DELEGATE_YIELD_SPEC_VAR_HANDLER, + ZEND_DELEGATE_YIELD_SPEC_VAR_HANDLER, + ZEND_DELEGATE_YIELD_SPEC_VAR_HANDLER, + ZEND_DELEGATE_YIELD_SPEC_VAR_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_NULL_HANDLER, + ZEND_DELEGATE_YIELD_SPEC_CV_HANDLER, + ZEND_DELEGATE_YIELD_SPEC_CV_HANDLER, + ZEND_DELEGATE_YIELD_SPEC_CV_HANDLER, + ZEND_DELEGATE_YIELD_SPEC_CV_HANDLER, + ZEND_DELEGATE_YIELD_SPEC_CV_HANDLER, ZEND_NULL_HANDLER }; zend_opcode_handlers = (opcode_handler_t*)labels; diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 53e00a1743c3..46a711d17931 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -161,3 +161,4 @@ #define ZEND_JMP_SET_VAR 158 #define ZEND_SUSPEND_AND_RETURN_GENERATOR 159 #define ZEND_YIELD 160 +#define ZEND_DELEGATE_YIELD 161 From 6233408a2a51389a2bbd250c5900d395648e897f Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 20 Jun 2012 21:31:23 +0200 Subject: [PATCH 42/70] Fix thread safe build --- Zend/zend_execute.c | 2 +- Zend/zend_execute.h | 2 +- Zend/zend_generators.c | 4 ++-- Zend/zend_vm_def.h | 6 +++--- Zend/zend_vm_execute.h | 6 +++--- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index c7ef212887e9..0e40650fb5e7 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1538,7 +1538,7 @@ ZEND_API zval **zend_get_zval_ptr_ptr(int op_type, const znode_op *node, const t return get_zval_ptr_ptr(op_type, node, Ts, should_free, type); } -void zend_clean_and_cache_symbol_table(HashTable *symbol_table) /* {{{ */ +void zend_clean_and_cache_symbol_table(HashTable *symbol_table TSRMLS_DC) /* {{{ */ { if (EG(symtable_cache_ptr) >= EG(symtable_cache_limit)) { zend_hash_destroy(symbol_table); diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 48f46bb8a448..92160b54eb0a 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -432,7 +432,7 @@ ZEND_API zval **zend_get_zval_ptr_ptr(int op_type, const znode_op *node, const t ZEND_API int zend_do_fcall(ZEND_OPCODE_HANDLER_ARGS); -void zend_clean_and_cache_symbol_table(HashTable *symbol_table); +void zend_clean_and_cache_symbol_table(HashTable *symbol_table TSRMLS_DC); void zend_free_compiled_variables(zval ***CVs, int num); void **zend_copy_arguments(void **arguments_end); diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index f8374b8a65e5..b7538e7c9ef7 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -34,7 +34,7 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio if (!execute_data->symbol_table) { zend_free_compiled_variables(execute_data->CVs, execute_data->op_array->last_var); } else { - zend_clean_and_cache_symbol_table(execute_data->symbol_table); + zend_clean_and_cache_symbol_table(execute_data->symbol_table TSRMLS_CC); } if (execute_data->current_this) { @@ -560,7 +560,7 @@ ZEND_METHOD(Generator, close) generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); - zend_generator_close(generator, 0); + zend_generator_close(generator, 0 TSRMLS_CC); } /* }}} */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index e5d8a7624a9a..fea1f404e14f 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2572,7 +2572,7 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY) EG(active_op_array) = EX(op_array); EG(return_value_ptr_ptr) = EX(original_return_value); if (EG(active_symbol_table)) { - zend_clean_and_cache_symbol_table(EG(active_symbol_table)); + zend_clean_and_cache_symbol_table(EG(active_symbol_table) TSRMLS_CC); } EG(active_symbol_table) = EX(symbol_table); @@ -2717,7 +2717,7 @@ ZEND_VM_HELPER(zend_do_fcall_common_helper, ANY, ANY) EG(active_op_array) = EX(op_array); EG(return_value_ptr_ptr) = EX(original_return_value); if (EG(active_symbol_table)) { - zend_clean_and_cache_symbol_table(EG(active_symbol_table)); + zend_clean_and_cache_symbol_table(EG(active_symbol_table) TSRMLS_CC); } EG(active_symbol_table) = EX(symbol_table); } else { /* ZEND_OVERLOADED_FUNCTION */ @@ -5265,7 +5265,7 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) if (!EG(active_symbol_table)) { zend_free_compiled_variables(EX_CVs(), execute_data->op_array->last_var); } else { - zend_clean_and_cache_symbol_table(EG(active_symbol_table)); + zend_clean_and_cache_symbol_table(EG(active_symbol_table) TSRMLS_CC); } efree(execute_data); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 6f1a885bb65b..0f965c5eef78 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -556,7 +556,7 @@ static int ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS) EG(active_op_array) = EX(op_array); EG(return_value_ptr_ptr) = EX(original_return_value); if (EG(active_symbol_table)) { - zend_clean_and_cache_symbol_table(EG(active_symbol_table)); + zend_clean_and_cache_symbol_table(EG(active_symbol_table) TSRMLS_CC); } EG(active_symbol_table) = EX(symbol_table); @@ -701,7 +701,7 @@ static int ZEND_FASTCALL zend_do_fcall_common_helper_SPEC(ZEND_OPCODE_HANDLER_AR EG(active_op_array) = EX(op_array); EG(return_value_ptr_ptr) = EX(original_return_value); if (EG(active_symbol_table)) { - zend_clean_and_cache_symbol_table(EG(active_symbol_table)); + zend_clean_and_cache_symbol_table(EG(active_symbol_table) TSRMLS_CC); } EG(active_symbol_table) = EX(symbol_table); } else { /* ZEND_OVERLOADED_FUNCTION */ @@ -1248,7 +1248,7 @@ static int ZEND_FASTCALL ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER(ZEND_OP if (!EG(active_symbol_table)) { zend_free_compiled_variables(EX_CVs(), execute_data->op_array->last_var); } else { - zend_clean_and_cache_symbol_table(EG(active_symbol_table)); + zend_clean_and_cache_symbol_table(EG(active_symbol_table) TSRMLS_CC); } efree(execute_data); } From 1d3f37ddedc931d700a4e1135f63e094df88dbc4 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 23 Jun 2012 00:03:28 +0200 Subject: [PATCH 43/70] Fix segfault in method test A ref has to be added to $this if the generator is called !nested (which is the case when it is invoked via getIterator). --- Zend/zend_vm_def.h | 16 +++++++++++++--- Zend/zend_vm_execute.h | 16 +++++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index fea1f404e14f..cb24eb990469 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5234,10 +5234,15 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) /* back up some executor globals */ SAVE_OPLINE(); - EX(current_this) = EG(This); + EX(current_scope) = EG(scope); EX(current_called_scope) = EG(called_scope); + if (EG(This)) { + Z_ADDREF_P(EG(This)); + } + EX(current_this) = EG(This); + /* back up the execution context */ generator = (zend_generator *) zend_object_store_get_object(return_value TSRMLS_CC); generator->execute_data = execute_data; @@ -5278,6 +5283,13 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) ZEND_VM_RETURN(); } + /* Free $this and stack arguments */ + if (EG(This)) { + zval_ptr_dtor(&EG(This)); + } + + zend_vm_stack_clear_multiple(TSRMLS_C); + /* Bring back the previous execution context */ execute_data = EG(current_execute_data); @@ -5295,8 +5307,6 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) EX(object) = EX(current_object); EX(called_scope) = DECODE_CTOR(EX(called_scope)); - zend_vm_stack_clear_multiple(TSRMLS_C); - LOAD_REGS(); LOAD_OPLINE(); ZEND_VM_INC_OPCODE(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 0f965c5eef78..3bf89d317c69 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1217,10 +1217,15 @@ static int ZEND_FASTCALL ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER(ZEND_OP /* back up some executor globals */ SAVE_OPLINE(); - EX(current_this) = EG(This); + EX(current_scope) = EG(scope); EX(current_called_scope) = EG(called_scope); + if (EG(This)) { + Z_ADDREF_P(EG(This)); + } + EX(current_this) = EG(This); + /* back up the execution context */ generator = (zend_generator *) zend_object_store_get_object(return_value TSRMLS_CC); generator->execute_data = execute_data; @@ -1261,6 +1266,13 @@ static int ZEND_FASTCALL ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER(ZEND_OP ZEND_VM_RETURN(); } + /* Free $this and stack arguments */ + if (EG(This)) { + zval_ptr_dtor(&EG(This)); + } + + zend_vm_stack_clear_multiple(TSRMLS_C); + /* Bring back the previous execution context */ execute_data = EG(current_execute_data); @@ -1278,8 +1290,6 @@ static int ZEND_FASTCALL ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER(ZEND_OP EX(object) = EX(current_object); EX(called_scope) = DECODE_CTOR(EX(called_scope)); - zend_vm_stack_clear_multiple(TSRMLS_C); - LOAD_REGS(); LOAD_OPLINE(); ZEND_VM_INC_OPCODE(); From 04e781f0e4a4ea879c5e85b8d209b9b44cc32f8d Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 23 Jun 2012 01:28:16 +0200 Subject: [PATCH 44/70] Implement get_iterator This implements the get_iterator handler for Generator objects, thus making direct foreach() iteration significantly faster. --- Zend/zend_generators.c | 109 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index b7538e7c9ef7..10d91f5b1120 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -564,6 +564,114 @@ ZEND_METHOD(Generator, close) } /* }}} */ +/* get_iterator implementation */ + +typedef struct _zend_generator_iterator { + zend_object_iterator intern; + zend_generator *generator; +} zend_generator_iterator; + +static void zend_generator_iterator_dtor(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */ +{ + zval_ptr_dtor((zval **) &iterator->data); + efree(iterator); +} +/* }}} */ + +static int zend_generator_iterator_valid(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */ +{ + zval *object = (zval *) iterator->data; + zend_generator *generator = ((zend_generator_iterator *) iterator)->generator; + + zend_generator_ensure_initialized(object, generator TSRMLS_CC); + + return generator->value != NULL ? SUCCESS : FAILURE; +} +/* }}} */ + +static void zend_generator_iterator_get_data(zend_object_iterator *iterator, zval ***data TSRMLS_DC) /* {{{ */ +{ + zval *object = (zval *) iterator->data; + zend_generator *generator = ((zend_generator_iterator *) iterator)->generator; + + zend_generator_ensure_initialized(object, generator TSRMLS_CC); + + if (generator->value) { + *data = &generator->value; + } else { + *data = NULL; + } +} +/* }}} */ + +static int zend_generator_iterator_get_key(zend_object_iterator *iterator, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) /* {{{ */ +{ + zval *object = (zval *) iterator->data; + zend_generator *generator = ((zend_generator_iterator *) iterator)->generator; + + zend_generator_ensure_initialized(object, generator TSRMLS_CC); + + if (!generator->key) { + return HASH_KEY_NON_EXISTANT; + } + + if (Z_TYPE_P(generator->key) == IS_LONG) { + *int_key = Z_LVAL_P(generator->key); + return HASH_KEY_IS_LONG; + } + + if (Z_TYPE_P(generator->key) == IS_STRING) { + *str_key = estrndup(Z_STRVAL_P(generator->key), Z_STRLEN_P(generator->key)); + *str_key_len = Z_STRLEN_P(generator->key) + 1; + return HASH_KEY_IS_STRING; + } + + /* Waiting for Etienne's patch to allow arbitrary zval keys. Until then + * error out on non-int and non-string keys. */ + zend_error(E_ERROR, "Currently only int and string keys can be yielded"); +} +/* }}} */ + +static void zend_generator_iterator_move_forward(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */ +{ + zval *object = (zval *) iterator->data; + zend_generator *generator = ((zend_generator_iterator *) iterator)->generator; + + zend_generator_ensure_initialized(object, generator TSRMLS_CC); + + zend_generator_resume(object, generator TSRMLS_CC); +} +/* }}} */ + +static zend_object_iterator_funcs zend_generator_iterator_functions = { + zend_generator_iterator_dtor, + zend_generator_iterator_valid, + zend_generator_iterator_get_data, + zend_generator_iterator_get_key, + zend_generator_iterator_move_forward, + NULL +}; + +zend_object_iterator *zend_generator_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC) /* {{{ */ +{ + zend_generator_iterator *iterator; + + if (by_ref) { + zend_error(E_ERROR, "By reference iteration of generators is currently not supported"); + } + + iterator = emalloc(sizeof(zend_generator_iterator)); + iterator->intern.funcs = &zend_generator_iterator_functions; + + Z_ADDREF_P(object); + iterator->intern.data = (void *) object; + + iterator->generator = zend_object_store_get_object(object TSRMLS_CC); + + return (zend_object_iterator *) iterator; +} +/* }}} */ + ZEND_BEGIN_ARG_INFO(arginfo_generator_void, 0) ZEND_END_ARG_INFO() @@ -590,6 +698,7 @@ void zend_register_generator_ce(TSRMLS_D) /* {{{ */ zend_ce_generator = zend_register_internal_class(&ce TSRMLS_CC); zend_ce_generator->ce_flags |= ZEND_ACC_FINAL_CLASS; zend_ce_generator->create_object = zend_generator_create; + zend_ce_generator->get_iterator = zend_generator_get_iterator; zend_class_implements(zend_ce_generator TSRMLS_CC, 1, zend_ce_iterator); From 14766e1417c721d9643f6a2a785db3e88b565814 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 23 Jun 2012 14:43:52 +0200 Subject: [PATCH 45/70] Pass zend_generator directly to Zend VM Previously the zval* of the generator was passed into the VM by misusing EG(return_value_ptr_ptr). Now the zend_generator* itself is directly passed in. This saves us from always having to pass the zval* around everywhere. --- Zend/zend_generators.c | 93 ++++++++++++++++++++---------------------- Zend/zend_vm_def.h | 8 ++-- Zend/zend_vm_execute.h | 62 ++++++++++++++-------------- 3 files changed, 79 insertions(+), 84 deletions(-) diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 10d91f5b1120..487975e23e6d 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -321,7 +321,7 @@ static zend_function *zend_generator_get_constructor(zval *object TSRMLS_DC) /* } /* }}} */ -static void zend_generator_resume(zval *object, zend_generator *generator TSRMLS_DC) /* {{{ */ +static void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */ { /* The generator is already closed, thus can't resume */ if (!generator->execute_data) { @@ -352,7 +352,7 @@ static void zend_generator_resume(zval *object, zend_generator *generator TSRMLS /* We (mis)use the return_value_ptr_ptr to provide the generator object * to the executor, so YIELD will be able to set the yielded value */ - EG(return_value_ptr_ptr) = &object; + EG(return_value_ptr_ptr) = (zval **) generator; /* Set executor globals */ EG(current_execute_data) = generator->execute_data; @@ -399,10 +399,10 @@ static void zend_generator_resume(zval *object, zend_generator *generator TSRMLS } /* }}} */ -static void zend_generator_ensure_initialized(zval *object, zend_generator *generator TSRMLS_DC) /* {{{ */ +static void zend_generator_ensure_initialized(zend_generator *generator TSRMLS_DC) /* {{{ */ { if (!generator->value) { - zend_generator_resume(object, generator TSRMLS_CC); + zend_generator_resume(generator TSRMLS_CC); } } /* }}} */ @@ -411,17 +411,15 @@ static void zend_generator_ensure_initialized(zval *object, zend_generator *gene * Rewind the generator */ ZEND_METHOD(Generator, rewind) { - zval *object; zend_generator *generator; if (zend_parse_parameters_none() == FAILURE) { return; } - object = getThis(); - generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); + generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); - zend_generator_ensure_initialized(object, generator TSRMLS_CC); + zend_generator_ensure_initialized(generator TSRMLS_CC); /* Generators aren't rewindable, so rewind() only has to make sure that * the generator is initialized, nothing more */ @@ -432,17 +430,15 @@ ZEND_METHOD(Generator, rewind) * Check whether the generator is valid */ ZEND_METHOD(Generator, valid) { - zval *object; zend_generator *generator; if (zend_parse_parameters_none() == FAILURE) { return; } - object = getThis(); - generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); + generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); - zend_generator_ensure_initialized(object, generator TSRMLS_CC); + zend_generator_ensure_initialized(generator TSRMLS_CC); RETURN_BOOL(generator->value != NULL); } @@ -452,17 +448,15 @@ ZEND_METHOD(Generator, valid) * Get the current value */ ZEND_METHOD(Generator, current) { - zval *object; zend_generator *generator; if (zend_parse_parameters_none() == FAILURE) { return; } - object = getThis(); - generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); + generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); - zend_generator_ensure_initialized(object, generator TSRMLS_CC); + zend_generator_ensure_initialized(generator TSRMLS_CC); if (generator->value) { RETURN_ZVAL(generator->value, 1, 0); @@ -474,17 +468,15 @@ ZEND_METHOD(Generator, current) * Get the current key */ ZEND_METHOD(Generator, key) { - zval *object; zend_generator *generator; if (zend_parse_parameters_none() == FAILURE) { return; } - object = getThis(); - generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); + generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); - zend_generator_ensure_initialized(object, generator TSRMLS_CC); + zend_generator_ensure_initialized(generator TSRMLS_CC); if (generator->key) { RETURN_ZVAL(generator->key, 1, 0); @@ -496,19 +488,17 @@ ZEND_METHOD(Generator, key) * Advances the generator */ ZEND_METHOD(Generator, next) { - zval *object; zend_generator *generator; if (zend_parse_parameters_none() == FAILURE) { return; } - object = getThis(); - generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); + generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); - zend_generator_ensure_initialized(object, generator TSRMLS_CC); + zend_generator_ensure_initialized(generator TSRMLS_CC); - zend_generator_resume(object, generator TSRMLS_CC); + zend_generator_resume(generator TSRMLS_CC); } /* }}} */ @@ -516,17 +506,16 @@ ZEND_METHOD(Generator, next) * Sends a value to the generator */ ZEND_METHOD(Generator, send) { - zval *object, *value; + zval *value; zend_generator *generator; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) { return; } - object = getThis(); - generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); + generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); - zend_generator_ensure_initialized(object, generator TSRMLS_CC); + zend_generator_ensure_initialized(generator TSRMLS_CC); /* The generator is already closed, thus can't send anything */ if (!generator->execute_data) { @@ -541,7 +530,7 @@ ZEND_METHOD(Generator, send) generator->send_target->var.ptr = value; generator->send_target->var.ptr_ptr = &value; - zend_generator_resume(object, generator TSRMLS_CC); + zend_generator_resume(generator TSRMLS_CC); if (generator->value) { RETURN_ZVAL(generator->value, 1, 0); @@ -568,22 +557,27 @@ ZEND_METHOD(Generator, close) typedef struct _zend_generator_iterator { zend_object_iterator intern; - zend_generator *generator; + + /* The generator object zval has to be stored, because the iterator is + * holding a ref to it, which has to be dtored. */ + zval *object; } zend_generator_iterator; static void zend_generator_iterator_dtor(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */ { - zval_ptr_dtor((zval **) &iterator->data); + zval *object = ((zend_generator_iterator *) iterator)->object; + + zval_ptr_dtor(&object); + efree(iterator); } /* }}} */ static int zend_generator_iterator_valid(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */ { - zval *object = (zval *) iterator->data; - zend_generator *generator = ((zend_generator_iterator *) iterator)->generator; + zend_generator *generator = (zend_generator *) iterator->data; - zend_generator_ensure_initialized(object, generator TSRMLS_CC); + zend_generator_ensure_initialized(generator TSRMLS_CC); return generator->value != NULL ? SUCCESS : FAILURE; } @@ -591,10 +585,9 @@ static int zend_generator_iterator_valid(zend_object_iterator *iterator TSRMLS_D static void zend_generator_iterator_get_data(zend_object_iterator *iterator, zval ***data TSRMLS_DC) /* {{{ */ { - zval *object = (zval *) iterator->data; - zend_generator *generator = ((zend_generator_iterator *) iterator)->generator; + zend_generator *generator = (zend_generator *) iterator->data; - zend_generator_ensure_initialized(object, generator TSRMLS_CC); + zend_generator_ensure_initialized(generator TSRMLS_CC); if (generator->value) { *data = &generator->value; @@ -606,10 +599,9 @@ static void zend_generator_iterator_get_data(zend_object_iterator *iterator, zva static int zend_generator_iterator_get_key(zend_object_iterator *iterator, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) /* {{{ */ { - zval *object = (zval *) iterator->data; - zend_generator *generator = ((zend_generator_iterator *) iterator)->generator; + zend_generator *generator = (zend_generator *) iterator->data; - zend_generator_ensure_initialized(object, generator TSRMLS_CC); + zend_generator_ensure_initialized(generator TSRMLS_CC); if (!generator->key) { return HASH_KEY_NON_EXISTANT; @@ -628,18 +620,17 @@ static int zend_generator_iterator_get_key(zend_object_iterator *iterator, char /* Waiting for Etienne's patch to allow arbitrary zval keys. Until then * error out on non-int and non-string keys. */ - zend_error(E_ERROR, "Currently only int and string keys can be yielded"); + zend_error_noreturn(E_ERROR, "Currently only int and string keys can be yielded"); } /* }}} */ static void zend_generator_iterator_move_forward(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */ { - zval *object = (zval *) iterator->data; - zend_generator *generator = ((zend_generator_iterator *) iterator)->generator; + zend_generator *generator = (zend_generator *) iterator->data; - zend_generator_ensure_initialized(object, generator TSRMLS_CC); + zend_generator_ensure_initialized(generator TSRMLS_CC); - zend_generator_resume(object, generator TSRMLS_CC); + zend_generator_resume(generator TSRMLS_CC); } /* }}} */ @@ -655,18 +646,22 @@ static zend_object_iterator_funcs zend_generator_iterator_functions = { zend_object_iterator *zend_generator_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC) /* {{{ */ { zend_generator_iterator *iterator; + zend_generator *generator; if (by_ref) { zend_error(E_ERROR, "By reference iteration of generators is currently not supported"); } + generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); + iterator = emalloc(sizeof(zend_generator_iterator)); iterator->intern.funcs = &zend_generator_iterator_functions; + iterator->intern.data = (void *) generator; + /* We have to keep a reference to the generator object zval around, + * otherwise the generator may be destroyed during iteration. */ Z_ADDREF_P(object); - iterator->intern.data = (void *) object; - - iterator->generator = zend_object_store_get_object(object TSRMLS_CC); + iterator->object = object; return (zend_object_iterator *) iterator; } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index cb24eb990469..015263f52f5e 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2819,7 +2819,7 @@ ZEND_VM_HANDLER(62, ZEND_RETURN, CONST|TMP|VAR|CV, ANY) if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Close the generator to free up resources */ zend_generator_close(generator, 1 TSRMLS_CC); @@ -4998,7 +4998,7 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) * arguments that have to be dtor'ed) starts */ if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* For generators the next stack frame is conveniently stored in the * generator object. */ @@ -5087,7 +5087,7 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) /* For generators skip the leave handler return directly */ if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Close the generator to free up resources */ zend_generator_close(generator, 1 TSRMLS_CC); @@ -5318,7 +5318,7 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 3bf89d317c69..29b6d7c500c4 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1061,7 +1061,7 @@ static int ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER * arguments that have to be dtor'ed) starts */ if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* For generators the next stack frame is conveniently stored in the * generator object. */ @@ -1150,7 +1150,7 @@ static int ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER /* For generators skip the leave handler return directly */ if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Close the generator to free up resources */ zend_generator_close(generator, 1 TSRMLS_CC); @@ -2371,7 +2371,7 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARG if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Close the generator to free up resources */ zend_generator_close(generator, 1 TSRMLS_CC); @@ -4119,7 +4119,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLE USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -4760,7 +4760,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMP_HANDLER(ZEND_OPCODE_HANDLER_ USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -5726,7 +5726,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -6387,7 +6387,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDL USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -7087,7 +7087,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_A USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -7426,7 +7426,7 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Close the generator to free up resources */ zend_generator_close(generator, 1 TSRMLS_CC); @@ -9095,7 +9095,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -9736,7 +9736,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_TMP_HANDLER(ZEND_OPCODE_HANDLER_AR USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -10702,7 +10702,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_VAR_HANDLER(ZEND_OPCODE_HANDLER_AR USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -11229,7 +11229,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCODE_HANDLER USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -11867,7 +11867,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARG USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -12386,7 +12386,7 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Close the generator to free up resources */ zend_generator_close(generator, 1 TSRMLS_CC); @@ -15731,7 +15731,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -17759,7 +17759,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_TMP_HANDLER(ZEND_OPCODE_HANDLER_AR USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -20167,7 +20167,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_AR USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -21254,7 +21254,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -23331,7 +23331,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARG USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -24767,7 +24767,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDL USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -26026,7 +26026,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER(ZEND_OPCODE_HANDLER USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -27285,7 +27285,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER(ZEND_OPCODE_HANDLER USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -27656,7 +27656,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HAND USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -28912,7 +28912,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -29412,7 +29412,7 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Close the generator to free up resources */ zend_generator_close(generator, 1 TSRMLS_CC); @@ -32377,7 +32377,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_A USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -34275,7 +34275,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARG USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -36552,7 +36552,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -37499,7 +37499,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { @@ -39445,7 +39445,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS USE_OPLINE /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC); + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); /* Destroy the previously yielded value */ if (generator->value) { From ab75ed6ba9b5eace710976bf76d631728d4bb403 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 23 Jun 2012 16:08:15 +0200 Subject: [PATCH 46/70] Disallow closing a generator during its execution If a generator is closed while it is running an E_WARNING is thrown and the call is ignored. Maybe a fatal error should be thrown instead? --- Zend/zend_generators.c | 13 +++++++++++++ Zend/zend_generators.h | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 487975e23e6d..bccbb48ca417 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -300,6 +300,8 @@ static zend_object_value zend_generator_create(zend_class_entry *class_type TSRM /* The key will be incremented on first use, so it'll start at 0 */ generator->largest_used_integer_key = -1; + generator->is_currently_running = 0; + zend_object_std_init(&generator->std, class_type TSRMLS_CC); object.handle = zend_objects_store_put(generator, NULL, @@ -339,6 +341,8 @@ static void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */ zend_class_entry *original_scope = EG(scope); zend_class_entry *original_called_scope = EG(called_scope); + zend_bool original_is_currently_running = generator->is_currently_running; + /* Remember the current stack position so we can back up pushed args */ generator->original_stack_top = zend_vm_stack_top(TSRMLS_C); @@ -363,6 +367,8 @@ static void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */ EG(scope) = generator->execute_data->current_scope; EG(called_scope) = generator->execute_data->current_called_scope; + generator->is_currently_running = 1; + /* We want the backtrace to look as if the generator function was * called from whatever method we are current running (e.g. next()). * The first prev_execute_data contains an additional stack frame, @@ -387,6 +393,8 @@ static void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */ EG(scope) = original_scope; EG(called_scope) = original_called_scope; + generator->is_currently_running = original_is_currently_running; + /* The stack top before and after the execution differ, i.e. there are * arguments pushed to the stack. */ if (generator->original_stack_top != zend_vm_stack_top(TSRMLS_C)) { @@ -549,6 +557,11 @@ ZEND_METHOD(Generator, close) generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); + if (generator->is_currently_running) { + zend_error(E_WARNING, "A generator cannot be closed while it is running"); + return; + } + zend_generator_close(generator, 0 TSRMLS_CC); } /* }}} */ diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index 89193bbe6e46..73d85287a705 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -46,6 +46,10 @@ typedef struct _zend_generator { temp_variable *send_target; /* Largest used integer key for auto-incrementing keys */ long largest_used_integer_key; + + /* We need to know whether the generator is currently executed to avoid it + * being closed while still running */ + zend_bool is_currently_running; } zend_generator; extern ZEND_API zend_class_entry *zend_ce_generator; From 5a9bddba6699d056383107728392048cd7ccb598 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 25 Jun 2012 21:41:50 +0200 Subject: [PATCH 47/70] Forgot to git add two tests --- .../generators/close_inside_generator.phpt | 22 +++++++++++++++++++ Zend/tests/generators/func_get_args.phpt | 20 +++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 Zend/tests/generators/close_inside_generator.phpt create mode 100644 Zend/tests/generators/func_get_args.phpt diff --git a/Zend/tests/generators/close_inside_generator.phpt b/Zend/tests/generators/close_inside_generator.phpt new file mode 100644 index 000000000000..41a91c9fc7b9 --- /dev/null +++ b/Zend/tests/generators/close_inside_generator.phpt @@ -0,0 +1,22 @@ +--TEST-- +Calling close() during the exectution of the generator +--FILE-- +close(); + + echo "Still running"; +} + +$gen = gen(); +$gen->send($gen); + +?> +--EXPECTF-- +Warning: A generator cannot be closed while it is running in %s on line %d +Still running diff --git a/Zend/tests/generators/func_get_args.phpt b/Zend/tests/generators/func_get_args.phpt new file mode 100644 index 000000000000..7ce7fb002f0a --- /dev/null +++ b/Zend/tests/generators/func_get_args.phpt @@ -0,0 +1,20 @@ +--TEST-- +func_get_args() can be used inside generator functions +--FILE-- +rewind(); + +?> +--EXPECT-- +array(2) { + [0]=> + string(3) "foo" + [1]=> + string(3) "bar" +} From 85f077cea13b3cb4927453b8a2f8ce51a9461bbb Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 17 Jul 2012 13:24:27 +0200 Subject: [PATCH 48/70] Add support by yielding by-reference --- ...n_ref_generator_iterated_by_ref_error.phpt | 13 + .../errors/yield_const_by_ref_error.phpt | 16 + ...ld_non_ref_function_call_by_ref_error.phpt | 20 + .../generators/generator_method_by_ref.phpt | 44 + Zend/tests/generators/yield_by_reference.phpt | 32 + .../yield_ref_function_call_by_reference.phpt | 24 + Zend/zend_compile.c | 12 +- Zend/zend_generators.c | 8 +- Zend/zend_vm_def.h | 81 +- Zend/zend_vm_execute.h | 1935 +++++++++++++---- 10 files changed, 1746 insertions(+), 439 deletions(-) create mode 100644 Zend/tests/generators/errors/non_ref_generator_iterated_by_ref_error.phpt create mode 100644 Zend/tests/generators/errors/yield_const_by_ref_error.phpt create mode 100644 Zend/tests/generators/errors/yield_non_ref_function_call_by_ref_error.phpt create mode 100644 Zend/tests/generators/generator_method_by_ref.phpt create mode 100644 Zend/tests/generators/yield_by_reference.phpt create mode 100644 Zend/tests/generators/yield_ref_function_call_by_reference.phpt diff --git a/Zend/tests/generators/errors/non_ref_generator_iterated_by_ref_error.phpt b/Zend/tests/generators/errors/non_ref_generator_iterated_by_ref_error.phpt new file mode 100644 index 000000000000..5d1a9e34841c --- /dev/null +++ b/Zend/tests/generators/errors/non_ref_generator_iterated_by_ref_error.phpt @@ -0,0 +1,13 @@ +--TEST-- +Non-ref generators cannot be iterated by-ref +--FILE-- + +--EXPECTF-- +Fatal error: You can only iterate a generator by-reference if it declared that it yields by-reference in %s on line %d diff --git a/Zend/tests/generators/errors/yield_const_by_ref_error.phpt b/Zend/tests/generators/errors/yield_const_by_ref_error.phpt new file mode 100644 index 000000000000..37ce1450db14 --- /dev/null +++ b/Zend/tests/generators/errors/yield_const_by_ref_error.phpt @@ -0,0 +1,16 @@ +--TEST-- +A notice is thrown when yielding a constant value by reference +--FILE-- +current()); + +?> +--EXPECTF-- +Notice: Only variable references should be yielded by reference in %s on line %d +string(3) "foo" diff --git a/Zend/tests/generators/errors/yield_non_ref_function_call_by_ref_error.phpt b/Zend/tests/generators/errors/yield_non_ref_function_call_by_ref_error.phpt new file mode 100644 index 000000000000..2487149eefa5 --- /dev/null +++ b/Zend/tests/generators/errors/yield_non_ref_function_call_by_ref_error.phpt @@ -0,0 +1,20 @@ +--TEST-- +Yielding the result of a non-ref function call throw a notice +--FILE-- +current()); + +?> +--EXPECTF-- +Notice: Only variable references should be yielded by reference in %s on line %d +string(3) "bar" diff --git a/Zend/tests/generators/generator_method_by_ref.phpt b/Zend/tests/generators/generator_method_by_ref.phpt new file mode 100644 index 000000000000..108c2133f63e --- /dev/null +++ b/Zend/tests/generators/generator_method_by_ref.phpt @@ -0,0 +1,44 @@ +--TEST-- +Generator methods can yield by reference +--FILE-- +data = $data; + } + + public function getData() { + return $this->data; + } + + public function *&getIterator() { + foreach ($this->data as $key => &$value) { + yield $key => $value; + } + } +} + +$test = new Test([1, 2, 3, 4, 5]); +foreach ($test as &$value) { + $value *= -1; +} + +var_dump($test->getData()); + +?> +--EXPECT-- +array(5) { + [0]=> + int(-1) + [1]=> + int(-2) + [2]=> + int(-3) + [3]=> + int(-4) + [4]=> + &int(-5) +} diff --git a/Zend/tests/generators/yield_by_reference.phpt b/Zend/tests/generators/yield_by_reference.phpt new file mode 100644 index 000000000000..86dd419bd64f --- /dev/null +++ b/Zend/tests/generators/yield_by_reference.phpt @@ -0,0 +1,32 @@ +--TEST-- +Generators can yield by-reference +--FILE-- + &$value) { + yield $key => $value; + } +} + +$array = [1, 2, 3, 4, 5]; +$iter = iter($array); +foreach ($iter as &$value) { + $value *= -1; +} +var_dump($array); + +?> +--EXPECT-- +array(5) { + [0]=> + int(-1) + [1]=> + int(-2) + [2]=> + int(-3) + [3]=> + int(-4) + [4]=> + &int(-5) +} diff --git a/Zend/tests/generators/yield_ref_function_call_by_reference.phpt b/Zend/tests/generators/yield_ref_function_call_by_reference.phpt new file mode 100644 index 000000000000..88f72eabd126 --- /dev/null +++ b/Zend/tests/generators/yield_ref_function_call_by_reference.phpt @@ -0,0 +1,24 @@ +--TEST-- +The result of a by-ref function call can be yielded just fine +--FILE-- + +--EXPECT-- +string(3) "bar" diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 35ff2bbf5223..9264fde8e295 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2615,12 +2615,16 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */ zend_op *opline; int start_op_number, end_op_number; + /* For generators the & modifier applies to the yielded values, not the + * return value. */ + zend_bool returns_reference = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) && !(CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR); + if ((CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) && expr != NULL) { zend_error(E_COMPILE_ERROR, "Generators cannot return values using \"return\""); } if (do_end_vparse) { - if ((CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) && !zend_is_function_or_method_call(expr)) { + if (returns_reference && !zend_is_function_or_method_call(expr)) { zend_do_end_variable_parse(expr, BP_VAR_W, 0 TSRMLS_CC); } else { zend_do_end_variable_parse(expr, BP_VAR_R, 0 TSRMLS_CC); @@ -2645,7 +2649,7 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */ opline = get_next_op(CG(active_op_array) TSRMLS_CC); - opline->opcode = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) ? ZEND_RETURN_BY_REF : ZEND_RETURN; + opline->opcode = returns_reference ? ZEND_RETURN_BY_REF : ZEND_RETURN; if (expr) { SET_NODE(opline->op1, expr); @@ -2676,6 +2680,10 @@ void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC if (value) { SET_NODE(opline->op1, value); + + if (zend_is_function_or_method_call(value)) { + opline->extended_value = ZEND_RETURNS_FUNCTION; + } } else { SET_UNUSED(opline->op1); } diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index bccbb48ca417..20ab9b16c68f 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -661,12 +661,12 @@ zend_object_iterator *zend_generator_get_iterator(zend_class_entry *ce, zval *ob zend_generator_iterator *iterator; zend_generator *generator; - if (by_ref) { - zend_error(E_ERROR, "By reference iteration of generators is currently not supported"); - } - generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); + if (by_ref && !(generator->execute_data->op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) { + zend_error(E_ERROR, "You can only iterate a generator by-reference if it declared that it yields by-reference"); + } + iterator = emalloc(sizeof(zend_generator_iterator)); iterator->intern.funcs = &zend_generator_iterator_functions; iterator->intern.data = (void *) generator; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 015263f52f5e..4be644a33279 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5316,6 +5316,7 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSED) { USE_OPLINE + zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -5332,30 +5333,74 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE /* Set the new yielded value */ if (OP1_TYPE != IS_UNUSED) { - zend_free_op free_op1; - zval *value = GET_OP1_ZVAL_PTR(BP_VAR_R); + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (OP1_TYPE == IS_CONST || OP1_TYPE == IS_TMP_VAR) { + zval *value, *copy; - /* Consts, temporary variables and references need copying */ - if (OP1_TYPE == IS_CONST || OP1_TYPE == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + value = GET_OP1_ZVAL_PTR(BP_VAR_R); + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - /* Temporary variables don't need ctor copying */ - if (!IS_OP1_TMP_FREE()) { - zval_copy_ctor(copy); - } + /* Temporary variables don't need ctor copying */ + if (!IS_OP1_TMP_FREE()) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + zval **value_ptr = GET_OP1_ZVAL_PTR_PTR(BP_VAR_W); - generator->value = copy; + if (OP1_TYPE == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (OP1_TYPE == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); + + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } + + FREE_OP1_IF_VAR(); + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = GET_OP1_ZVAL_PTR(BP_VAR_R); - FREE_OP1_IF_VAR(); + /* Consts, temporary variables and references need copying */ + if (OP1_TYPE == IS_CONST || OP1_TYPE == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!IS_OP1_TMP_FREE()) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + FREE_OP1_IF_VAR(); + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 29b6d7c500c4..b1bcd74d2332 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -4118,6 +4118,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLE { USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -4133,29 +4134,72 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLE /* Set the new yielded value */ if (IS_CONST != IS_UNUSED) { + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) { + zval *value, *copy; - zval *value = opline->op1.zv; + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - /* Consts, temporary variables and references need copying */ - if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + value = opline->op1.zv; + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } + generator->value = copy; + } else { + zval **value_ptr = NULL; + + if (IS_CONST == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_CONST == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); + + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } - generator->value = copy; + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = opline->op1.zv; + /* Consts, temporary variables and references need copying */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -4759,6 +4803,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMP_HANDLER(ZEND_OPCODE_HANDLER_ { USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -4774,29 +4819,72 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMP_HANDLER(ZEND_OPCODE_HANDLER_ /* Set the new yielded value */ if (IS_CONST != IS_UNUSED) { + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) { + zval *value, *copy; - zval *value = opline->op1.zv; + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - /* Consts, temporary variables and references need copying */ - if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + value = opline->op1.zv; + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } + generator->value = copy; + } else { + zval **value_ptr = NULL; + + if (IS_CONST == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_CONST == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - generator->value = copy; + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } + + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = opline->op1.zv; + + /* Consts, temporary variables and references need copying */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -5725,6 +5813,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ { USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -5740,29 +5829,72 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ /* Set the new yielded value */ if (IS_CONST != IS_UNUSED) { + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) { + zval *value, *copy; - zval *value = opline->op1.zv; + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - /* Consts, temporary variables and references need copying */ - if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + value = opline->op1.zv; + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } + generator->value = copy; + } else { + zval **value_ptr = NULL; - generator->value = copy; + if (IS_CONST == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_CONST == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); + + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } + + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = opline->op1.zv; + /* Consts, temporary variables and references need copying */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -6386,6 +6518,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDL { USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -6401,29 +6534,72 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDL /* Set the new yielded value */ if (IS_CONST != IS_UNUSED) { + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) { + zval *value, *copy; - zval *value = opline->op1.zv; + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - /* Consts, temporary variables and references need copying */ - if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + value = opline->op1.zv; + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } + generator->value = copy; + } else { + zval **value_ptr = NULL; + + if (IS_CONST == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_CONST == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - generator->value = copy; + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } + + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = opline->op1.zv; + + /* Consts, temporary variables and references need copying */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -7086,6 +7262,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_A { USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -7101,29 +7278,72 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_A /* Set the new yielded value */ if (IS_CONST != IS_UNUSED) { + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR) { + zval *value, *copy; - zval *value = opline->op1.zv; + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - /* Consts, temporary variables and references need copying */ - if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + value = opline->op1.zv; + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } + generator->value = copy; + } else { + zval **value_ptr = NULL; - generator->value = copy; + if (IS_CONST == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_CONST == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); + + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } + + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = opline->op1.zv; + + /* Consts, temporary variables and references need copying */ + if (IS_CONST == IS_CONST || IS_CONST == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -9093,6 +9313,7 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_TMP_CONST_HANDLER(ZEND_OPC static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE + zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -9109,29 +9330,72 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ /* Set the new yielded value */ if (IS_TMP_VAR != IS_UNUSED) { - zend_free_op free_op1; - zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR) { + zval *value, *copy; - /* Consts, temporary variables and references need copying */ - if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - /* Temporary variables don't need ctor copying */ - if (!1) { - zval_copy_ctor(copy); - } + /* Temporary variables don't need ctor copying */ + if (!1) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + zval **value_ptr = NULL; + + if (IS_TMP_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_TMP_VAR == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); + + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } - generator->value = copy; + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!1) { + zval_copy_ctor(copy); + } + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -9734,6 +9998,7 @@ static int ZEND_FASTCALL ZEND_INIT_ARRAY_SPEC_TMP_TMP_HANDLER(ZEND_OPCODE_HANDL static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE + zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -9750,29 +10015,72 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_TMP_HANDLER(ZEND_OPCODE_HANDLER_AR /* Set the new yielded value */ if (IS_TMP_VAR != IS_UNUSED) { - zend_free_op free_op1; - zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR) { + zval *value, *copy; - /* Consts, temporary variables and references need copying */ - if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - /* Temporary variables don't need ctor copying */ - if (!1) { - zval_copy_ctor(copy); - } + /* Temporary variables don't need ctor copying */ + if (!1) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + zval **value_ptr = NULL; + + if (IS_TMP_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_TMP_VAR == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); + + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } - generator->value = copy; + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!1) { + zval_copy_ctor(copy); + } + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -10700,6 +11008,7 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_TMP_VAR_HANDLER(ZEND_OPCOD static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE + zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -10716,29 +11025,72 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_VAR_HANDLER(ZEND_OPCODE_HANDLER_AR /* Set the new yielded value */ if (IS_TMP_VAR != IS_UNUSED) { - zend_free_op free_op1; - zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR) { + zval *value, *copy; - /* Consts, temporary variables and references need copying */ - if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - /* Temporary variables don't need ctor copying */ - if (!1) { - zval_copy_ctor(copy); - } + /* Temporary variables don't need ctor copying */ + if (!1) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + zval **value_ptr = NULL; + + if (IS_TMP_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_TMP_VAR == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); + + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } - generator->value = copy; + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!1) { + zval_copy_ctor(copy); + } + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -11227,6 +11579,7 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_TMP_UNUSED_HANDLER(ZEND_OP static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE + zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -11243,29 +11596,72 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCODE_HANDLER /* Set the new yielded value */ if (IS_TMP_VAR != IS_UNUSED) { - zend_free_op free_op1; - zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR) { + zval *value, *copy; - /* Consts, temporary variables and references need copying */ - if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - /* Temporary variables don't need ctor copying */ - if (!1) { - zval_copy_ctor(copy); - } + /* Temporary variables don't need ctor copying */ + if (!1) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + zval **value_ptr = NULL; + + if (IS_TMP_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_TMP_VAR == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); + + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } - generator->value = copy; + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!1) { + zval_copy_ctor(copy); + } + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -11865,6 +12261,7 @@ static int ZEND_FASTCALL ZEND_INIT_ARRAY_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLE static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE + zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -11881,29 +12278,72 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARG /* Set the new yielded value */ if (IS_TMP_VAR != IS_UNUSED) { - zend_free_op free_op1; - zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR) { + zval *value, *copy; - /* Consts, temporary variables and references need copying */ - if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - /* Temporary variables don't need ctor copying */ - if (!1) { - zval_copy_ctor(copy); - } + /* Temporary variables don't need ctor copying */ + if (!1) { + zval_copy_ctor(copy); + } - generator->value = copy; + generator->value = copy; + } else { + zval **value_ptr = NULL; + + if (IS_TMP_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_TMP_VAR == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); + + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } + + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_TMP_VAR == IS_CONST || IS_TMP_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + /* Temporary variables don't need ctor copying */ + if (!1) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -15729,6 +16169,7 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_VAR_CONST_HANDLER(ZEN static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE + zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -15745,30 +16186,74 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ /* Set the new yielded value */ if (IS_VAR != IS_UNUSED) { - zend_free_op free_op1; - zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR) { + zval *value, *copy; - /* Consts, temporary variables and references need copying */ - if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } - generator->value = copy; + generator->value = copy; + } else { + zval **value_ptr = _get_zval_ptr_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + + if (IS_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_VAR == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); + + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } + + if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); - if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + /* Consts, temporary variables and references need copying */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -17757,6 +18242,7 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_VAR_TMP_HANDLER(ZEND_ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE + zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -17773,30 +18259,74 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_TMP_HANDLER(ZEND_OPCODE_HANDLER_AR /* Set the new yielded value */ if (IS_VAR != IS_UNUSED) { - zend_free_op free_op1; - zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR) { + zval *value, *copy; - /* Consts, temporary variables and references need copying */ - if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + zval **value_ptr = _get_zval_ptr_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); - generator->value = copy; + if (IS_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_VAR == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); + + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } + + if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); - if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + /* Consts, temporary variables and references need copying */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -20165,6 +20695,7 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_VAR_VAR_HANDLER(ZEND_ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE + zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -20181,30 +20712,74 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_AR /* Set the new yielded value */ if (IS_VAR != IS_UNUSED) { - zend_free_op free_op1; - zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR) { + zval *value, *copy; - /* Consts, temporary variables and references need copying */ - if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + zval **value_ptr = _get_zval_ptr_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); - generator->value = copy; + if (IS_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_VAR == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); + + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } + + if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); - if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + /* Consts, temporary variables and references need copying */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -21252,6 +21827,7 @@ static int ZEND_FASTCALL ZEND_SEPARATE_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HAND static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE + zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -21268,30 +21844,74 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER /* Set the new yielded value */ if (IS_VAR != IS_UNUSED) { - zend_free_op free_op1; - zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR) { + zval *value, *copy; - /* Consts, temporary variables and references need copying */ - if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + zval **value_ptr = _get_zval_ptr_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + + if (IS_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_VAR == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); + + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } - generator->value = copy; + if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); - if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + /* Consts, temporary variables and references need copying */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -23329,6 +23949,7 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_VAR_CV_HANDLER(ZEND_O static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE + zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -23345,30 +23966,74 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARG /* Set the new yielded value */ if (IS_VAR != IS_UNUSED) { - zend_free_op free_op1; - zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR) { + zval *value, *copy; - /* Consts, temporary variables and references need copying */ - if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + zval **value_ptr = _get_zval_ptr_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); + + if (IS_VAR == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_VAR == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - generator->value = copy; + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } + + if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); - if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + /* Consts, temporary variables and references need copying */ + if (IS_VAR == IS_CONST || IS_VAR == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + if (free_op1.var) {zval_ptr_dtor(&free_op1.var);}; + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -24766,6 +25431,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDL { USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -24781,29 +25447,72 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDL /* Set the new yielded value */ if (IS_UNUSED != IS_UNUSED) { + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR) { + zval *value, *copy; - zval *value = NULL; + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - /* Consts, temporary variables and references need copying */ - if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + value = NULL; + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } + generator->value = copy; + } else { + zval **value_ptr = NULL; + + if (IS_UNUSED == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } - generator->value = copy; + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_UNUSED == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); + + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } + + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = NULL; + + /* Consts, temporary variables and references need copying */ + if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -26025,6 +26734,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER(ZEND_OPCODE_HANDLER { USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -26040,29 +26750,72 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER(ZEND_OPCODE_HANDLER /* Set the new yielded value */ if (IS_UNUSED != IS_UNUSED) { + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR) { + zval *value, *copy; - zval *value = NULL; + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - /* Consts, temporary variables and references need copying */ - if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + value = NULL; + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } + generator->value = copy; + } else { + zval **value_ptr = NULL; - generator->value = copy; + if (IS_UNUSED == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_UNUSED == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); + + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } + + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = NULL; + + /* Consts, temporary variables and references need copying */ + if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -27284,6 +28037,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER(ZEND_OPCODE_HANDLER { USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -27299,29 +28053,72 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER(ZEND_OPCODE_HANDLER /* Set the new yielded value */ if (IS_UNUSED != IS_UNUSED) { + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR) { + zval *value, *copy; - zval *value = NULL; + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - /* Consts, temporary variables and references need copying */ - if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + value = NULL; + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } + generator->value = copy; + } else { + zval **value_ptr = NULL; - generator->value = copy; + if (IS_UNUSED == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_UNUSED == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); + + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } + + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = NULL; + /* Consts, temporary variables and references need copying */ + if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -27655,6 +28452,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HAND { USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -27670,29 +28468,72 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HAND /* Set the new yielded value */ if (IS_UNUSED != IS_UNUSED) { + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR) { + zval *value, *copy; - zval *value = NULL; + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - /* Consts, temporary variables and references need copying */ - if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + value = NULL; + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } + generator->value = copy; + } else { + zval **value_ptr = NULL; + + if (IS_UNUSED == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_UNUSED == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); + + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } - generator->value = copy; + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = NULL; + /* Consts, temporary variables and references need copying */ + if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -28911,6 +29752,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ { USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -28926,29 +29768,72 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ /* Set the new yielded value */ if (IS_UNUSED != IS_UNUSED) { + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR) { + zval *value, *copy; - zval *value = NULL; + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - /* Consts, temporary variables and references need copying */ - if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + value = NULL; + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } + generator->value = copy; + } else { + zval **value_ptr = NULL; + + if (IS_UNUSED == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } - generator->value = copy; + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_UNUSED == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); + + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } + + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = NULL; + + /* Consts, temporary variables and references need copying */ + if (IS_UNUSED == IS_CONST || IS_UNUSED == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -32376,6 +33261,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_A { USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -32391,29 +33277,72 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_A /* Set the new yielded value */ if (IS_CV != IS_UNUSED) { + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR) { + zval *value, *copy; - zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - /* Consts, temporary variables and references need copying */ - if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } + generator->value = copy; + } else { + zval **value_ptr = _get_zval_ptr_ptr_cv_BP_VAR_W(EX_CVs(), opline->op1.var TSRMLS_CC); + + if (IS_CV == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_CV == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - generator->value = copy; + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } + + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -34274,6 +35203,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARG { USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -34289,29 +35219,72 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARG /* Set the new yielded value */ if (IS_CV != IS_UNUSED) { + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR) { + zval *value, *copy; - zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - /* Consts, temporary variables and references need copying */ - if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } + generator->value = copy; + } else { + zval **value_ptr = _get_zval_ptr_ptr_cv_BP_VAR_W(EX_CVs(), opline->op1.var TSRMLS_CC); + + if (IS_CV == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_CV == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - generator->value = copy; + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } + + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -36551,6 +37524,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG { USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -36566,29 +37540,72 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG /* Set the new yielded value */ if (IS_CV != IS_UNUSED) { + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR) { + zval *value, *copy; - zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - /* Consts, temporary variables and references need copying */ - if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } + generator->value = copy; + } else { + zval **value_ptr = _get_zval_ptr_ptr_cv_BP_VAR_W(EX_CVs(), opline->op1.var TSRMLS_CC); + + if (IS_CV == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_CV == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - generator->value = copy; + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } + + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -37498,6 +38515,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ { USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -37513,29 +38531,72 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ /* Set the new yielded value */ if (IS_CV != IS_UNUSED) { + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR) { + zval *value, *copy; - zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - /* Consts, temporary variables and references need copying */ - if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } + generator->value = copy; + } else { + zval **value_ptr = _get_zval_ptr_ptr_cv_BP_VAR_W(EX_CVs(), opline->op1.var TSRMLS_CC); + + if (IS_CV == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_CV == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - generator->value = copy; + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } + + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); @@ -39444,6 +40505,7 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS { USE_OPLINE + /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -39459,29 +40521,72 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS /* Set the new yielded value */ if (IS_CV != IS_UNUSED) { + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { + /* Constants and temporary variables aren't yieldable by reference, + * but we still allow them with a notice. */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR) { + zval *value, *copy; - zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); - /* Consts, temporary variables and references need copying */ - if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR - || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) - ) { - zval *copy; + value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); - ALLOC_ZVAL(copy); - INIT_PZVAL_COPY(copy, value); + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } - /* Temporary variables don't need ctor copying */ - if (!0) { - zval_copy_ctor(copy); - } + generator->value = copy; + } else { + zval **value_ptr = _get_zval_ptr_ptr_cv_BP_VAR_W(EX_CVs(), opline->op1.var TSRMLS_CC); + + if (IS_CV == IS_VAR && UNEXPECTED(value_ptr == NULL)) { + zend_error_noreturn(E_ERROR, "Cannot yield string offsets by reference"); + } + + /* If a function call result is yielded and the function did + * not return by reference we throw a notice. */ + if (IS_CV == IS_VAR && !Z_ISREF_PP(value_ptr) + && !(opline->extended_value == ZEND_RETURNS_FUNCTION + && EX_T(opline->op1.var).var.fcall_returned_reference) + && EX_T(opline->op1.var).var.ptr_ptr == &EX_T(opline->op1.var).var.ptr) { + zend_error(E_NOTICE, "Only variable references should be yielded by reference"); + + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } else { + SEPARATE_ZVAL_TO_MAKE_IS_REF(value_ptr); + Z_ADDREF_PP(value_ptr); + generator->value = *value_ptr; + } - generator->value = copy; + } } else { - Z_ADDREF_P(value); - generator->value = value; - } + zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); + + /* Consts, temporary variables and references need copying */ + if (IS_CV == IS_CONST || IS_CV == IS_TMP_VAR + || (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) + ) { + zval *copy; + ALLOC_ZVAL(copy); + INIT_PZVAL_COPY(copy, value); + + /* Temporary variables don't need ctor copying */ + if (!0) { + zval_copy_ctor(copy); + } + + generator->value = copy; + } else { + Z_ADDREF_P(value); + generator->value = value; + } + + } } else { /* If no value was specified yield null */ Z_ADDREF(EG(uninitialized_zval)); From c9709bfbd7a71a42e0472abaf9c7d30598e264bf Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 20 Jul 2012 00:49:50 +0200 Subject: [PATCH 49/70] Remove asterix modifier (*) for generators Generators are now automatically detected by the presence of a `yield` expression in their body. This removes the ZEND_SUSPEND_AND_RETURN_GENERATOR opcode. Instead additional checks for ZEND_ACC_GENERATOR are added to the fcall_common helper and zend_call_function. This also adds a new function zend_generator_create_zval, which handles the actual creation of the generator zval from an op array. I feel like I should deglobalize the zend_create_execute_data_from_op_array code a bit. It currently changes EG(current_execute_data) and EG(opline_ptr) which is somewhat confusing (given the name). --- .../generators/auto_incrementing_keys.phpt | 2 +- Zend/tests/generators/backtrace.phpt | 3 +- Zend/tests/generators/clone.phpt | 2 +- Zend/tests/generators/clone_with_foreach.phpt | 2 +- Zend/tests/generators/clone_with_stack.phpt | 2 +- .../generators/clone_with_symbol_table.phpt | 2 +- Zend/tests/generators/clone_with_this.phpt | 4 +- .../generators/close_inside_generator.phpt | 2 +- Zend/tests/generators/dynamic_call.phpt | 2 +- .../errors/generator_cannot_return_error.phpt | 3 +- ...n_ref_generator_iterated_by_ref_error.phpt | 2 +- .../errors/yield_const_by_ref_error.phpt | 2 +- .../yield_in_normal_function_error.phpt | 12 - ...ld_non_ref_function_call_by_ref_error.phpt | 2 +- .../errors/yield_outside_function_error.phpt | 2 +- Zend/tests/generators/func_get_args.phpt | 3 +- Zend/tests/generators/generator_close.phpt | 2 +- Zend/tests/generators/generator_method.phpt | 2 +- .../generators/generator_method_by_ref.phpt | 2 +- .../generator_returns_generator.phpt | 6 +- Zend/tests/generators/generator_send.phpt | 2 +- ...nerator_throwing_during_function_call.phpt | 2 +- .../generator_throwing_exception.phpt | 2 +- .../tests/generators/generator_with_keys.phpt | 2 +- .../generators/no_foreach_var_leaks.phpt | 2 +- Zend/tests/generators/send_after_close.phpt | 8 +- .../generators/send_returns_current.phpt | 2 +- .../tests/generators/unused_return_value.phpt | 2 +- Zend/tests/generators/xrange.phpt | 2 +- Zend/tests/generators/yield_by_reference.phpt | 2 +- .../yield_during_function_call.phpt | 2 +- .../generators/yield_during_method_call.phpt | 2 +- .../yield_ref_function_call_by_reference.phpt | 2 +- .../tests/generators/yield_without_value.phpt | 2 +- Zend/zend_compile.c | 38 +-- Zend/zend_compile.h | 5 +- Zend/zend_execute.h | 1 + Zend/zend_execute_API.c | 9 +- Zend/zend_generators.c | 53 +++- Zend/zend_generators.h | 1 + Zend/zend_language_parser.y | 27 +-- Zend/zend_vm_def.h | 109 +-------- Zend/zend_vm_execute.h | 228 ++++++++---------- Zend/zend_vm_execute.skl | 2 +- Zend/zend_vm_opcodes.h | 5 +- 45 files changed, 247 insertions(+), 324 deletions(-) delete mode 100644 Zend/tests/generators/errors/yield_in_normal_function_error.phpt diff --git a/Zend/tests/generators/auto_incrementing_keys.phpt b/Zend/tests/generators/auto_incrementing_keys.phpt index 623f2d813d45..acfb2f2ce00b 100644 --- a/Zend/tests/generators/auto_incrementing_keys.phpt +++ b/Zend/tests/generators/auto_incrementing_keys.phpt @@ -3,7 +3,7 @@ Generator keys are auto-incrementing by default --FILE-- 'rab'; diff --git a/Zend/tests/generators/backtrace.phpt b/Zend/tests/generators/backtrace.phpt index 77976f932446..5f665b7e4ac1 100644 --- a/Zend/tests/generators/backtrace.phpt +++ b/Zend/tests/generators/backtrace.phpt @@ -7,8 +7,9 @@ function f1() { debug_print_backtrace(); } -function *f2($arg1, $arg2) { +function f2($arg1, $arg2) { f1(); + yield; // force generator } function f3($gen) { diff --git a/Zend/tests/generators/clone.phpt b/Zend/tests/generators/clone.phpt index 94c4c6364e7d..36811dfe6e74 100644 --- a/Zend/tests/generators/clone.phpt +++ b/Zend/tests/generators/clone.phpt @@ -3,7 +3,7 @@ Generators can be cloned --FILE-- foo = 'bar'; yield; // interrupt var_dump($this->foo); diff --git a/Zend/tests/generators/close_inside_generator.phpt b/Zend/tests/generators/close_inside_generator.phpt index 41a91c9fc7b9..1df64bf6b1ab 100644 --- a/Zend/tests/generators/close_inside_generator.phpt +++ b/Zend/tests/generators/close_inside_generator.phpt @@ -3,7 +3,7 @@ Calling close() during the exectution of the generator --FILE-- ---EXPECTF-- -Fatal error: The "yield" expression can only be used inside a generator function in %s on line %d diff --git a/Zend/tests/generators/errors/yield_non_ref_function_call_by_ref_error.phpt b/Zend/tests/generators/errors/yield_non_ref_function_call_by_ref_error.phpt index 2487149eefa5..4b8563331c67 100644 --- a/Zend/tests/generators/errors/yield_non_ref_function_call_by_ref_error.phpt +++ b/Zend/tests/generators/errors/yield_non_ref_function_call_by_ref_error.phpt @@ -7,7 +7,7 @@ function foo() { return "bar"; } -function *&gen() { +function &gen() { yield foo(); } diff --git a/Zend/tests/generators/errors/yield_outside_function_error.phpt b/Zend/tests/generators/errors/yield_outside_function_error.phpt index 5f47e7536656..f999c1c03bf4 100644 --- a/Zend/tests/generators/errors/yield_outside_function_error.phpt +++ b/Zend/tests/generators/errors/yield_outside_function_error.phpt @@ -7,4 +7,4 @@ yield "Test"; ?> --EXPECTF-- -Fatal error: The "yield" expression can only be used inside a generator function in %s on line %d +Fatal error: The "yield" expression can only be used inside a function in %s on line %d diff --git a/Zend/tests/generators/func_get_args.phpt b/Zend/tests/generators/func_get_args.phpt index 7ce7fb002f0a..f8d3fa7c146e 100644 --- a/Zend/tests/generators/func_get_args.phpt +++ b/Zend/tests/generators/func_get_args.phpt @@ -3,8 +3,9 @@ func_get_args() can be used inside generator functions --FILE-- close() --FILE-- data = $data; } - public function *getIterator() { + public function getIterator() { foreach ($this->data as $value) { yield $value; } diff --git a/Zend/tests/generators/generator_method_by_ref.phpt b/Zend/tests/generators/generator_method_by_ref.phpt index 108c2133f63e..cfe52fe67ff8 100644 --- a/Zend/tests/generators/generator_method_by_ref.phpt +++ b/Zend/tests/generators/generator_method_by_ref.phpt @@ -14,7 +14,7 @@ class Test implements IteratorAggregate { return $this->data; } - public function *&getIterator() { + public function &getIterator() { foreach ($this->data as $key => &$value) { yield $key => $value; } diff --git a/Zend/tests/generators/generator_returns_generator.phpt b/Zend/tests/generators/generator_returns_generator.phpt index a3e2b29468ff..ad332a3be924 100644 --- a/Zend/tests/generators/generator_returns_generator.phpt +++ b/Zend/tests/generators/generator_returns_generator.phpt @@ -3,12 +3,14 @@ A generator function returns a Generator object --FILE-- diff --git a/Zend/tests/generators/generator_send.phpt b/Zend/tests/generators/generator_send.phpt index 11ac37f846e0..074d81538971 100644 --- a/Zend/tests/generators/generator_send.phpt +++ b/Zend/tests/generators/generator_send.phpt @@ -3,7 +3,7 @@ Values can be sent back to the generator --FILE-- current($array); diff --git a/Zend/tests/generators/no_foreach_var_leaks.phpt b/Zend/tests/generators/no_foreach_var_leaks.phpt index 36ab91bb15a6..62743895ebcb 100644 --- a/Zend/tests/generators/no_foreach_var_leaks.phpt +++ b/Zend/tests/generators/no_foreach_var_leaks.phpt @@ -3,7 +3,7 @@ foreach() (and other) variables aren't leaked on premature close --FILE-- send("Test"); +$gen->send('foo'); +$gen->send('bar'); ?> -===DONE=== --EXPECT-- -===DONE=== +string(3) "foo" diff --git a/Zend/tests/generators/send_returns_current.phpt b/Zend/tests/generators/send_returns_current.phpt index d3a4afd53a37..fc260c0af0c4 100644 --- a/Zend/tests/generators/send_returns_current.phpt +++ b/Zend/tests/generators/send_returns_current.phpt @@ -3,7 +3,7 @@ $generator->send() returns the yielded value --FILE-- &$value) { yield $key => $value; } diff --git a/Zend/tests/generators/yield_during_function_call.phpt b/Zend/tests/generators/yield_during_function_call.phpt index 9727b8fd33e0..21071f9fb4af 100644 --- a/Zend/tests/generators/yield_during_function_call.phpt +++ b/Zend/tests/generators/yield_during_function_call.phpt @@ -3,7 +3,7 @@ --FILE-- b(yield); } diff --git a/Zend/tests/generators/yield_ref_function_call_by_reference.phpt b/Zend/tests/generators/yield_ref_function_call_by_reference.phpt index 88f72eabd126..e371affd92c8 100644 --- a/Zend/tests/generators/yield_ref_function_call_by_reference.phpt +++ b/Zend/tests/generators/yield_ref_function_call_by_reference.phpt @@ -7,7 +7,7 @@ function &nop(&$var) { return $var; } -function *&gen(&$var) { +function &gen(&$var) { yield nop($var); } diff --git a/Zend/tests/generators/yield_without_value.phpt b/Zend/tests/generators/yield_without_value.phpt index dc467a83bd13..510c755bd3fe 100644 --- a/Zend/tests/generators/yield_without_value.phpt +++ b/Zend/tests/generators/yield_without_value.phpt @@ -3,7 +3,7 @@ yield can be used without a value --FILE-- u.constant.value.str.val; @@ -1553,9 +1553,6 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n CG(interactive) = orig_interactive; op_array.function_name = name; - if (is_generator) { - op_array.fn_flags |= ZEND_ACC_GENERATOR; - } if (return_reference) { op_array.fn_flags |= ZEND_ACC_RETURN_REFERENCE; } @@ -1754,7 +1751,7 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n } /* }}} */ -void zend_do_begin_lambda_function_declaration(znode *result, znode *function_token, int is_generator, int return_reference, int is_static TSRMLS_DC) /* {{{ */ +void zend_do_begin_lambda_function_declaration(znode *result, znode *function_token, int return_reference, int is_static TSRMLS_DC) /* {{{ */ { znode function_name; zend_op_array *current_op_array = CG(active_op_array); @@ -1764,7 +1761,7 @@ void zend_do_begin_lambda_function_declaration(znode *result, znode *function_to function_name.op_type = IS_CONST; ZVAL_STRINGL(&function_name.u.constant, "{closure}", sizeof("{closure}")-1, 1); - zend_do_begin_function_declaration(function_token, &function_name, 0, is_generator, return_reference, NULL TSRMLS_CC); + zend_do_begin_function_declaration(function_token, &function_name, 0, return_reference, NULL TSRMLS_CC); result->op_type = IS_TMP_VAR; result->u.op.var = get_temporary_variable(current_op_array); @@ -2670,10 +2667,12 @@ void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC { zend_op *opline; - if ((CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) == 0) { - zend_error(E_COMPILE_ERROR, "The \"yield\" expression can only be used inside a generator function"); + if (!CG(active_op_array)->function_name) { + zend_error(E_COMPILE_ERROR, "The \"yield\" expression can only be used inside a function"); } + CG(active_op_array)->fn_flags |= ZEND_ACC_GENERATOR; + opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = ZEND_YIELD; @@ -2704,10 +2703,12 @@ void zend_do_delegate_yield(znode *result, const znode *value TSRMLS_DC) /* {{{ { zend_op *opline; - if ((CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) == 0) { - zend_error(E_COMPILE_ERROR, "The \"yield*\" expression can only be used inside a generator function"); + if (!CG(active_op_array)->function_name) { + zend_error(E_COMPILE_ERROR, "The \"yield*\" expression can only be used inside a function"); } + CG(active_op_array)->fn_flags |= ZEND_ACC_GENERATOR; + opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = ZEND_DELEGATE_YIELD; @@ -2721,23 +2722,6 @@ void zend_do_delegate_yield(znode *result, const znode *value TSRMLS_DC) /* {{{ } /* }}} */ -void zend_do_suspend_if_generator(TSRMLS_D) /* {{{ */ -{ - zend_op *opline; - - // we only suspend execution if the current function is a generator - if ((CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) == 0) { - return; - } - - opline = get_next_op(CG(active_op_array) TSRMLS_CC); - - opline->opcode = ZEND_SUSPEND_AND_RETURN_GENERATOR; - SET_UNUSED(opline->op1); - SET_UNUSED(opline->op2); -} -/* }}} */ - static int zend_add_try_element(zend_uint try_op TSRMLS_DC) /* {{{ */ { int try_catch_offset = CG(active_op_array)->last_try_catch++; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index d0587d1faa4d..c4355e0dde0e 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -478,7 +478,7 @@ void zend_do_add_string(znode *result, const znode *op1, znode *op2 TSRMLS_DC); void zend_do_add_variable(znode *result, const znode *op1, const znode *op2 TSRMLS_DC); int zend_do_verify_access_types(const znode *current_access_type, const znode *new_modifier); -void zend_do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int is_generator, int return_reference, znode *fn_flags_znode TSRMLS_DC); +void zend_do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int return_reference, znode *fn_flags_znode TSRMLS_DC); void zend_do_end_function_declaration(const znode *function_token TSRMLS_DC); void zend_do_receive_arg(zend_uchar op, znode *varname, const znode *offset, const znode *initialization, znode *class_type, zend_bool pass_by_reference TSRMLS_DC); int zend_do_begin_function_call(znode *function_name, zend_bool check_namespace TSRMLS_DC); @@ -492,10 +492,9 @@ void zend_do_end_function_call(znode *function_name, znode *result, const znode void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC); void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC); void zend_do_delegate_yield(znode *result, const znode *value TSRMLS_DC); -void zend_do_suspend_if_generator(TSRMLS_D); void zend_do_handle_exception(TSRMLS_D); -void zend_do_begin_lambda_function_declaration(znode *result, znode *function_token, int is_generator, int return_reference, int is_static TSRMLS_DC); +void zend_do_begin_lambda_function_declaration(znode *result, znode *function_token, int return_reference, int is_static TSRMLS_DC); void zend_do_fetch_lexical_variable(znode *varname, zend_bool is_ref TSRMLS_DC); void zend_do_try(znode *try_token TSRMLS_DC); diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 92160b54eb0a..4cfc52b6a3b6 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -55,6 +55,7 @@ ZEND_API extern void (*zend_execute_internal)(zend_execute_data *execute_data_pt void init_executor(TSRMLS_D); void shutdown_executor(TSRMLS_D); void shutdown_destructors(TSRMLS_D); +zend_execute_data *zend_create_execute_data_from_op_array(zend_op_array *op_array, zend_bool nested TSRMLS_DC); ZEND_API void execute(zend_op_array *op_array TSRMLS_DC); ZEND_API void execute_ex(zend_execute_data *execute_data TSRMLS_DC); ZEND_API void execute_internal(zend_execute_data *execute_data_ptr, int return_value_used TSRMLS_DC); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 1deee2a86c89..fb0c18b27cc3 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -31,6 +31,7 @@ #include "zend_extensions.h" #include "zend_exceptions.h" #include "zend_closures.h" +#include "zend_generators.h" #include "zend_vm.h" #include "zend_float.h" #ifdef HAVE_SYS_TIME_H @@ -955,7 +956,13 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache TS EG(return_value_ptr_ptr) = fci->retval_ptr_ptr; EG(active_op_array) = (zend_op_array *) EX(function_state).function; original_opline_ptr = EG(opline_ptr); - zend_execute(EG(active_op_array) TSRMLS_CC); + + if (EG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) { + *fci->retval_ptr_ptr = zend_generator_create_zval(EG(active_op_array) TSRMLS_CC); + } else { + zend_execute(EG(active_op_array) TSRMLS_CC); + } + if (!fci->symbol_table && EG(active_symbol_table)) { if (EG(symtable_cache_ptr)>=EG(symtable_cache_limit)) { zend_hash_destroy(EG(active_symbol_table)); diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 20ab9b16c68f..b164fb835e53 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -315,6 +315,56 @@ static zend_object_value zend_generator_create(zend_class_entry *class_type TSRM } /* }}} */ +/* Requires globals EG(scope), EG(current_scope), EG(This), + * EG(active_symbol_table) and EG(current_execute_data). */ +zval *zend_generator_create_zval(zend_op_array *op_array TSRMLS_DC) /* {{{ */ +{ + zval *return_value; + zend_generator *generator; + + /* Create new execution context. We have to back up and restore + * EG(current_execute_data) and EG(opline_ptr) here because the function + * modifies it. */ + zend_execute_data *current_execute_data = EG(current_execute_data); + zend_op **opline_ptr = EG(opline_ptr); + zend_execute_data *execute_data = zend_create_execute_data_from_op_array(op_array, 0 TSRMLS_CC); + EG(current_execute_data) = current_execute_data; + EG(opline_ptr) = opline_ptr; + + ALLOC_INIT_ZVAL(return_value); + object_init_ex(return_value, zend_ce_generator); + + if (EG(This)) { + Z_ADDREF_P(EG(This)); + } + + /* Back up executor globals. */ + execute_data->current_scope = EG(scope); + execute_data->current_called_scope = EG(called_scope); + execute_data->symbol_table = EG(active_symbol_table); + execute_data->current_this = EG(This); + + /* Save execution context in generator object. */ + generator = (zend_generator *) zend_object_store_get_object(return_value TSRMLS_CC); + generator->execute_data = execute_data; + + /* We have to add another stack frame so the generator function shows + * up in backtraces and func_get_all() can access the function + * arguments. */ + execute_data->prev_execute_data = emalloc(sizeof(zend_execute_data)); + if (EG(current_execute_data)) { + memcpy(execute_data->prev_execute_data, EG(current_execute_data), sizeof(zend_execute_data)); + execute_data->prev_execute_data->function_state.arguments = zend_copy_arguments(EG(current_execute_data)->function_state.arguments); + } else { + memset(execute_data->prev_execute_data, 0, sizeof(zend_execute_data)); + execute_data->prev_execute_data->function_state.function = (zend_function *) op_array; + execute_data->prev_execute_data->function_state.arguments = NULL; + } + + return return_value; +} +/* }}} */ + static zend_function *zend_generator_get_constructor(zval *object TSRMLS_DC) /* {{{ */ { zend_error(E_RECOVERABLE_ERROR, "The \"Generator\" class is reserved for internal use and cannot be manually instantiated"); @@ -377,9 +427,6 @@ static void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */ * set the prev_execute_data of that prev_execute_data :) */ generator->execute_data->prev_execute_data->prev_execute_data = original_execute_data; - /* Go to next opcode (we don't want to run the last one again) */ - generator->execute_data->opline++; - /* Resume execution */ execute_ex(generator->execute_data TSRMLS_CC); diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index 73d85287a705..d67ea4137bf8 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -55,6 +55,7 @@ typedef struct _zend_generator { extern ZEND_API zend_class_entry *zend_ce_generator; void zend_register_generator_ce(TSRMLS_D); +zval *zend_generator_create_zval(zend_op_array *op_array TSRMLS_CC); void zend_generator_close(zend_generator *generator, zend_bool finished_execution TSRMLS_DC); END_EXTERN_C() diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 02aa694c6eb6..b705d60d9a53 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -360,11 +360,6 @@ class_declaration_statement: unticked_class_declaration_statement { DO_TICKS(); } ; -is_generator: - /* empty */ { $$.op_type = 0; } - | '*' { $$.op_type = 1; } -; - is_reference: /* empty */ { $$.op_type = ZEND_RETURN_VAL; } | '&' { $$.op_type = ZEND_RETURN_REF; } @@ -372,8 +367,8 @@ is_reference: unticked_function_declaration_statement: - function is_generator is_reference T_STRING { zend_do_begin_function_declaration(&$1, &$4, 0, $2.op_type, $3.op_type, NULL TSRMLS_CC); } - '(' parameter_list ')' { zend_do_suspend_if_generator(TSRMLS_C); } + function is_reference T_STRING { zend_do_begin_function_declaration(&$1, &$3, 0, $2.op_type, NULL TSRMLS_CC); } + '(' parameter_list ')' '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); } ; @@ -584,9 +579,9 @@ 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_generator is_reference T_STRING { zend_do_begin_function_declaration(&$2, &$5, 1, $3.op_type, $4.op_type, &$1 TSRMLS_CC); } - '(' parameter_list ')' { zend_do_suspend_if_generator(TSRMLS_C); } - method_body { zend_do_abstract_method(&$5, &$1, &$11 TSRMLS_CC); zend_do_end_function_declaration(&$2 TSRMLS_CC); } + | 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: @@ -805,12 +800,12 @@ expr_without_variable: | T_YIELD { zend_do_yield(&$$, NULL, NULL TSRMLS_CC); } | T_YIELD expr { zend_do_yield(&$$, &$2, NULL TSRMLS_CC); } | T_YIELD '*' expr { zend_do_delegate_yield(&$$, &$3 TSRMLS_CC); } - | function is_generator is_reference { zend_do_begin_lambda_function_declaration(&$$, &$1, $2.op_type, $3.op_type, 0 TSRMLS_CC); } - '(' parameter_list ')' lexical_vars { zend_do_suspend_if_generator(TSRMLS_C); } - '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); $$ = $4; } - | T_STATIC function is_generator is_reference { zend_do_begin_lambda_function_declaration(&$$, &$2, $3.op_type, $4.op_type, 1 TSRMLS_CC); } - '(' parameter_list ')' lexical_vars { zend_do_suspend_if_generator(TSRMLS_C); } - '{' inner_statement_list '}' { zend_do_end_function_declaration(&$2 TSRMLS_CC); $$ = $5; } + | function is_reference { zend_do_begin_lambda_function_declaration(&$$, &$1, $2.op_type, 0 TSRMLS_CC); } + '(' parameter_list ')' lexical_vars + '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); $$ = $3; } + | T_STATIC function is_reference { zend_do_begin_lambda_function_declaration(&$$, &$2, $3.op_type, 1 TSRMLS_CC); } + '(' parameter_list ')' lexical_vars + '{' inner_statement_list '}' { zend_do_end_function_declaration(&$2 TSRMLS_CC); $$ = $4; } ; combined_scalar_offset: diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 4be644a33279..e8b89a6db5b3 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2705,7 +2705,11 @@ ZEND_VM_HELPER(zend_do_fcall_common_helper, ANY, ANY) ret->var.fcall_returned_reference = (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0; } - if (EXPECTED(zend_execute == execute)) { + if (EG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) { + if (RETURN_VALUE_USED(opline)) { + EX_T(opline->result.var).var.ptr = zend_generator_create_zval(EG(active_op_array) TSRMLS_CC); + } + } else if (EXPECTED(zend_execute == execute)) { if (EXPECTED(EG(exception) == NULL)) { ZEND_VM_ENTER(); } @@ -5218,102 +5222,7 @@ ZEND_VM_HANDLER(156, ZEND_SEPARATE, VAR, UNUSED) ZEND_VM_NEXT_OPCODE(); } -ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY) -{ - zend_bool nested = EX(nested); - zend_execute_data *prev_execute_data = EX(prev_execute_data); - - if (EG(return_value_ptr_ptr)) { - zval *return_value; - zend_generator *generator; - - ALLOC_INIT_ZVAL(return_value); - object_init_ex(return_value, zend_ce_generator); - - *EG(return_value_ptr_ptr) = return_value; - - /* back up some executor globals */ - SAVE_OPLINE(); - - EX(current_scope) = EG(scope); - EX(current_called_scope) = EG(called_scope); - - if (EG(This)) { - Z_ADDREF_P(EG(This)); - } - EX(current_this) = EG(This); - - /* back up the execution context */ - generator = (zend_generator *) zend_object_store_get_object(return_value TSRMLS_CC); - generator->execute_data = execute_data; - - /* We have to add another stack frame so the generator function shows - * up in backtraces and func_get_all() can access the function - * arguments. */ - EX(prev_execute_data) = emalloc(sizeof(zend_execute_data)); - if (prev_execute_data) { - memcpy(EX(prev_execute_data), prev_execute_data, sizeof(zend_execute_data)); - EX(prev_execute_data)->function_state.arguments = zend_copy_arguments(prev_execute_data->function_state.arguments); - } else { - memset(EX(prev_execute_data), 0, sizeof(zend_execute_data)); - EX(prev_execute_data)->function_state.function = (zend_function *) EX(op_array); - EX(prev_execute_data)->function_state.arguments = NULL; - } - } - - /* restore the previous execution context */ - EG(current_execute_data) = prev_execute_data; - - /* if there is no return value pointer we are responsible for freeing the - * execution data */ - if (!EG(return_value_ptr_ptr)) { - if (!EG(active_symbol_table)) { - zend_free_compiled_variables(EX_CVs(), execute_data->op_array->last_var); - } else { - zend_clean_and_cache_symbol_table(EG(active_symbol_table) TSRMLS_CC); - } - efree(execute_data); - } - - - /* Happens whenever the function is invoked using call_user_function, - * e.g. when doing a dynamic function call using call_user_func(). */ - if (!nested) { - EG(opline_ptr) = NULL; - ZEND_VM_RETURN(); - } - - /* Free $this and stack arguments */ - if (EG(This)) { - zval_ptr_dtor(&EG(This)); - } - - zend_vm_stack_clear_multiple(TSRMLS_C); - - /* Bring back the previous execution context */ - execute_data = EG(current_execute_data); - - EG(opline_ptr) = &EX(opline); - EG(active_op_array) = EX(op_array); - EG(return_value_ptr_ptr) = EX(original_return_value); - EG(active_symbol_table) = EX(symbol_table); - EG(This) = EX(current_this); - EG(scope) = EX(current_scope); - EG(called_scope) = EX(current_called_scope); - - EX(function_state).function = (zend_function *) EX(op_array); - EX(function_state).arguments = NULL; - - EX(object) = EX(current_object); - EX(called_scope) = DECODE_CTOR(EX(called_scope)); - - LOAD_REGS(); - LOAD_OPLINE(); - ZEND_VM_INC_OPCODE(); - ZEND_VM_LEAVE(); -} - -ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSED) +ZEND_VM_HANDLER(159, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSED) { USE_OPLINE zend_free_op free_op1; @@ -5454,6 +5363,10 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -5461,7 +5374,7 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE ZEND_VM_RETURN(); } -ZEND_VM_HANDLER(161, ZEND_DELEGATE_YIELD, CONST|TMP|VAR|CV, ANY) +ZEND_VM_HANDLER(160, ZEND_DELEGATE_YIELD, CONST|TMP|VAR|CV, ANY) { ZEND_VM_NEXT_OPCODE(); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index b1bcd74d2332..0fcae113baa4 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -339,7 +339,7 @@ static opcode_handler_t zend_vm_get_opcode_handler(zend_uchar opcode, zend_op* o #define EX_Ts() EX(Ts) -static zend_execute_data *zend_create_execute_data_from_op_array(zend_op_array *op_array, zend_bool nested TSRMLS_DC) { +zend_execute_data *zend_create_execute_data_from_op_array(zend_op_array *op_array, zend_bool nested TSRMLS_DC) { zend_execute_data *execute_data; /* @@ -689,7 +689,11 @@ static int ZEND_FASTCALL zend_do_fcall_common_helper_SPEC(ZEND_OPCODE_HANDLER_AR ret->var.fcall_returned_reference = (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0; } - if (EXPECTED(zend_execute == execute)) { + if (EG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) { + if (RETURN_VALUE_USED(opline)) { + EX_T(opline->result.var).var.ptr = zend_generator_create_zval(EG(active_op_array) TSRMLS_CC); + } + } else if (EXPECTED(zend_execute == execute)) { if (EXPECTED(EG(exception) == NULL)) { ZEND_VM_ENTER(); } @@ -1201,101 +1205,6 @@ static int ZEND_FASTCALL ZEND_USER_OPCODE_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS } } -static int ZEND_FASTCALL ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - zend_bool nested = EX(nested); - zend_execute_data *prev_execute_data = EX(prev_execute_data); - - if (EG(return_value_ptr_ptr)) { - zval *return_value; - zend_generator *generator; - - ALLOC_INIT_ZVAL(return_value); - object_init_ex(return_value, zend_ce_generator); - - *EG(return_value_ptr_ptr) = return_value; - - /* back up some executor globals */ - SAVE_OPLINE(); - - EX(current_scope) = EG(scope); - EX(current_called_scope) = EG(called_scope); - - if (EG(This)) { - Z_ADDREF_P(EG(This)); - } - EX(current_this) = EG(This); - - /* back up the execution context */ - generator = (zend_generator *) zend_object_store_get_object(return_value TSRMLS_CC); - generator->execute_data = execute_data; - - /* We have to add another stack frame so the generator function shows - * up in backtraces and func_get_all() can access the function - * arguments. */ - EX(prev_execute_data) = emalloc(sizeof(zend_execute_data)); - if (prev_execute_data) { - memcpy(EX(prev_execute_data), prev_execute_data, sizeof(zend_execute_data)); - EX(prev_execute_data)->function_state.arguments = zend_copy_arguments(prev_execute_data->function_state.arguments); - } else { - memset(EX(prev_execute_data), 0, sizeof(zend_execute_data)); - EX(prev_execute_data)->function_state.function = (zend_function *) EX(op_array); - EX(prev_execute_data)->function_state.arguments = NULL; - } - } - - /* restore the previous execution context */ - EG(current_execute_data) = prev_execute_data; - - /* if there is no return value pointer we are responsible for freeing the - * execution data */ - if (!EG(return_value_ptr_ptr)) { - if (!EG(active_symbol_table)) { - zend_free_compiled_variables(EX_CVs(), execute_data->op_array->last_var); - } else { - zend_clean_and_cache_symbol_table(EG(active_symbol_table) TSRMLS_CC); - } - efree(execute_data); - } - - - /* Happens whenever the function is invoked using call_user_function, - * e.g. when doing a dynamic function call using call_user_func(). */ - if (!nested) { - EG(opline_ptr) = NULL; - ZEND_VM_RETURN(); - } - - /* Free $this and stack arguments */ - if (EG(This)) { - zval_ptr_dtor(&EG(This)); - } - - zend_vm_stack_clear_multiple(TSRMLS_C); - - /* Bring back the previous execution context */ - execute_data = EG(current_execute_data); - - EG(opline_ptr) = &EX(opline); - EG(active_op_array) = EX(op_array); - EG(return_value_ptr_ptr) = EX(original_return_value); - EG(active_symbol_table) = EX(symbol_table); - EG(This) = EX(current_this); - EG(scope) = EX(current_scope); - EG(called_scope) = EX(current_called_scope); - - EX(function_state).function = (zend_function *) EX(op_array); - EX(function_state).arguments = NULL; - - EX(object) = EX(current_object); - EX(called_scope) = DECODE_CTOR(EX(called_scope)); - - LOAD_REGS(); - LOAD_OPLINE(); - ZEND_VM_INC_OPCODE(); - ZEND_VM_LEAVE(); -} - static int ZEND_FASTCALL ZEND_FETCH_CLASS_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -4252,6 +4161,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLE Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -4937,6 +4850,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMP_HANDLER(ZEND_OPCODE_HANDLER_ Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -5948,6 +5865,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -6652,6 +6573,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDL Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -7396,6 +7321,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_A Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -9448,6 +9377,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -10133,6 +10066,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_TMP_HANDLER(ZEND_OPCODE_HANDLER_AR Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -11144,6 +11081,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_VAR_HANDLER(ZEND_OPCODE_HANDLER_AR Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -11714,6 +11655,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCODE_HANDLER Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -12396,6 +12341,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARG Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -16306,6 +16255,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -18379,6 +18332,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_TMP_HANDLER(ZEND_OPCODE_HANDLER_AR Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -20833,6 +20790,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_AR Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -21964,6 +21925,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -24086,6 +24051,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARG Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -25565,6 +25534,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDL Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -26868,6 +26841,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER(ZEND_OPCODE_HANDLER Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -28172,6 +28149,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER(ZEND_OPCODE_HANDLER Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -28586,6 +28567,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HAND Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -29886,6 +29871,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -33395,6 +33384,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_A Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -35337,6 +35330,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARG Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -37659,6 +37656,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -38649,6 +38650,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -40639,6 +40644,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS Z_ADDREF(EG(uninitialized_zval)); AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval)); + /* We increment to the next op, so we are at the correct position when the + * generator is resumed. */ + ZEND_VM_INC_OPCODE(); + /* The GOTO VM uses a local opline variable. We need to set the opline * variable in execute_data so we don't resume at an old position. */ SAVE_OPLINE(); @@ -44630,31 +44639,6 @@ void zend_init_opcodes_handlers(void) ZEND_JMP_SET_VAR_SPEC_CV_HANDLER, ZEND_JMP_SET_VAR_SPEC_CV_HANDLER, ZEND_JMP_SET_VAR_SPEC_CV_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, - ZEND_SUSPEND_AND_RETURN_GENERATOR_SPEC_HANDLER, ZEND_YIELD_SPEC_CONST_CONST_HANDLER, ZEND_YIELD_SPEC_CONST_TMP_HANDLER, ZEND_YIELD_SPEC_CONST_VAR_HANDLER, diff --git a/Zend/zend_vm_execute.skl b/Zend/zend_vm_execute.skl index e77c7cb3c63b..909ec8ad7a73 100644 --- a/Zend/zend_vm_execute.skl +++ b/Zend/zend_vm_execute.skl @@ -1,6 +1,6 @@ {%DEFINES%} -static zend_execute_data *zend_create_execute_data_from_op_array(zend_op_array *op_array, zend_bool nested TSRMLS_DC) { +zend_execute_data *zend_create_execute_data_from_op_array(zend_op_array *op_array, zend_bool nested TSRMLS_DC) { zend_execute_data *execute_data; /* diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 46a711d17931..87fabfc2acc1 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -159,6 +159,5 @@ #define ZEND_SEPARATE 156 #define ZEND_QM_ASSIGN_VAR 157 #define ZEND_JMP_SET_VAR 158 -#define ZEND_SUSPEND_AND_RETURN_GENERATOR 159 -#define ZEND_YIELD 160 -#define ZEND_DELEGATE_YIELD 161 +#define ZEND_YIELD 159 +#define ZEND_DELEGATE_YIELD 160 From 612c2490b7973d71d472860ade48d7ab342b5911 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 20 Jul 2012 17:38:39 +0200 Subject: [PATCH 50/70] Move a variable --- Zend/zend_vm_def.h | 3 +- Zend/zend_vm_execute.h | 75 ++++++++++++++++++++++++++++-------------- 2 files changed, 52 insertions(+), 26 deletions(-) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index e8b89a6db5b3..6b8425558c98 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5225,7 +5225,6 @@ ZEND_VM_HANDLER(156, ZEND_SEPARATE, VAR, UNUSED) ZEND_VM_HANDLER(159, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSED) { USE_OPLINE - zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -5242,6 +5241,8 @@ ZEND_VM_HANDLER(159, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE /* Set the new yielded value */ if (OP1_TYPE != IS_UNUSED) { + zend_free_op free_op1; + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 0fcae113baa4..9a9917fede26 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -4027,7 +4027,6 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLE { USE_OPLINE - /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -4043,6 +4042,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLE /* Set the new yielded value */ if (IS_CONST != IS_UNUSED) { + + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -4716,7 +4717,6 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMP_HANDLER(ZEND_OPCODE_HANDLER_ { USE_OPLINE - /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -4732,6 +4732,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMP_HANDLER(ZEND_OPCODE_HANDLER_ /* Set the new yielded value */ if (IS_CONST != IS_UNUSED) { + + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -5730,7 +5732,6 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ { USE_OPLINE - /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -5746,6 +5747,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ /* Set the new yielded value */ if (IS_CONST != IS_UNUSED) { + + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -6439,7 +6442,6 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDL { USE_OPLINE - /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -6455,6 +6457,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDL /* Set the new yielded value */ if (IS_CONST != IS_UNUSED) { + + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -7187,7 +7191,6 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_A { USE_OPLINE - /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -7203,6 +7206,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_A /* Set the new yielded value */ if (IS_CONST != IS_UNUSED) { + + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -9242,7 +9247,6 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_TMP_CONST_HANDLER(ZEND_OPC static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -9259,6 +9263,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ /* Set the new yielded value */ if (IS_TMP_VAR != IS_UNUSED) { + zend_free_op free_op1; + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -9931,7 +9937,6 @@ static int ZEND_FASTCALL ZEND_INIT_ARRAY_SPEC_TMP_TMP_HANDLER(ZEND_OPCODE_HANDL static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -9948,6 +9953,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_TMP_HANDLER(ZEND_OPCODE_HANDLER_AR /* Set the new yielded value */ if (IS_TMP_VAR != IS_UNUSED) { + zend_free_op free_op1; + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -10945,7 +10952,6 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_TMP_VAR_HANDLER(ZEND_OPCOD static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -10962,6 +10968,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_VAR_HANDLER(ZEND_OPCODE_HANDLER_AR /* Set the new yielded value */ if (IS_TMP_VAR != IS_UNUSED) { + zend_free_op free_op1; + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -11520,7 +11528,6 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_VAR_SPEC_TMP_UNUSED_HANDLER(ZEND_OP static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -11537,6 +11544,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCODE_HANDLER /* Set the new yielded value */ if (IS_TMP_VAR != IS_UNUSED) { + zend_free_op free_op1; + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -12206,7 +12215,6 @@ static int ZEND_FASTCALL ZEND_INIT_ARRAY_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLE static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -12223,6 +12231,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARG /* Set the new yielded value */ if (IS_TMP_VAR != IS_UNUSED) { + zend_free_op free_op1; + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -16118,7 +16128,6 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_VAR_CONST_HANDLER(ZEN static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -16135,6 +16144,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ /* Set the new yielded value */ if (IS_VAR != IS_UNUSED) { + zend_free_op free_op1; + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -18195,7 +18206,6 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_VAR_TMP_HANDLER(ZEND_ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -18212,6 +18222,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_TMP_HANDLER(ZEND_OPCODE_HANDLER_AR /* Set the new yielded value */ if (IS_VAR != IS_UNUSED) { + zend_free_op free_op1; + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -20652,7 +20664,6 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_VAR_VAR_HANDLER(ZEND_ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -20669,6 +20680,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_AR /* Set the new yielded value */ if (IS_VAR != IS_UNUSED) { + zend_free_op free_op1; + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -21788,7 +21801,6 @@ static int ZEND_FASTCALL ZEND_SEPARATE_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HAND static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -21805,6 +21817,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER /* Set the new yielded value */ if (IS_VAR != IS_UNUSED) { + zend_free_op free_op1; + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -23914,7 +23928,6 @@ static int ZEND_FASTCALL ZEND_ISSET_ISEMPTY_PROP_OBJ_SPEC_VAR_CV_HANDLER(ZEND_O static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE - zend_free_op free_op1; /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -23931,6 +23944,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARG /* Set the new yielded value */ if (IS_VAR != IS_UNUSED) { + zend_free_op free_op1; + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -25400,7 +25415,6 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDL { USE_OPLINE - /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -25416,6 +25430,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDL /* Set the new yielded value */ if (IS_UNUSED != IS_UNUSED) { + + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -26707,7 +26723,6 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER(ZEND_OPCODE_HANDLER { USE_OPLINE - /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -26723,6 +26738,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER(ZEND_OPCODE_HANDLER /* Set the new yielded value */ if (IS_UNUSED != IS_UNUSED) { + + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -28014,7 +28031,6 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER(ZEND_OPCODE_HANDLER { USE_OPLINE - /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -28030,6 +28046,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER(ZEND_OPCODE_HANDLER /* Set the new yielded value */ if (IS_UNUSED != IS_UNUSED) { + + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -28433,7 +28451,6 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HAND { USE_OPLINE - /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -28449,6 +28466,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HAND /* Set the new yielded value */ if (IS_UNUSED != IS_UNUSED) { + + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -29737,7 +29756,6 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ { USE_OPLINE - /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -29753,6 +29771,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ /* Set the new yielded value */ if (IS_UNUSED != IS_UNUSED) { + + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -33250,7 +33270,6 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_A { USE_OPLINE - /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -33266,6 +33285,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_A /* Set the new yielded value */ if (IS_CV != IS_UNUSED) { + + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -35196,7 +35217,6 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARG { USE_OPLINE - /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -35212,6 +35232,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARG /* Set the new yielded value */ if (IS_CV != IS_UNUSED) { + + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -37521,7 +37543,6 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG { USE_OPLINE - /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -37537,6 +37558,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG /* Set the new yielded value */ if (IS_CV != IS_UNUSED) { + + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -38516,7 +38539,6 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ { USE_OPLINE - /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -38532,6 +38554,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ /* Set the new yielded value */ if (IS_CV != IS_UNUSED) { + + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ @@ -40510,7 +40534,6 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS { USE_OPLINE - /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); @@ -40526,6 +40549,8 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS /* Set the new yielded value */ if (IS_CV != IS_UNUSED) { + + if (EX(op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) { /* Constants and temporary variables aren't yieldable by reference, * but we still allow them with a notice. */ From 1f70a4c5fea97aa577aa5d9ee5f33d91d70e690d Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 20 Jul 2012 17:40:04 +0200 Subject: [PATCH 51/70] Add some more tests --- Zend/tests/generators/fibonacci.phpt | 36 +++++++++++++++++++ Zend/tests/generators/generator_closure.phpt | 20 +++++++++++ .../generator_closure_with_this.phpt | 20 +++++++++++ .../generators/generator_static_method.phpt | 29 +++++++++++++++ 4 files changed, 105 insertions(+) create mode 100644 Zend/tests/generators/fibonacci.phpt create mode 100644 Zend/tests/generators/generator_closure.phpt create mode 100644 Zend/tests/generators/generator_closure_with_this.phpt create mode 100644 Zend/tests/generators/generator_static_method.phpt diff --git a/Zend/tests/generators/fibonacci.phpt b/Zend/tests/generators/fibonacci.phpt new file mode 100644 index 000000000000..35b31352ff3e --- /dev/null +++ b/Zend/tests/generators/fibonacci.phpt @@ -0,0 +1,36 @@ +--TEST-- +Creating an infinite fibonacci list using a generator +--FILE-- + 1000) break; + + var_dump($n); +} + +?> +--EXPECT-- +int(1) +int(2) +int(3) +int(5) +int(8) +int(13) +int(21) +int(34) +int(55) +int(89) +int(144) +int(233) +int(377) +int(610) +int(987) diff --git a/Zend/tests/generators/generator_closure.phpt b/Zend/tests/generators/generator_closure.phpt new file mode 100644 index 000000000000..bf80066015f5 --- /dev/null +++ b/Zend/tests/generators/generator_closure.phpt @@ -0,0 +1,20 @@ +--TEST-- +Closures can be generators +--FILE-- + +--EXPECT-- +int(1) +int(2) +int(3) diff --git a/Zend/tests/generators/generator_closure_with_this.phpt b/Zend/tests/generators/generator_closure_with_this.phpt new file mode 100644 index 000000000000..d5a4861e8046 --- /dev/null +++ b/Zend/tests/generators/generator_closure_with_this.phpt @@ -0,0 +1,20 @@ +--TEST-- +Non-static closures can be generators +--FILE-- +getGenFactory(); +var_dump($genFactory()->current()); + +?> +--EXPECT-- +object(Test)#1 (0) { +} diff --git a/Zend/tests/generators/generator_static_method.phpt b/Zend/tests/generators/generator_static_method.phpt new file mode 100644 index 000000000000..cd9b450a76ca --- /dev/null +++ b/Zend/tests/generators/generator_static_method.phpt @@ -0,0 +1,29 @@ +--TEST-- +A static method can be a generator +--FILE-- + +--EXPECT-- +string(4) "Test" +string(12) "ExtendedTest" +int(1) +int(2) +int(3) From 80748631aa1c4193cbc68f8854d82e7a57817fe2 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 21 Jul 2012 21:05:46 +0200 Subject: [PATCH 52/70] Require parenthesis around yield expressions If yield is used in an expression context parenthesis are now required. This ensures that the code is unambiguos. Yield statements can still be used without parenthesis (which should be the most common case). Also yield expressions without value can be used without parenthesis, too (this should be the most common case for coroutines). If the yield expression is used in a context where parenthesis are required anyway, no additional parenthesis have to be inserted. Examples: // Statements don't need parenthesis yield $foo; yield $foo => $bar; // Yield without value doesn't need parenthesis either $data = yield; // Parentheses don't have to be duplicated foo(yield $bar); if (yield $bar) { ... } // But we have to use parentheses here $foo = (yield $bar); This commit also fixes an issue with by-ref passing of $foo[0] like variables. They previously weren't properly fetched for write. Additionally this fixes valgrind warnings which were caused by access to uninitialized memory in zend_is_function_or_method_call(). --- .../generators/send_returns_current.phpt | 2 +- .../generators/yield_array_offset_by_ref.phpt | 26 ++++++ .../generators/yield_in_parenthesis.phpt | 23 +++++ Zend/zend_compile.c | 12 ++- Zend/zend_compile.h | 2 +- Zend/zend_language_parser.y | 91 ++++++++++--------- 6 files changed, 109 insertions(+), 47 deletions(-) create mode 100644 Zend/tests/generators/yield_array_offset_by_ref.phpt create mode 100644 Zend/tests/generators/yield_in_parenthesis.phpt diff --git a/Zend/tests/generators/send_returns_current.phpt b/Zend/tests/generators/send_returns_current.phpt index fc260c0af0c4..27ba74bc1bd8 100644 --- a/Zend/tests/generators/send_returns_current.phpt +++ b/Zend/tests/generators/send_returns_current.phpt @@ -6,7 +6,7 @@ $generator->send() returns the yielded value function reverseEchoGenerator() { $data = yield; while (true) { - $data = yield strrev($data); + $data = (yield strrev($data)); } } diff --git a/Zend/tests/generators/yield_array_offset_by_ref.phpt b/Zend/tests/generators/yield_array_offset_by_ref.phpt new file mode 100644 index 000000000000..544108e64d89 --- /dev/null +++ b/Zend/tests/generators/yield_array_offset_by_ref.phpt @@ -0,0 +1,26 @@ +--TEST-- +Array offsets can be yielded by reference +--FILE-- + +--EXPECT-- +array(3) { + [0]=> + &int(-1) + [1]=> + int(2) + [2]=> + int(3) +} diff --git a/Zend/tests/generators/yield_in_parenthesis.phpt b/Zend/tests/generators/yield_in_parenthesis.phpt new file mode 100644 index 000000000000..4a603f4cc1a5 --- /dev/null +++ b/Zend/tests/generators/yield_in_parenthesis.phpt @@ -0,0 +1,23 @@ +--TEST-- +No additional parenthesis are required around yield if they are already present +--FILE-- +func(yield $foo); + new Foo(yield $foo); +} + +echo "Done"; + +?> +--EXPECT-- +Done diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index e030f94c78b4..51fc8c3e5be3 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2663,7 +2663,7 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */ } /* }}} */ -void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC) /* {{{ */ +void zend_do_yield(znode *result, znode *value, const znode *key, zend_bool is_variable TSRMLS_DC) /* {{{ */ { zend_op *opline; @@ -2673,6 +2673,14 @@ void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC CG(active_op_array)->fn_flags |= ZEND_ACC_GENERATOR; + if (is_variable) { + if ((CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) && !zend_is_function_or_method_call(value)) { + zend_do_end_variable_parse(value, BP_VAR_W, 0 TSRMLS_CC); + } else { + zend_do_end_variable_parse(value, BP_VAR_R, 0 TSRMLS_CC); + } + } + opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = ZEND_YIELD; @@ -2680,7 +2688,7 @@ void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC if (value) { SET_NODE(opline->op1, value); - if (zend_is_function_or_method_call(value)) { + if (is_variable && zend_is_function_or_method_call(value)) { opline->extended_value = ZEND_RETURNS_FUNCTION; } } else { diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index c4355e0dde0e..1972f85c6678 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -490,7 +490,7 @@ void zend_do_build_full_name(znode *result, znode *prefix, znode *name, int is_c int zend_do_begin_class_member_function_call(znode *class_name, znode *method_name TSRMLS_DC); void zend_do_end_function_call(znode *function_name, znode *result, const znode *argument_list, int is_method, int is_dynamic_fcall TSRMLS_DC); void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC); -void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC); +void zend_do_yield(znode *result, znode *value, const znode *key, zend_bool is_variable TSRMLS_DC); void zend_do_delegate_yield(znode *result, const znode *value TSRMLS_DC); void zend_do_handle_exception(TSRMLS_D); diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index b705d60d9a53..e5f31b5d149a 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -278,10 +278,10 @@ statement: unticked_statement: '{' inner_statement_list '}' - | T_IF '(' expr ')' { zend_do_if_cond(&$3, &$4 TSRMLS_CC); } statement { zend_do_if_after_statement(&$4, 1 TSRMLS_CC); } elseif_list else_single { zend_do_if_end(TSRMLS_C); } - | T_IF '(' expr ')' ':' { zend_do_if_cond(&$3, &$4 TSRMLS_CC); } inner_statement_list { zend_do_if_after_statement(&$4, 1 TSRMLS_CC); } new_elseif_list new_else_single T_ENDIF ';' { zend_do_if_end(TSRMLS_C); } - | T_WHILE '(' { $1.u.op.opline_num = get_next_op_number(CG(active_op_array)); } expr ')' { zend_do_while_cond(&$4, &$5 TSRMLS_CC); } while_statement { zend_do_while_end(&$1, &$5 TSRMLS_CC); } - | T_DO { $1.u.op.opline_num = get_next_op_number(CG(active_op_array)); zend_do_do_while_begin(TSRMLS_C); } statement T_WHILE '(' { $5.u.op.opline_num = get_next_op_number(CG(active_op_array)); } expr ')' ';' { zend_do_do_while_end(&$1, &$5, &$7 TSRMLS_CC); } + | T_IF parenthesis_expr { zend_do_if_cond(&$2, &$1 TSRMLS_CC); } statement { zend_do_if_after_statement(&$1, 1 TSRMLS_CC); } elseif_list else_single { zend_do_if_end(TSRMLS_C); } + | T_IF parenthesis_expr ':' { zend_do_if_cond(&$2, &$1 TSRMLS_CC); } inner_statement_list { zend_do_if_after_statement(&$1, 1 TSRMLS_CC); } new_elseif_list new_else_single T_ENDIF ';' { zend_do_if_end(TSRMLS_C); } + | T_WHILE { $1.u.op.opline_num = get_next_op_number(CG(active_op_array)); } parenthesis_expr { zend_do_while_cond(&$3, &$$ TSRMLS_CC); } while_statement { zend_do_while_end(&$1, &$4 TSRMLS_CC); } + | T_DO { $1.u.op.opline_num = get_next_op_number(CG(active_op_array)); zend_do_do_while_begin(TSRMLS_C); } statement T_WHILE { $4.u.op.opline_num = get_next_op_number(CG(active_op_array)); } parenthesis_expr ';' { zend_do_do_while_end(&$1, &$4, &$6 TSRMLS_CC); } | T_FOR '(' for_expr @@ -291,7 +291,7 @@ unticked_statement: for_expr ')' { zend_do_free(&$9 TSRMLS_CC); zend_do_for_before_statement(&$4, &$7 TSRMLS_CC); } for_statement { zend_do_for_end(&$7 TSRMLS_CC); } - | T_SWITCH '(' expr ')' { zend_do_switch_cond(&$3 TSRMLS_CC); } switch_case_list { zend_do_switch_end(&$6 TSRMLS_CC); } + | T_SWITCH parenthesis_expr { zend_do_switch_cond(&$2 TSRMLS_CC); } switch_case_list { zend_do_switch_end(&$4 TSRMLS_CC); } | T_BREAK ';' { zend_do_brk_cont(ZEND_BRK, NULL TSRMLS_CC); } | T_BREAK expr ';' { zend_do_brk_cont(ZEND_BRK, &$2 TSRMLS_CC); } | T_CONTINUE ';' { zend_do_brk_cont(ZEND_CONT, NULL TSRMLS_CC); } @@ -299,7 +299,7 @@ unticked_statement: | T_RETURN ';' { zend_do_return(NULL, 0 TSRMLS_CC); } | T_RETURN expr_without_variable ';' { zend_do_return(&$2, 0 TSRMLS_CC); } | T_RETURN variable ';' { zend_do_return(&$2, 1 TSRMLS_CC); } - | T_YIELD expr T_DOUBLE_ARROW expr ';' { zend_do_yield(&$$, &$4, &$2 TSRMLS_CC); } + | yield_expr ';' { $$ = $1; } | T_GLOBAL global_var_list ';' | T_STATIC static_var_list ';' | T_ECHO echo_expr_list ';' @@ -484,13 +484,13 @@ while_statement: elseif_list: /* empty */ - | elseif_list T_ELSEIF '(' expr ')' { zend_do_if_cond(&$4, &$5 TSRMLS_CC); } statement { zend_do_if_after_statement(&$5, 0 TSRMLS_CC); } + | elseif_list T_ELSEIF parenthesis_expr { zend_do_if_cond(&$3, &$2 TSRMLS_CC); } statement { zend_do_if_after_statement(&$2, 0 TSRMLS_CC); } ; new_elseif_list: /* empty */ - | new_elseif_list T_ELSEIF '(' expr ')' ':' { zend_do_if_cond(&$4, &$5 TSRMLS_CC); } inner_statement_list { zend_do_if_after_statement(&$5, 0 TSRMLS_CC); } + | new_elseif_list T_ELSEIF parenthesis_expr ':' { zend_do_if_cond(&$3, &$2 TSRMLS_CC); } inner_statement_list { zend_do_if_after_statement(&$2, 0 TSRMLS_CC); } ; @@ -533,8 +533,9 @@ optional_class_type: function_call_parameter_list: - non_empty_function_call_parameter_list { $$ = $1; } - | /* empty */ { Z_LVAL($$.u.constant) = 0; } + '(' ')' { Z_LVAL($$.u.constant) = 0; } + | '(' non_empty_function_call_parameter_list ')' { $$ = $2; } + | '(' yield_expr ')' { Z_LVAL($$.u.constant) = 1; zend_do_pass_param(&$2, ZEND_SEND_VAL, Z_LVAL($$.u.constant) TSRMLS_CC); } ; @@ -774,7 +775,7 @@ expr_without_variable: | expr '>' expr { zend_do_binary_op(ZEND_IS_SMALLER, &$$, &$3, &$1 TSRMLS_CC); } | expr T_IS_GREATER_OR_EQUAL expr { zend_do_binary_op(ZEND_IS_SMALLER_OR_EQUAL, &$$, &$3, &$1 TSRMLS_CC); } | expr T_INSTANCEOF class_name_reference { zend_do_instanceof(&$$, &$1, &$3, 0 TSRMLS_CC); } - | '(' expr ')' { $$ = $2; } + | parenthesis_expr { $$ = $1; } | new_expr { $$ = $1; } | '(' new_expr ')' { $$ = $2; } instance_call { $$ = $5; } | expr '?' { zend_do_begin_qm_op(&$1, &$2 TSRMLS_CC); } @@ -797,8 +798,9 @@ expr_without_variable: | combined_scalar { $$ = $1; } | '`' backticks_expr '`' { zend_do_shell_exec(&$$, &$2 TSRMLS_CC); } | T_PRINT expr { zend_do_print(&$$, &$2 TSRMLS_CC); } - | T_YIELD { zend_do_yield(&$$, NULL, NULL TSRMLS_CC); } - | T_YIELD expr { zend_do_yield(&$$, &$2, NULL TSRMLS_CC); } + | T_YIELD { zend_do_yield(&$$, NULL, NULL, 0 TSRMLS_CC); } + /*| T_YIELD expr_without_variable { zend_do_yield(&$$, &$2, NULL, 0 TSRMLS_CC); } + | T_YIELD variable { zend_do_yield(&$$, &$2, NULL, 1 TSRMLS_CC); }*/ | T_YIELD '*' expr { zend_do_delegate_yield(&$$, &$3 TSRMLS_CC); } | function is_reference { zend_do_begin_lambda_function_declaration(&$$, &$1, $2.op_type, 0 TSRMLS_CC); } '(' parameter_list ')' lexical_vars @@ -808,6 +810,13 @@ expr_without_variable: '{' inner_statement_list '}' { zend_do_end_function_declaration(&$2 TSRMLS_CC); $$ = $4; } ; +yield_expr: + T_YIELD expr_without_variable { zend_do_yield(&$$, &$2, NULL, 0 TSRMLS_CC); } + | T_YIELD variable { zend_do_yield(&$$, &$2, NULL, 1 TSRMLS_CC); } + | T_YIELD expr T_DOUBLE_ARROW expr_without_variable { zend_do_yield(&$$, &$4, &$2, 0 TSRMLS_CC); } + | T_YIELD expr T_DOUBLE_ARROW variable { zend_do_yield(&$$, &$4, &$2, 1 TSRMLS_CC); } +; + combined_scalar_offset: combined_scalar '[' dim_offset ']' { zend_do_begin_variable_parse(TSRMLS_C); fetch_array_dim(&$$, &$1, &$3 TSRMLS_CC); } | combined_scalar_offset '[' dim_offset ']' { fetch_array_dim(&$$, &$1, &$3 TSRMLS_CC); } @@ -834,30 +843,22 @@ lexical_var_list: ; function_call: - namespace_name '(' { $2.u.op.opline_num = zend_do_begin_function_call(&$1, 1 TSRMLS_CC); } - function_call_parameter_list - ')' { zend_do_end_function_call(&$1, &$$, &$4, 0, $2.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); } - | T_NAMESPACE T_NS_SEPARATOR namespace_name '(' { $1.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$1.u.constant); zend_do_build_namespace_name(&$1, &$1, &$3 TSRMLS_CC); $4.u.op.opline_num = zend_do_begin_function_call(&$1, 0 TSRMLS_CC); } - function_call_parameter_list - ')' { zend_do_end_function_call(&$1, &$$, &$6, 0, $4.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); } - | T_NS_SEPARATOR namespace_name '(' { $3.u.op.opline_num = zend_do_begin_function_call(&$2, 0 TSRMLS_CC); } - function_call_parameter_list - ')' { zend_do_end_function_call(&$2, &$$, &$5, 0, $3.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); } - | class_name T_PAAMAYIM_NEKUDOTAYIM variable_name '(' { $4.u.op.opline_num = zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } - function_call_parameter_list - ')' { zend_do_end_function_call($4.u.op.opline_num?NULL:&$3, &$$, &$6, $4.u.op.opline_num, $4.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} - | class_name T_PAAMAYIM_NEKUDOTAYIM variable_without_objects '(' { zend_do_end_variable_parse(&$3, BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } - function_call_parameter_list - ')' { zend_do_end_function_call(NULL, &$$, &$6, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} - | variable_class_name T_PAAMAYIM_NEKUDOTAYIM variable_name '(' { zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } - function_call_parameter_list - ')' { zend_do_end_function_call(NULL, &$$, &$6, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} - | variable_class_name T_PAAMAYIM_NEKUDOTAYIM variable_without_objects '(' { zend_do_end_variable_parse(&$3, BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } - function_call_parameter_list - ')' { zend_do_end_function_call(NULL, &$$, &$6, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} - | variable_without_objects '(' { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_dynamic_function_call(&$1, 0 TSRMLS_CC); } - function_call_parameter_list ')' - { zend_do_end_function_call(&$1, &$$, &$4, 0, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} + namespace_name { $$.u.op.opline_num = zend_do_begin_function_call(&$1, 1 TSRMLS_CC); } + function_call_parameter_list { zend_do_end_function_call(&$1, &$$, &$3, 0, $2.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); } + | T_NAMESPACE T_NS_SEPARATOR namespace_name { $1.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$1.u.constant); zend_do_build_namespace_name(&$1, &$1, &$3 TSRMLS_CC); $$.u.op.opline_num = zend_do_begin_function_call(&$1, 0 TSRMLS_CC); } + function_call_parameter_list { zend_do_end_function_call(&$1, &$$, &$5, 0, $4.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); } + | T_NS_SEPARATOR namespace_name { $$.u.op.opline_num = zend_do_begin_function_call(&$2, 0 TSRMLS_CC); } + function_call_parameter_list { zend_do_end_function_call(&$2, &$$, &$4, 0, $3.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); } + | class_name T_PAAMAYIM_NEKUDOTAYIM variable_name { $$.u.op.opline_num = zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } + function_call_parameter_list { zend_do_end_function_call($4.u.op.opline_num?NULL:&$3, &$$, &$5, $4.u.op.opline_num, $4.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} + | class_name T_PAAMAYIM_NEKUDOTAYIM variable_without_objects { zend_do_end_variable_parse(&$3, BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } + function_call_parameter_list { zend_do_end_function_call(NULL, &$$, &$5, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} + | variable_class_name T_PAAMAYIM_NEKUDOTAYIM variable_name { zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } + function_call_parameter_list { zend_do_end_function_call(NULL, &$$, &$5, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} + | variable_class_name T_PAAMAYIM_NEKUDOTAYIM variable_without_objects { zend_do_end_variable_parse(&$3, BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); } + function_call_parameter_list { zend_do_end_function_call(NULL, &$$, &$5, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} + | variable_without_objects { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); zend_do_begin_dynamic_function_call(&$1, 0 TSRMLS_CC); } + function_call_parameter_list { zend_do_end_function_call(&$1, &$$, &$3, 0, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);} ; class_name: @@ -902,7 +903,7 @@ dynamic_class_name_variable_property: exit_expr: /* empty */ { memset(&$$, 0, sizeof(znode)); $$.op_type = IS_UNUSED; } | '(' ')' { memset(&$$, 0, sizeof(znode)); $$.op_type = IS_UNUSED; } - | '(' expr ')' { $$ = $2; } + | parenthesis_expr { $$ = $1; } ; backticks_expr: @@ -913,8 +914,8 @@ backticks_expr: ctor_arguments: - /* empty */ { Z_LVAL($$.u.constant)=0; } - | '(' function_call_parameter_list ')' { $$ = $2; } + /* empty */ { Z_LVAL($$.u.constant) = 0; } + | function_call_parameter_list { $$ = $1; } ; @@ -986,6 +987,11 @@ expr: | expr_without_variable { $$ = $1; } ; +parenthesis_expr: + '(' expr ')' { $$ = $2; } + | '(' yield_expr ')' { $$ = $2; } +; + r_variable: variable { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); $$ = $1; } @@ -1025,9 +1031,8 @@ array_method_dereference: ; method: - '(' { zend_do_pop_object(&$1 TSRMLS_CC); zend_do_begin_method_call(&$1 TSRMLS_CC); } - function_call_parameter_list ')' - { zend_do_end_function_call(&$1, &$$, &$3, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); } + { zend_do_pop_object(&$$ TSRMLS_CC); zend_do_begin_method_call(&$$ TSRMLS_CC); } + function_call_parameter_list { zend_do_end_function_call(&$1, &$$, &$2, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); } ; method_or_not: From de80e3ce4b5b7a9ec0cfdd0778e77027a7ebfcc2 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 22 Jul 2012 14:33:25 +0200 Subject: [PATCH 53/70] Remove reference restrictions from foreach foreach only allowed variables to be traversed by reference. This never really made sense because a) Expressions like array(&$a, &$b) can be meaningfully iterated by-ref b) Function calls can return by-ref (so they can also be meaningfully iterated) c) Iterators could at least in theory also be iterated by-ref (not sure if any iterator makes use of this) With by-ref generators the restriction makes even less sense, so I removed it altogether. --- Zend/tests/errmsg_043.phpt | 12 ---------- .../foreach_temp_array_expr_with_refs.phpt | 18 ++++++++++++++ Zend/tests/generators/yield_by_reference.phpt | 24 +++++++++++++------ Zend/zend_compile.c | 4 +--- Zend/zend_language_parser.y | 2 +- tests/lang/foreachLoop.008.phpt | 10 -------- 6 files changed, 37 insertions(+), 33 deletions(-) delete mode 100644 Zend/tests/errmsg_043.phpt create mode 100644 Zend/tests/foreach_temp_array_expr_with_refs.phpt delete mode 100644 tests/lang/foreachLoop.008.phpt diff --git a/Zend/tests/errmsg_043.phpt b/Zend/tests/errmsg_043.phpt deleted file mode 100644 index 3de8bc2062e9..000000000000 --- a/Zend/tests/errmsg_043.phpt +++ /dev/null @@ -1,12 +0,0 @@ ---TEST-- -errmsg: cannot create references to temp array ---FILE-- -&$v) { -} - -echo "Done\n"; -?> ---EXPECTF-- -Fatal error: Cannot create references to elements of a temporary array expression in %s on line %d diff --git a/Zend/tests/foreach_temp_array_expr_with_refs.phpt b/Zend/tests/foreach_temp_array_expr_with_refs.phpt new file mode 100644 index 000000000000..8978b7b01141 --- /dev/null +++ b/Zend/tests/foreach_temp_array_expr_with_refs.phpt @@ -0,0 +1,18 @@ +--TEST-- +Temporary array expressions can be iterated by reference +--FILE-- + +--EXPECT-- +string(5) "a-foo" +string(5) "b-foo" diff --git a/Zend/tests/generators/yield_by_reference.phpt b/Zend/tests/generators/yield_by_reference.phpt index 5a6c169b3e23..dba0791c0d49 100644 --- a/Zend/tests/generators/yield_by_reference.phpt +++ b/Zend/tests/generators/yield_by_reference.phpt @@ -9,24 +9,34 @@ function &iter(array &$array) { } } -$array = [1, 2, 3, 4, 5]; +$array = [1, 2, 3]; $iter = iter($array); foreach ($iter as &$value) { $value *= -1; } var_dump($array); +$array = [1, 2, 3]; +foreach (iter($array) as &$value) { + $value *= -1; +} +var_dump($array); + ?> --EXPECT-- -array(5) { +array(3) { + [0]=> + int(-1) + [1]=> + int(-2) + [2]=> + &int(-3) +} +array(3) { [0]=> int(-1) [1]=> int(-2) [2]=> - int(-3) - [3]=> - int(-4) - [4]=> - &int(-5) + &int(-3) } diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 51fc8c3e5be3..f0648a2d977f 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6317,9 +6317,7 @@ void zend_do_foreach_cont(znode *foreach_token, const znode *open_brackets_token if (value->EA & ZEND_PARSED_REFERENCE_VARIABLE) { assign_by_ref = 1; - if (!(opline-1)->extended_value) { - zend_error(E_COMPILE_ERROR, "Cannot create references to elements of a temporary array expression"); - } + /* Mark extended_value for assign-by-reference */ opline->extended_value |= ZEND_FE_FETCH_BYREF; CG(active_op_array)->opcodes[foreach_token->u.op.opline_num].extended_value |= ZEND_FE_RESET_REFERENCE; diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index e5f31b5d149a..4221752dbab0 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -312,7 +312,7 @@ unticked_statement: foreach_statement { zend_do_foreach_end(&$1, &$4 TSRMLS_CC); } | T_FOREACH '(' expr_without_variable T_AS { zend_do_foreach_begin(&$1, &$2, &$3, &$4, 0 TSRMLS_CC); } - variable foreach_optional_arg ')' { zend_check_writable_variable(&$6); zend_do_foreach_cont(&$1, &$2, &$4, &$6, &$7 TSRMLS_CC); } + foreach_variable foreach_optional_arg ')' { zend_do_foreach_cont(&$1, &$2, &$4, &$6, &$7 TSRMLS_CC); } foreach_statement { zend_do_foreach_end(&$1, &$4 TSRMLS_CC); } | T_DECLARE { $1.u.op.opline_num = get_next_op_number(CG(active_op_array)); zend_do_declare_begin(TSRMLS_C); } '(' declare_list ')' declare_statement { zend_do_declare_end(&$1 TSRMLS_CC); } | ';' /* empty statement */ diff --git a/tests/lang/foreachLoop.008.phpt b/tests/lang/foreachLoop.008.phpt deleted file mode 100644 index 787f43b88375..000000000000 --- a/tests/lang/foreachLoop.008.phpt +++ /dev/null @@ -1,10 +0,0 @@ ---TEST-- -Foreach loop tests - error case: reference to constant array, with key. ---FILE-- -&$v) { - var_dump($v); -} -?> ---EXPECTF-- -Fatal error: Cannot create references to elements of a temporary array expression in %s on line 2 From 94b2ccae9ce95c4c71bb8db8ce75dcdf26df7d7a Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 22 Jul 2012 17:46:46 +0200 Subject: [PATCH 54/70] Fix throwing of exceptions within a generator If a generator threw an exception and was iterated using foreach (i.e. not manually) an infinite loop was triggered. The reason was that the exception was not properly rethrown using zend_throw_exception_internal. --- Zend/tests/generators/backtrace.phpt | 2 +- .../generator_throwing_in_foreach.phpt | 20 +++++++++++++++++++ Zend/zend_generators.c | 12 ++++++++--- Zend/zend_vm_def.h | 2 +- Zend/zend_vm_execute.h | 2 +- 5 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 Zend/tests/generators/generator_throwing_in_foreach.phpt diff --git a/Zend/tests/generators/backtrace.phpt b/Zend/tests/generators/backtrace.phpt index 5f665b7e4ac1..5fed1d467e67 100644 --- a/Zend/tests/generators/backtrace.phpt +++ b/Zend/tests/generators/backtrace.phpt @@ -22,6 +22,6 @@ f3($gen); ?> --EXPECTF-- #0 f1() called at [%s:%d] -#1 f2(foo, bar) called at [%s:%d] +#1 f2(foo, bar) #2 Generator->rewind() called at [%s:%d] #3 f3(Generator Object ()) called at [%s:%d] diff --git a/Zend/tests/generators/generator_throwing_in_foreach.phpt b/Zend/tests/generators/generator_throwing_in_foreach.phpt new file mode 100644 index 000000000000..dbf20c2ca1c6 --- /dev/null +++ b/Zend/tests/generators/generator_throwing_in_foreach.phpt @@ -0,0 +1,20 @@ +--TEST-- +Exceptions throwing by generators during foreach iteration are properly handled +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught exception 'Exception' with message 'foo' in %s:%d +Stack trace: +#0 %s(%d): gen() +#1 {main} + thrown in %s on line %d + diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index b164fb835e53..d7ffb3055a6a 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -21,6 +21,7 @@ #include "zend.h" #include "zend_API.h" #include "zend_interfaces.h" +#include "zend_exceptions.h" #include "zend_generators.h" ZEND_API zend_class_entry *zend_ce_generator; @@ -352,12 +353,11 @@ zval *zend_generator_create_zval(zend_op_array *op_array TSRMLS_DC) /* {{{ */ * up in backtraces and func_get_all() can access the function * arguments. */ execute_data->prev_execute_data = emalloc(sizeof(zend_execute_data)); + memset(execute_data->prev_execute_data, 0, sizeof(zend_execute_data)); + execute_data->prev_execute_data->function_state.function = (zend_function *) op_array; if (EG(current_execute_data)) { - memcpy(execute_data->prev_execute_data, EG(current_execute_data), sizeof(zend_execute_data)); execute_data->prev_execute_data->function_state.arguments = zend_copy_arguments(EG(current_execute_data)->function_state.arguments); } else { - memset(execute_data->prev_execute_data, 0, sizeof(zend_execute_data)); - execute_data->prev_execute_data->function_state.function = (zend_function *) op_array; execute_data->prev_execute_data->function_state.arguments = NULL; } @@ -450,6 +450,12 @@ static void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */ memcpy(generator->backed_up_stack, generator->original_stack_top, generator->backed_up_stack_size); zend_vm_stack_free(generator->original_stack_top TSRMLS_CC); } + + /* If an exception was thrown in the generator we have to internally + * rethrow it in the parent scope. */ + if (UNEXPECTED(EG(exception) != NULL)) { + zend_throw_exception_internal(NULL TSRMLS_CC); + } } } /* }}} */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 6b8425558c98..03768e8bd74f 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5088,7 +5088,7 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) EX(old_error_reporting) = NULL; if (!catched) { - /* For generators skip the leave handler return directly */ + /* For generators skip the leave handler and return directly */ if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 9a9917fede26..5ded3e39c7e2 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1151,7 +1151,7 @@ static int ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER EX(old_error_reporting) = NULL; if (!catched) { - /* For generators skip the leave handler return directly */ + /* For generators skip the leave handler and return directly */ if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); From 134089372b94de2e3e8c2a1aba4cbc415c803d67 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 22 Jul 2012 20:11:09 +0200 Subject: [PATCH 55/70] Throw error also for return occuring before yield Previously only an error was thrown when return occured after yield. Also returns before the first yield would fail for by-ref generators. Now the error message is handled in pass_two, so all returns are checked. --- .../generator_cannot_return_before_yield_error.phpt | 13 +++++++++++++ .../errors/generator_cannot_return_error.phpt | 2 +- Zend/zend_compile.c | 9 ++------- Zend/zend_opcode.c | 12 ++++++++++++ 4 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 Zend/tests/generators/errors/generator_cannot_return_before_yield_error.phpt diff --git a/Zend/tests/generators/errors/generator_cannot_return_before_yield_error.phpt b/Zend/tests/generators/errors/generator_cannot_return_before_yield_error.phpt new file mode 100644 index 000000000000..ad618d20ba2d --- /dev/null +++ b/Zend/tests/generators/errors/generator_cannot_return_before_yield_error.phpt @@ -0,0 +1,13 @@ +--TEST-- +Generators cannot return values (even before yield) +--FILE-- + +--EXPECTF-- +Fatal error: Generators cannot return values using "return" in %s on line 4 diff --git a/Zend/tests/generators/errors/generator_cannot_return_error.phpt b/Zend/tests/generators/errors/generator_cannot_return_error.phpt index 9a46bff5e666..51149062a7ed 100644 --- a/Zend/tests/generators/errors/generator_cannot_return_error.phpt +++ b/Zend/tests/generators/errors/generator_cannot_return_error.phpt @@ -10,4 +10,4 @@ function gen() { ?> --EXPECTF-- -Fatal error: Generators cannot return values using "return" in %s on line %d +Fatal error: Generators cannot return values using "return" in %s on line 5 diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index f0648a2d977f..483ff30ddc7f 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2611,14 +2611,9 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */ { zend_op *opline; int start_op_number, end_op_number; + zend_bool returns_reference = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0; - /* For generators the & modifier applies to the yielded values, not the - * return value. */ - zend_bool returns_reference = (CG(active_op_array)->fn_flags & ZEND_ACC_RETURN_REFERENCE) && !(CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR); - - if ((CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) && expr != NULL) { - zend_error(E_COMPILE_ERROR, "Generators cannot return values using \"return\""); - } + /* The error for use of return inside a generator is thrown in pass_two. */ if (do_end_vparse) { if (returns_reference && !zend_is_function_or_method_call(expr)) { diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 65fa85185ef8..0042c3729977 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -532,6 +532,18 @@ ZEND_API int pass_two(zend_op_array *op_array TSRMLS_DC) case ZEND_JMP_SET_VAR: opline->op2.jmp_addr = &op_array->opcodes[opline->op2.opline_num]; break; + case ZEND_RETURN: + case ZEND_RETURN_BY_REF: + if (op_array->fn_flags & ZEND_ACC_GENERATOR) { + if (opline->op1_type != IS_CONST || Z_TYPE_P(opline->op1.zv) != IS_NULL) { + CG(zend_lineno) = opline->lineno; + zend_error(E_COMPILE_ERROR, "Generators cannot return values using \"return\""); + } + if (opline->opcode == ZEND_RETURN_BY_REF) { + opline->opcode = ZEND_RETURN; + } + } + break; } ZEND_VM_SET_OPCODE_HANDLER(opline); opline++; From 99f93dd9a846e3d615ec61c734aca2e7ee256600 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 22 Jul 2012 20:19:07 +0200 Subject: [PATCH 56/70] Add T_YIELD in tokenizer_data.c Also had to fix up some tokenizer tests that were affected by the token number changes. --- .../tests/token_get_all_variation11.phpt | 4 ++-- .../tests/token_get_all_variation13.phpt | 2 +- .../tests/token_get_all_variation17.phpt | 2 +- .../tests/token_get_all_variation4.phpt | 4 ++-- .../tests/token_get_all_variation5.phpt | 22 +++++++++---------- .../tests/token_get_all_variation6.phpt | 4 ++-- .../tests/token_get_all_variation8.phpt | 2 +- ext/tokenizer/tokenizer_data.c | 2 ++ 8 files changed, 22 insertions(+), 20 deletions(-) diff --git a/ext/tokenizer/tests/token_get_all_variation11.phpt b/ext/tokenizer/tests/token_get_all_variation11.phpt index ecc86177a4df..98d89961b77b 100644 --- a/ext/tokenizer/tests/token_get_all_variation11.phpt +++ b/ext/tokenizer/tests/token_get_all_variation11.phpt @@ -130,7 +130,7 @@ array(49) { [6]=> array(3) { [0]=> - int(283) + int(%d) [1]=> string(2) "==" [2]=> @@ -273,7 +273,7 @@ array(49) { [27]=> array(3) { [0]=> - int(283) + int(%d) [1]=> string(2) "==" [2]=> diff --git a/ext/tokenizer/tests/token_get_all_variation13.phpt b/ext/tokenizer/tests/token_get_all_variation13.phpt index 9b2f3bc94fb6..6f85492a99a7 100644 --- a/ext/tokenizer/tests/token_get_all_variation13.phpt +++ b/ext/tokenizer/tests/token_get_all_variation13.phpt @@ -1005,7 +1005,7 @@ array(145) { [122]=> array(3) { [0]=> - int(288) + int(%d) [1]=> string(10) "instanceof" [2]=> diff --git a/ext/tokenizer/tests/token_get_all_variation17.phpt b/ext/tokenizer/tests/token_get_all_variation17.phpt index dccc4c9c23dd..f71444bc1e84 100644 --- a/ext/tokenizer/tests/token_get_all_variation17.phpt +++ b/ext/tokenizer/tests/token_get_all_variation17.phpt @@ -145,7 +145,7 @@ array(81) { [14]=> array(3) { [0]=> - int(283) + int(%d) [1]=> string(2) "==" [2]=> diff --git a/ext/tokenizer/tests/token_get_all_variation4.phpt b/ext/tokenizer/tests/token_get_all_variation4.phpt index 45e6f8afbdef..6bc111efbac2 100644 --- a/ext/tokenizer/tests/token_get_all_variation4.phpt +++ b/ext/tokenizer/tests/token_get_all_variation4.phpt @@ -339,7 +339,7 @@ array(89) { [38]=> array(3) { [0]=> - int(279) + int(%d) [1]=> string(2) "&&" [2]=> @@ -518,7 +518,7 @@ array(89) { [60]=> array(3) { [0]=> - int(278) + int(%d) [1]=> string(2) "||" [2]=> diff --git a/ext/tokenizer/tests/token_get_all_variation5.phpt b/ext/tokenizer/tests/token_get_all_variation5.phpt index 0068f2866fb3..681fb48e573f 100644 --- a/ext/tokenizer/tests/token_get_all_variation5.phpt +++ b/ext/tokenizer/tests/token_get_all_variation5.phpt @@ -181,7 +181,7 @@ array(94) { [18]=> array(3) { [0]=> - int(277) + int(%d) [1]=> string(2) "+=" [2]=> @@ -238,7 +238,7 @@ array(94) { [25]=> array(3) { [0]=> - int(276) + int(%d) [1]=> string(2) "-=" [2]=> @@ -295,7 +295,7 @@ array(94) { [32]=> array(3) { [0]=> - int(275) + int(%d) [1]=> string(2) "*=" [2]=> @@ -352,7 +352,7 @@ array(94) { [39]=> array(3) { [0]=> - int(274) + int(%d) [1]=> string(2) "/=" [2]=> @@ -409,7 +409,7 @@ array(94) { [46]=> array(3) { [0]=> - int(272) + int(%d) [1]=> string(2) "%=" [2]=> @@ -466,7 +466,7 @@ array(94) { [53]=> array(3) { [0]=> - int(271) + int(%d) [1]=> string(2) "&=" [2]=> @@ -523,7 +523,7 @@ array(94) { [60]=> array(3) { [0]=> - int(270) + int(%d) [1]=> string(2) "|=" [2]=> @@ -580,7 +580,7 @@ array(94) { [67]=> array(3) { [0]=> - int(269) + int(%d) [1]=> string(2) "^=" [2]=> @@ -637,7 +637,7 @@ array(94) { [74]=> array(3) { [0]=> - int(267) + int(%d) [1]=> string(3) ">>=" [2]=> @@ -694,7 +694,7 @@ array(94) { [81]=> array(3) { [0]=> - int(268) + int(%d) [1]=> string(3) "<<=" [2]=> @@ -751,7 +751,7 @@ array(94) { [88]=> array(3) { [0]=> - int(273) + int(%d) [1]=> string(2) ".=" [2]=> diff --git a/ext/tokenizer/tests/token_get_all_variation6.phpt b/ext/tokenizer/tests/token_get_all_variation6.phpt index 54936d0c89ac..6213dab9d052 100644 --- a/ext/tokenizer/tests/token_get_all_variation6.phpt +++ b/ext/tokenizer/tests/token_get_all_variation6.phpt @@ -191,7 +191,7 @@ array(50) { [21]=> array(3) { [0]=> - int(287) + int(%d) [1]=> string(2) "<<" [2]=> @@ -277,7 +277,7 @@ array(50) { [32]=> array(3) { [0]=> - int(286) + int(%d) [1]=> string(2) ">>" [2]=> diff --git a/ext/tokenizer/tests/token_get_all_variation8.phpt b/ext/tokenizer/tests/token_get_all_variation8.phpt index 0cf1d6347112..c80a5d0f04d5 100644 --- a/ext/tokenizer/tests/token_get_all_variation8.phpt +++ b/ext/tokenizer/tests/token_get_all_variation8.phpt @@ -794,7 +794,7 @@ array(108) { [103]=> array(3) { [0]=> - int(289) + int(%d) [1]=> string(7) "(unset)" [2]=> diff --git a/ext/tokenizer/tokenizer_data.c b/ext/tokenizer/tokenizer_data.c index 85822f199408..85fa343db1e8 100644 --- a/ext/tokenizer/tokenizer_data.c +++ b/ext/tokenizer/tokenizer_data.c @@ -108,6 +108,7 @@ void tokenizer_register_constants(INIT_FUNC_ARGS) { REGISTER_LONG_CONSTANT("T_FUNCTION", T_FUNCTION, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_CONST", T_CONST, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_RETURN", T_RETURN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("T_YIELD", T_YIELD, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_TRY", T_TRY, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_CATCH", T_CATCH, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("T_THROW", T_THROW, CONST_CS | CONST_PERSISTENT); @@ -242,6 +243,7 @@ char *get_token_type_name(int token_type) case T_FUNCTION: return "T_FUNCTION"; case T_CONST: return "T_CONST"; case T_RETURN: return "T_RETURN"; + case T_YIELD: return "T_YIELD"; case T_TRY: return "T_TRY"; case T_CATCH: return "T_CATCH"; case T_THROW: return "T_THROW"; From 268740d9848d435054ce73a8cfe36b2b732cd1f7 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 26 Jul 2012 17:07:24 +0200 Subject: [PATCH 57/70] Fix implementation of Iterator interface It looks like you have to implement the Iterator interface *before* assigning get_iterator. Otherwise the structure for user iterators isn't correctly zeroed out. Additionaly I'm setting class_entry->iterator_funcs.funcs now. Not sure if this is strictly necessary, but better safe than sorry ;) --- .../generator_in_multipleiterator.phpt | 37 +++++++++++++++++++ Zend/zend_generators.c | 4 +- 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/generators/generator_in_multipleiterator.phpt diff --git a/Zend/tests/generators/generator_in_multipleiterator.phpt b/Zend/tests/generators/generator_in_multipleiterator.phpt new file mode 100644 index 000000000000..611dbc965274 --- /dev/null +++ b/Zend/tests/generators/generator_in_multipleiterator.phpt @@ -0,0 +1,37 @@ +--TEST-- +Generators work properly in MultipleIterator +--FILE-- +attachIterator(gen1()); +$it->attachIterator(gen2()); + +foreach ($it as $values) { + var_dump($values); +} + +?> +--EXPECT-- +array(2) { + [0]=> + string(1) "a" + [1]=> + string(1) "b" +} +array(2) { + [0]=> + string(2) "aa" + [1]=> + string(2) "bb" +} diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index d7ffb3055a6a..716b0a782edd 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -759,9 +759,11 @@ void zend_register_generator_ce(TSRMLS_D) /* {{{ */ zend_ce_generator = zend_register_internal_class(&ce TSRMLS_CC); zend_ce_generator->ce_flags |= ZEND_ACC_FINAL_CLASS; zend_ce_generator->create_object = zend_generator_create; - zend_ce_generator->get_iterator = zend_generator_get_iterator; + /* get_iterator has to be assigned *after* implementing the inferface */ zend_class_implements(zend_ce_generator TSRMLS_CC, 1, zend_ce_iterator); + zend_ce_generator->get_iterator = zend_generator_get_iterator; + zend_ce_generator->iterator_funcs.funcs = &zend_generator_iterator_functions; memcpy(&zend_generator_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); zend_generator_handlers.get_constructor = zend_generator_get_constructor; From ae716939eb500f962336d37b96069cb7452c25df Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 13 Aug 2012 17:17:18 +0200 Subject: [PATCH 58/70] Support trivial finally in generators (no yield, no return) The finally clause is now properly run when an exception is thrown in the try-block. It is not yet run on `return` and also not run when the generator is claused within a try block. I'll add those two things as soon as laruence refactored the finally code. --- Zend/zend_vm_def.h | 24 ++++++++++++------------ Zend/zend_vm_execute.h | 24 ++++++++++++------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 83ae5c5ba43f..49ee3148e3d0 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2513,6 +2513,18 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY) zend_bool nested; zend_op_array *op_array = EX(op_array); + /* Generators go throw a different cleanup process */ + if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + + /* Close the generator to free up resources */ + zend_generator_close(generator, 1 TSRMLS_CC); + + /* Pass execution back to handling code */ + ZEND_VM_RETURN(); + } + EG(current_execute_data) = EX(prev_execute_data); EG(opline_ptr) = NULL; if (!EG(active_symbol_table)) { @@ -5213,18 +5225,6 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY) ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[finally_op_num]); ZEND_VM_CONTINUE(); } else { - /* For generators skip the leave handler and return directly */ - if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { - /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); - - /* Close the generator to free up resources */ - zend_generator_close(generator, 1 TSRMLS_CC); - - /* Pass execution back to handling code */ - ZEND_VM_RETURN(); - } - ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper); } } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 495b520ecb22..94c2a7cf166b 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -496,6 +496,18 @@ static int ZEND_FASTCALL zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS) zend_bool nested; zend_op_array *op_array = EX(op_array); + /* Generators go throw a different cleanup process */ + if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { + /* The generator object is stored in return_value_ptr_ptr */ + zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + + /* Close the generator to free up resources */ + zend_generator_close(generator, 1 TSRMLS_CC); + + /* Pass execution back to handling code */ + ZEND_VM_RETURN(); + } + EG(current_execute_data) = EX(prev_execute_data); EG(opline_ptr) = NULL; if (!EG(active_symbol_table)) { @@ -1177,18 +1189,6 @@ static int ZEND_FASTCALL ZEND_HANDLE_EXCEPTION_SPEC_HANDLER(ZEND_OPCODE_HANDLER ZEND_VM_SET_OPCODE(&EX(op_array)->opcodes[finally_op_num]); ZEND_VM_CONTINUE(); } else { - /* For generators skip the leave handler and return directly */ - if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { - /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); - - /* Close the generator to free up resources */ - zend_generator_close(generator, 1 TSRMLS_CC); - - /* Pass execution back to handling code */ - ZEND_VM_RETURN(); - } - return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } } From 7195a5b3768e519b8f50d131a8c7041a0b57959e Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 13 Aug 2012 19:24:44 +0200 Subject: [PATCH 59/70] Forgot to add test --- .../generators/finally_uninterrupted.phpt | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Zend/tests/generators/finally_uninterrupted.phpt diff --git a/Zend/tests/generators/finally_uninterrupted.phpt b/Zend/tests/generators/finally_uninterrupted.phpt new file mode 100644 index 000000000000..64c94382aa01 --- /dev/null +++ b/Zend/tests/generators/finally_uninterrupted.phpt @@ -0,0 +1,28 @@ +--TEST-- +Use of finally in generator without interrupt +--FILE-- +rewind(); // force run + +?> +--EXPECTF-- +finally run + +Fatal error: Uncaught exception 'Exception' in %s:%d +Stack trace: +#0 [internal function]: gen() +#1 %s(%d): Generator->rewind() +#2 {main} + thrown in %s on line %d From 05f10480c556ebe52bbef52cb2da5a0aca8ee070 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 20 Aug 2012 12:53:18 +0200 Subject: [PATCH 60/70] Drop Generator::close() method --- Zend/tests/generators/clone_with_foreach.phpt | 2 +- Zend/tests/generators/clone_with_stack.phpt | 2 +- .../generators/clone_with_symbol_table.phpt | 2 +- Zend/tests/generators/clone_with_this.phpt | 2 +- .../generators/close_inside_generator.phpt | 22 ------------- Zend/tests/generators/generator_close.phpt | 32 ------------------- .../generators/yield_during_method_call.phpt | 4 +-- Zend/zend_generators.c | 30 ----------------- Zend/zend_generators.h | 4 --- 9 files changed, 6 insertions(+), 94 deletions(-) delete mode 100644 Zend/tests/generators/close_inside_generator.phpt delete mode 100644 Zend/tests/generators/generator_close.phpt diff --git a/Zend/tests/generators/clone_with_foreach.phpt b/Zend/tests/generators/clone_with_foreach.phpt index b88733803656..b05ed073120d 100644 --- a/Zend/tests/generators/clone_with_foreach.phpt +++ b/Zend/tests/generators/clone_with_foreach.phpt @@ -20,7 +20,7 @@ $g2->next(); var_dump($g1->current()); var_dump($g2->current()); -$g1->close(); +unset($g1); $g2->next(); var_dump($g2->current()); diff --git a/Zend/tests/generators/clone_with_stack.phpt b/Zend/tests/generators/clone_with_stack.phpt index 673c0e5d1e7e..5a8e6d842ca8 100644 --- a/Zend/tests/generators/clone_with_stack.phpt +++ b/Zend/tests/generators/clone_with_stack.phpt @@ -10,7 +10,7 @@ function gen() { $g1 = gen(); $g1->rewind(); $g2 = clone $g1; -$g1->close(); +unset($g1); $g2->send(10); ?> diff --git a/Zend/tests/generators/clone_with_symbol_table.phpt b/Zend/tests/generators/clone_with_symbol_table.phpt index 0d1bd4ec3c42..e1fefebd8fa4 100644 --- a/Zend/tests/generators/clone_with_symbol_table.phpt +++ b/Zend/tests/generators/clone_with_symbol_table.phpt @@ -19,7 +19,7 @@ function gen() { $g1 = gen(); $g1->rewind(); $g2 = clone $g1; -$g1->close(); +unset($g1); $g2->next(); ?> diff --git a/Zend/tests/generators/clone_with_this.phpt b/Zend/tests/generators/clone_with_this.phpt index 66efd02987d7..b242d851ebec 100644 --- a/Zend/tests/generators/clone_with_this.phpt +++ b/Zend/tests/generators/clone_with_this.phpt @@ -16,7 +16,7 @@ class Test { $g1 = (new Test)->gen(); $g1->rewind(); // goto yield $g2 = clone $g1; -$g1->close(); +unset($g1); $g2->next(); ?> diff --git a/Zend/tests/generators/close_inside_generator.phpt b/Zend/tests/generators/close_inside_generator.phpt deleted file mode 100644 index 1df64bf6b1ab..000000000000 --- a/Zend/tests/generators/close_inside_generator.phpt +++ /dev/null @@ -1,22 +0,0 @@ ---TEST-- -Calling close() during the exectution of the generator ---FILE-- -close(); - - echo "Still running"; -} - -$gen = gen(); -$gen->send($gen); - -?> ---EXPECTF-- -Warning: A generator cannot be closed while it is running in %s on line %d -Still running diff --git a/Zend/tests/generators/generator_close.phpt b/Zend/tests/generators/generator_close.phpt deleted file mode 100644 index 3dec285409ff..000000000000 --- a/Zend/tests/generators/generator_close.phpt +++ /dev/null @@ -1,32 +0,0 @@ ---TEST-- -Generator can be closed by calling ->close() ---FILE-- -close(); - } -} - -?> ---EXPECT-- -int(0) -int(1) -int(2) -int(3) -int(4) -int(5) -int(6) -int(7) -int(8) -int(9) diff --git a/Zend/tests/generators/yield_during_method_call.phpt b/Zend/tests/generators/yield_during_method_call.phpt index e8859ac0c27e..5fbe84fff559 100644 --- a/Zend/tests/generators/yield_during_method_call.phpt +++ b/Zend/tests/generators/yield_during_method_call.phpt @@ -20,13 +20,13 @@ $gen->send('foo'); // test resource cleanup $gen = gen(); $gen->rewind(); -$gen->close(); +unset($gen); // test cloning $g1 = gen(); $g1->rewind(); $g2 = clone $g1; -$g1->close(); +unset($g1); $g2->send('bar'); ?> diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 716b0a782edd..41c6dfcd946b 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -301,8 +301,6 @@ static zend_object_value zend_generator_create(zend_class_entry *class_type TSRM /* The key will be incremented on first use, so it'll start at 0 */ generator->largest_used_integer_key = -1; - generator->is_currently_running = 0; - zend_object_std_init(&generator->std, class_type TSRMLS_CC); object.handle = zend_objects_store_put(generator, NULL, @@ -391,8 +389,6 @@ static void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */ zend_class_entry *original_scope = EG(scope); zend_class_entry *original_called_scope = EG(called_scope); - zend_bool original_is_currently_running = generator->is_currently_running; - /* Remember the current stack position so we can back up pushed args */ generator->original_stack_top = zend_vm_stack_top(TSRMLS_C); @@ -417,8 +413,6 @@ static void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */ EG(scope) = generator->execute_data->current_scope; EG(called_scope) = generator->execute_data->current_called_scope; - generator->is_currently_running = 1; - /* We want the backtrace to look as if the generator function was * called from whatever method we are current running (e.g. next()). * The first prev_execute_data contains an additional stack frame, @@ -440,8 +434,6 @@ static void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */ EG(scope) = original_scope; EG(called_scope) = original_called_scope; - generator->is_currently_running = original_is_currently_running; - /* The stack top before and after the execution differ, i.e. there are * arguments pushed to the stack. */ if (generator->original_stack_top != zend_vm_stack_top(TSRMLS_C)) { @@ -598,27 +590,6 @@ ZEND_METHOD(Generator, send) } } -/* {{{ proto void Generator::close() - * Closes the generator */ -ZEND_METHOD(Generator, close) -{ - zend_generator *generator; - - if (zend_parse_parameters_none() == FAILURE) { - return; - } - - generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); - - if (generator->is_currently_running) { - zend_error(E_WARNING, "A generator cannot be closed while it is running"); - return; - } - - zend_generator_close(generator, 0 TSRMLS_CC); -} -/* }}} */ - /* get_iterator implementation */ typedef struct _zend_generator_iterator { @@ -747,7 +718,6 @@ static const zend_function_entry generator_functions[] = { ZEND_ME(Generator, key, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_ME(Generator, next, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_ME(Generator, send, arginfo_generator_send, ZEND_ACC_PUBLIC) - ZEND_ME(Generator, close, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_FE_END }; diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index d67ea4137bf8..f58dafdb1f0c 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -46,10 +46,6 @@ typedef struct _zend_generator { temp_variable *send_target; /* Largest used integer key for auto-incrementing keys */ long largest_used_integer_key; - - /* We need to know whether the generator is currently executed to avoid it - * being closed while still running */ - zend_bool is_currently_running; } zend_generator; extern ZEND_API zend_class_entry *zend_ce_generator; From 9003cd142553384c3d271b12407186d5352868ad Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 20 Aug 2012 13:28:36 +0200 Subject: [PATCH 61/70] Fix zts build (typo) --- Zend/zend_generators.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index f58dafdb1f0c..f5f4926e353f 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -51,7 +51,7 @@ typedef struct _zend_generator { extern ZEND_API zend_class_entry *zend_ce_generator; void zend_register_generator_ce(TSRMLS_D); -zval *zend_generator_create_zval(zend_op_array *op_array TSRMLS_CC); +zval *zend_generator_create_zval(zend_op_array *op_array TSRMLS_DC); void zend_generator_close(zend_generator *generator, zend_bool finished_execution TSRMLS_DC); END_EXTERN_C() From f45a0f31c8354947c0e2b9ea44a63fc0a2c23a01 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 20 Aug 2012 16:01:16 +0200 Subject: [PATCH 62/70] Disallow serialization and unserialization --- .../errors/serialize_unserialize_error.phpt | 46 +++++++++++++++++++ Zend/zend_generators.c | 32 ++++++++++--- 2 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 Zend/tests/generators/errors/serialize_unserialize_error.phpt diff --git a/Zend/tests/generators/errors/serialize_unserialize_error.phpt b/Zend/tests/generators/errors/serialize_unserialize_error.phpt new file mode 100644 index 000000000000..a8470b0a63ab --- /dev/null +++ b/Zend/tests/generators/errors/serialize_unserialize_error.phpt @@ -0,0 +1,46 @@ +--TEST-- +Generators can't be serialized or unserialized +--FILE-- + +--EXPECTF-- +exception 'Exception' with message 'Serialization of 'Generator' is not allowed' in %s:%d +Stack trace: +#0 %s(%d): serialize(Object(Generator)) +#1 {main} + +exception 'Exception' with message 'Unserialization of 'Generator' is not allowed' in %s:%d +Stack trace: +#0 [internal function]: Generator->__wakeup() +#1 %s(%d): unserialize('O:9:"Generator"...') +#2 {main} + + +Notice: unserialize(): Error at offset 19 of 20 bytes in %s on line %d +exception 'Exception' with message 'Unserialization of 'Generator' is not allowed' in %s:%d +Stack trace: +#0 %s(%d): unserialize('C:9:"Generator"...') +#1 {main} diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 41c6dfcd946b..b4d8932b6bbf 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -590,6 +590,23 @@ ZEND_METHOD(Generator, send) } } + +/* {{{ proto void Generator::__wakeup + * Throws an Exception as generators can't be serialized */ +ZEND_METHOD(Generator, __wakeup) +{ + /* Just specifying the zend_class_unserialize_deny handler is not enough, + * because it is only invoked for C unserialization. For O the error has + * to be thrown in __wakeup. */ + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + zend_throw_exception(NULL, "Unserialization of 'Generator' is not allowed", 0 TSRMLS_CC); +} +/* }}} */ + /* get_iterator implementation */ typedef struct _zend_generator_iterator { @@ -712,12 +729,13 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_generator_send, 0, 0, 1) ZEND_END_ARG_INFO() static const zend_function_entry generator_functions[] = { - ZEND_ME(Generator, rewind, arginfo_generator_void, ZEND_ACC_PUBLIC) - ZEND_ME(Generator, valid, arginfo_generator_void, ZEND_ACC_PUBLIC) - ZEND_ME(Generator, current, arginfo_generator_void, ZEND_ACC_PUBLIC) - ZEND_ME(Generator, key, arginfo_generator_void, ZEND_ACC_PUBLIC) - ZEND_ME(Generator, next, arginfo_generator_void, ZEND_ACC_PUBLIC) - ZEND_ME(Generator, send, arginfo_generator_send, ZEND_ACC_PUBLIC) + ZEND_ME(Generator, rewind, arginfo_generator_void, ZEND_ACC_PUBLIC) + ZEND_ME(Generator, valid, arginfo_generator_void, ZEND_ACC_PUBLIC) + ZEND_ME(Generator, current, arginfo_generator_void, ZEND_ACC_PUBLIC) + ZEND_ME(Generator, key, arginfo_generator_void, ZEND_ACC_PUBLIC) + ZEND_ME(Generator, next, arginfo_generator_void, ZEND_ACC_PUBLIC) + ZEND_ME(Generator, send, arginfo_generator_send, ZEND_ACC_PUBLIC) + ZEND_ME(Generator, __wakeup, arginfo_generator_void, ZEND_ACC_PUBLIC) ZEND_FE_END }; @@ -729,6 +747,8 @@ void zend_register_generator_ce(TSRMLS_D) /* {{{ */ zend_ce_generator = zend_register_internal_class(&ce TSRMLS_CC); zend_ce_generator->ce_flags |= ZEND_ACC_FINAL_CLASS; zend_ce_generator->create_object = zend_generator_create; + zend_ce_generator->serialize = zend_class_serialize_deny; + zend_ce_generator->unserialize = zend_class_unserialize_deny; /* get_iterator has to be assigned *after* implementing the inferface */ zend_class_implements(zend_ce_generator TSRMLS_CC, 1, zend_ce_iterator); From 68c1e1cfe95b026086cacf40a005ea8f399e9595 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 24 Aug 2012 13:51:39 +0200 Subject: [PATCH 63/70] Add dedicated opcode for returns from a generator Generators don't have a return value, so it doesn't make sense to have a shared implementation here. --- Zend/zend_opcode.c | 5 ++- Zend/zend_vm_def.h | 19 +++++------ Zend/zend_vm_execute.h | 77 ++++++++++++++++++------------------------ Zend/zend_vm_opcodes.h | 1 + 4 files changed, 44 insertions(+), 58 deletions(-) diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 0f39b8a41d81..5c4b20fd3482 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -586,9 +586,8 @@ ZEND_API int pass_two(zend_op_array *op_array TSRMLS_DC) CG(zend_lineno) = opline->lineno; zend_error(E_COMPILE_ERROR, "Generators cannot return values using \"return\""); } - if (opline->opcode == ZEND_RETURN_BY_REF) { - opline->opcode = ZEND_RETURN; - } + + opline->opcode = ZEND_GENERATOR_RETURN; } break; } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 975a2a70717a..216cd59bdbc4 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -2931,17 +2931,6 @@ ZEND_VM_HANDLER(62, ZEND_RETURN, CONST|TMP|VAR|CV, ANY) zval *retval_ptr; zend_free_op free_op1; - if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { - /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); - - /* Close the generator to free up resources */ - zend_generator_close(generator, 1 TSRMLS_CC); - - /* Pass execution back to handling code */ - ZEND_VM_RETURN(); - } - SAVE_OPLINE(); retval_ptr = GET_OP1_ZVAL_PTR(BP_VAR_R); @@ -3066,6 +3055,14 @@ ZEND_VM_HANDLER(111, ZEND_RETURN_BY_REF, CONST|TMP|VAR|CV, ANY) ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper); } +ZEND_VM_HANDLER(162, ZEND_GENERATOR_RETURN, ANY, ANY) +{ + if (EX(op_array)->has_finally_block) { + ZEND_VM_DISPATCH_TO_HELPER_EX(zend_finally_handler_leaving, type, ZEND_RETURN); + } + ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper); +} + ZEND_VM_HANDLER(108, ZEND_THROW, CONST|TMP|VAR|CV, ANY) { USE_OPLINE diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 402442f23a6b..ebc0fb9c49cc 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -885,6 +885,14 @@ static int ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER(ZEND_OPCODE_HANDLER return zend_do_fcall_common_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } +static int ZEND_FASTCALL ZEND_GENERATOR_RETURN_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) +{ + if (EX(op_array)->has_finally_block) { + return zend_finally_handler_leaving_SPEC(ZEND_RETURN, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); + } + return zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); +} + static int ZEND_FASTCALL ZEND_RECV_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -2443,17 +2451,6 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARG zval *retval_ptr; - if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { - /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); - - /* Close the generator to free up resources */ - zend_generator_close(generator, 1 TSRMLS_CC); - - /* Pass execution back to handling code */ - ZEND_VM_RETURN(); - } - SAVE_OPLINE(); retval_ptr = opline->op1.zv; @@ -7760,17 +7757,6 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) zval *retval_ptr; zend_free_op free_op1; - if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { - /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); - - /* Close the generator to free up resources */ - zend_generator_close(generator, 1 TSRMLS_CC); - - /* Pass execution back to handling code */ - ZEND_VM_RETURN(); - } - SAVE_OPLINE(); retval_ptr = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); @@ -12982,17 +12968,6 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) zval *retval_ptr; zend_free_op free_op1; - if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { - /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); - - /* Close the generator to free up resources */ - zend_generator_close(generator, 1 TSRMLS_CC); - - /* Pass execution back to handling code */ - ZEND_VM_RETURN(); - } - SAVE_OPLINE(); retval_ptr = _get_zval_ptr_var(opline->op1.var, EX_Ts(), &free_op1 TSRMLS_CC); @@ -30530,17 +30505,6 @@ static int ZEND_FASTCALL ZEND_RETURN_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) zval *retval_ptr; - if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) { - /* The generator object is stored in return_value_ptr_ptr */ - zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); - - /* Close the generator to free up resources */ - zend_generator_close(generator, 1 TSRMLS_CC); - - /* Pass execution back to handling code */ - ZEND_VM_RETURN(); - } - SAVE_OPLINE(); retval_ptr = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), opline->op1.var TSRMLS_CC); @@ -44986,6 +44950,31 @@ void zend_init_opcodes_handlers(void) ZEND_DELEGATE_YIELD_SPEC_CV_HANDLER, ZEND_DELEGATE_YIELD_SPEC_CV_HANDLER, ZEND_DELEGATE_YIELD_SPEC_CV_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, + ZEND_GENERATOR_RETURN_SPEC_HANDLER, ZEND_NULL_HANDLER }; zend_opcode_handlers = (opcode_handler_t*)labels; diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 0a9cf008e3ef..0b4903ac3651 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -162,3 +162,4 @@ #define ZEND_LEAVE 159 #define ZEND_YIELD 160 #define ZEND_DELEGATE_YIELD 161 +#define ZEND_GENERATOR_RETURN 162 From 7cdf6367a51a54fce8676aeb6fd32bf91b00f84b Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 24 Aug 2012 13:52:16 +0200 Subject: [PATCH 64/70] Finally with return now works in generators too --- .../tests/generators/finally_with_return.phpt | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 Zend/tests/generators/finally_with_return.phpt diff --git a/Zend/tests/generators/finally_with_return.phpt b/Zend/tests/generators/finally_with_return.phpt new file mode 100644 index 000000000000..b26a49f32fa3 --- /dev/null +++ b/Zend/tests/generators/finally_with_return.phpt @@ -0,0 +1,33 @@ +--TEST-- +Use of finally in generator with return +--FILE-- +rewind(); // force run + +?> +--EXPECTF-- +before return +before return in inner finally +outer finally run From 4d8edda341efef1901365f10213c027e745ac7ab Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 24 Aug 2012 15:50:53 +0200 Subject: [PATCH 65/70] Run finally if generator is closed before finishing --- .../generators/finally_ran_on_close.phpt | 25 +++++++++++++ Zend/zend_generators.c | 35 ++++++++++++++++++- Zend/zend_generators.h | 1 + 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/generators/finally_ran_on_close.phpt diff --git a/Zend/tests/generators/finally_ran_on_close.phpt b/Zend/tests/generators/finally_ran_on_close.phpt new file mode 100644 index 000000000000..44a84fae5c79 --- /dev/null +++ b/Zend/tests/generators/finally_ran_on_close.phpt @@ -0,0 +1,25 @@ +--TEST-- +finally is run even if a generator is closed mid-execution +--FILE-- +rewind(); +unset($gen); + +?> +--EXPECT-- +before yield +finally run diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index b4d8932b6bbf..3170ec9c33d6 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -32,6 +32,39 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio if (generator->execute_data) { zend_execute_data *execute_data = generator->execute_data; + if (!finished_execution) { + zend_op_array *op_array = execute_data->op_array; + if (op_array->has_finally_block) { + zend_uint op_num = execute_data->opline - op_array->opcodes; + zend_uint finally_op_num = 0; + + /* Find next finally block */ + int i; + for (i = 0; i < op_array->last_try_catch; i++) { + zend_try_catch_element *try_catch = &op_array->try_catch_array[i]; + + if (op_num < try_catch->try_op) { + break; + } + + if (op_num < try_catch->finally_op) { + finally_op_num = try_catch->finally_op; + } + } + + /* If a finally block was found we jump directly to it and + * resume the generator. Furthermore we abort this close call + * because the generator will already be closed somewhere in + * the resume. */ + if (finally_op_num) { + execute_data->opline = &op_array->opcodes[finally_op_num]; + execute_data->leaving = ZEND_RETURN; + zend_generator_resume(generator TSRMLS_CC); + return; + } + } + } + if (!execute_data->symbol_table) { zend_free_compiled_variables(execute_data->CVs, execute_data->op_array->last_var); } else { @@ -371,7 +404,7 @@ static zend_function *zend_generator_get_constructor(zval *object TSRMLS_DC) /* } /* }}} */ -static void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */ +void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */ { /* The generator is already closed, thus can't resume */ if (!generator->execute_data) { diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index f5f4926e353f..37ffbbd6ccbb 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -53,6 +53,7 @@ extern ZEND_API zend_class_entry *zend_ce_generator; void zend_register_generator_ce(TSRMLS_D); zval *zend_generator_create_zval(zend_op_array *op_array TSRMLS_DC); void zend_generator_close(zend_generator *generator, zend_bool finished_execution TSRMLS_DC); +void zend_generator_resume(zend_generator *generator TSRMLS_DC); END_EXTERN_C() From f53225a99ebae56c7a20d6e3ad4efe6772dda3f9 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 25 Aug 2012 17:40:08 +0200 Subject: [PATCH 66/70] Fix several issues and allow rewind only at/before first yield * Trying to resume a generator while it is already running now throws a fatal error. * Trying to use yield in finally while the generator is being force-closed (by GC) throws a fatal error. * Rewinding after the first yield now throws an Exception --- .../resume_running_generator_error.phpt | 17 +++ .../yield_in_force_closed_finally_error.phpt | 29 +++++ Zend/tests/generators/generator_rewind.phpt | 43 ++++++++ Zend/tests/generators/yield_in_finally.phpt | 29 +++++ Zend/zend_generators.c | 49 +++++++-- Zend/zend_generators.h | 11 +- Zend/zend_vm_def.h | 4 + Zend/zend_vm_execute.h | 100 ++++++++++++++++++ 8 files changed, 269 insertions(+), 13 deletions(-) create mode 100644 Zend/tests/generators/errors/resume_running_generator_error.phpt create mode 100644 Zend/tests/generators/errors/yield_in_force_closed_finally_error.phpt create mode 100644 Zend/tests/generators/generator_rewind.phpt create mode 100644 Zend/tests/generators/yield_in_finally.phpt diff --git a/Zend/tests/generators/errors/resume_running_generator_error.phpt b/Zend/tests/generators/errors/resume_running_generator_error.phpt new file mode 100644 index 000000000000..567d72f3f948 --- /dev/null +++ b/Zend/tests/generators/errors/resume_running_generator_error.phpt @@ -0,0 +1,17 @@ +--TEST-- +It is not possible to resume an already running generator +--FILE-- +next(); +} + +$gen = gen(); +$gen->send($gen); +$gen->next(); + +?> +--EXPECTF-- +Fatal error: Cannot resume an already running generator in %s on line %d diff --git a/Zend/tests/generators/errors/yield_in_force_closed_finally_error.phpt b/Zend/tests/generators/errors/yield_in_force_closed_finally_error.phpt new file mode 100644 index 000000000000..aada676a68e8 --- /dev/null +++ b/Zend/tests/generators/errors/yield_in_force_closed_finally_error.phpt @@ -0,0 +1,29 @@ +--TEST-- +yield cannot be used in a finally block when the generator is force-closed +--FILE-- +rewind(); +unset($gen); + +?> +--EXPECTF-- +before yield +before yield in finally + +Fatal error: Cannot yield from finally in a force-closed generator in %s on line %d diff --git a/Zend/tests/generators/generator_rewind.phpt b/Zend/tests/generators/generator_rewind.phpt new file mode 100644 index 000000000000..3224f6a9b6d8 --- /dev/null +++ b/Zend/tests/generators/generator_rewind.phpt @@ -0,0 +1,43 @@ +--TEST-- +A generator can only be rewinded before or at the first yield +--FILE-- +rewind(); +$gen->rewind(); +$gen->next(); + +try { + $gen->rewind(); +} catch (Exception $e) { + echo "\n", $e, "\n\n"; +} + +function gen2() { + echo "in generator\n"; + + if (false) yield; +} + +$gen = gen2(); +$gen->rewind(); + +?> +--EXPECTF-- +before yield +after yield + +exception 'Exception' with message 'Cannot rewind a generator that was already run' in %s:%d +Stack trace: +#0 %s(%d): Generator->rewind() +#1 {main} + +in generator diff --git a/Zend/tests/generators/yield_in_finally.phpt b/Zend/tests/generators/yield_in_finally.phpt new file mode 100644 index 000000000000..805484ad1d15 --- /dev/null +++ b/Zend/tests/generators/yield_in_finally.phpt @@ -0,0 +1,29 @@ +--TEST-- +yield can be used in finally (apart from forced closes) +--FILE-- +current()); +$gen->next(); + +?> +--EXPECTF-- +before return +before yield +string(%d) "yielded value" +after yield diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 3170ec9c33d6..03294f7f0ea4 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -31,11 +31,13 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio { if (generator->execute_data) { zend_execute_data *execute_data = generator->execute_data; + zend_op_array *op_array = execute_data->op_array; if (!finished_execution) { - zend_op_array *op_array = execute_data->op_array; if (op_array->has_finally_block) { - zend_uint op_num = execute_data->opline - op_array->opcodes; + /* -1 required because we want the last run opcode, not the + * next to-be-run one. */ + zend_uint op_num = execute_data->opline - op_array->opcodes - 1; zend_uint finally_op_num = 0; /* Find next finally block */ @@ -59,6 +61,7 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio if (finally_op_num) { execute_data->opline = &op_array->opcodes[finally_op_num]; execute_data->leaving = ZEND_RETURN; + generator->flags |= ZEND_GENERATOR_FORCED_CLOSE; zend_generator_resume(generator TSRMLS_CC); return; } @@ -66,7 +69,7 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio } if (!execute_data->symbol_table) { - zend_free_compiled_variables(execute_data->CVs, execute_data->op_array->last_var); + zend_free_compiled_variables(execute_data->CVs, op_array->last_var); } else { zend_clean_and_cache_symbol_table(execute_data->symbol_table TSRMLS_CC); } @@ -83,8 +86,9 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio * a return statement) we have to free loop variables manually, as * we don't know whether the SWITCH_FREE / FREE opcodes have run */ if (!finished_execution) { - zend_op_array *op_array = execute_data->op_array; - zend_uint op_num = execute_data->opline - op_array->opcodes; + /* -1 required because we want the last run opcode, not the + * next to-be-run one. */ + zend_uint op_num = execute_data->opline - op_array->opcodes - 1; int i; for (i = 0; i < op_array->last_brk_cont; ++i) { @@ -411,6 +415,13 @@ void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */ return; } + if (generator->flags & ZEND_GENERATOR_CURRENTLY_RUNNING) { + zend_error(E_ERROR, "Cannot resume an already running generator"); + } + + /* Drop the AT_FIRST_YIELD flag */ + generator->flags &= ~ZEND_GENERATOR_AT_FIRST_YIELD; + { /* Backup executor globals */ zval **original_return_value_ptr_ptr = EG(return_value_ptr_ptr); @@ -455,7 +466,9 @@ void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */ generator->execute_data->prev_execute_data->prev_execute_data = original_execute_data; /* Resume execution */ + generator->flags |= ZEND_GENERATOR_CURRENTLY_RUNNING; execute_ex(generator->execute_data TSRMLS_CC); + generator->flags &= ~ZEND_GENERATOR_CURRENTLY_RUNNING; /* Restore executor globals */ EG(return_value_ptr_ptr) = original_return_value_ptr_ptr; @@ -489,6 +502,17 @@ static void zend_generator_ensure_initialized(zend_generator *generator TSRMLS_D { if (!generator->value) { zend_generator_resume(generator TSRMLS_CC); + generator->flags |= ZEND_GENERATOR_AT_FIRST_YIELD; + } +} +/* }}} */ + +static void zend_generator_rewind(zend_generator *generator TSRMLS_DC) /* {{{ */ +{ + zend_generator_ensure_initialized(generator TSRMLS_CC); + + if (!(generator->flags & ZEND_GENERATOR_AT_FIRST_YIELD)) { + zend_throw_exception(NULL, "Cannot rewind a generator that was already run", 0 TSRMLS_CC); } } /* }}} */ @@ -505,10 +529,7 @@ ZEND_METHOD(Generator, rewind) generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC); - zend_generator_ensure_initialized(generator TSRMLS_CC); - - /* Generators aren't rewindable, so rewind() only has to make sure that - * the generator is initialized, nothing more */ + zend_generator_rewind(generator TSRMLS_CC); } /* }}} */ @@ -721,13 +742,21 @@ static void zend_generator_iterator_move_forward(zend_object_iterator *iterator } /* }}} */ +static void zend_generator_iterator_rewind(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */ +{ + zend_generator *generator = (zend_generator *) iterator->data; + + zend_generator_rewind(generator TSRMLS_CC); +} +/* }}} */ + static zend_object_iterator_funcs zend_generator_iterator_functions = { zend_generator_iterator_dtor, zend_generator_iterator_valid, zend_generator_iterator_get_data, zend_generator_iterator_get_key, zend_generator_iterator_move_forward, - NULL + zend_generator_iterator_rewind }; zend_object_iterator *zend_generator_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC) /* {{{ */ diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h index 37ffbbd6ccbb..e47b7ad885f8 100644 --- a/Zend/zend_generators.h +++ b/Zend/zend_generators.h @@ -22,6 +22,8 @@ #define ZEND_GENERATORS_H BEGIN_EXTERN_C() +extern ZEND_API zend_class_entry *zend_ce_generator; +END_EXTERN_C() typedef struct _zend_generator { zend_object std; @@ -46,17 +48,20 @@ typedef struct _zend_generator { temp_variable *send_target; /* Largest used integer key for auto-incrementing keys */ long largest_used_integer_key; + + /* ZEND_GENERATOR_* flags */ + zend_uchar flags; } zend_generator; -extern ZEND_API zend_class_entry *zend_ce_generator; +static const zend_uchar ZEND_GENERATOR_CURRENTLY_RUNNING = 0x1; +static const zend_uchar ZEND_GENERATOR_FORCED_CLOSE = 0x2; +static const zend_uchar ZEND_GENERATOR_AT_FIRST_YIELD = 0x4; void zend_register_generator_ce(TSRMLS_D); zval *zend_generator_create_zval(zend_op_array *op_array TSRMLS_DC); void zend_generator_close(zend_generator *generator, zend_bool finished_execution TSRMLS_DC); void zend_generator_resume(zend_generator *generator TSRMLS_DC); -END_EXTERN_C() - #endif /* diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 216cd59bdbc4..19031bc18dd5 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5402,6 +5402,10 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index ebc0fb9c49cc..6a5e2fff1202 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -4209,6 +4209,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLE /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -4899,6 +4903,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_TMP_HANDLER(ZEND_OPCODE_HANDLER_ /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -5914,6 +5922,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_VAR_HANDLER(ZEND_OPCODE_HANDLER_ /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -6624,6 +6636,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_UNUSED_HANDLER(ZEND_OPCODE_HANDL /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -7373,6 +7389,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_A /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -9436,6 +9456,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -10126,6 +10150,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_TMP_HANDLER(ZEND_OPCODE_HANDLER_AR /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -11141,6 +11169,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_VAR_HANDLER(ZEND_OPCODE_HANDLER_AR /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -11717,6 +11749,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_UNUSED_HANDLER(ZEND_OPCODE_HANDLER /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -12404,6 +12440,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_TMP_CV_HANDLER(ZEND_OPCODE_HANDLER_ARG /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -16326,6 +16366,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -18405,6 +18449,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_TMP_HANDLER(ZEND_OPCODE_HANDLER_AR /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -20864,6 +20912,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_VAR_HANDLER(ZEND_OPCODE_HANDLER_AR /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -22001,6 +22053,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_UNUSED_HANDLER(ZEND_OPCODE_HANDLER /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -24129,6 +24185,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_VAR_CV_HANDLER(ZEND_OPCODE_HANDLER_ARG /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -25616,6 +25676,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDL /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -26925,6 +26989,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_TMP_HANDLER(ZEND_OPCODE_HANDLER /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -28234,6 +28302,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_VAR_HANDLER(ZEND_OPCODE_HANDLER /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -28654,6 +28726,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_UNUSED_HANDLER(ZEND_OPCODE_HAND /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -29960,6 +30036,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_UNUSED_CV_HANDLER(ZEND_OPCODE_HANDLER_ /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -33481,6 +33561,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_A /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -35429,6 +35513,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARG /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -37756,6 +37844,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -38752,6 +38844,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); @@ -40748,6 +40844,10 @@ static int ZEND_FASTCALL ZEND_YIELD_SPEC_CV_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS /* The generator object is stored in return_value_ptr_ptr */ zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr); + if (generator->flags & ZEND_GENERATOR_FORCED_CLOSE) { + zend_error_noreturn(E_ERROR, "Cannot yield from finally in a force-closed generator"); + } + /* Destroy the previously yielded value */ if (generator->value) { zval_ptr_dtor(&generator->value); From bd70d155885fdc087afba912c1b290615b864e2f Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 25 Aug 2012 19:03:23 +0200 Subject: [PATCH 67/70] Remove implementation stubs for yield delegation I decided to leave out yield delegation for an initial proposal, so remove the stubs for it too. --- Zend/zend_compile.c | 23 ------------------- Zend/zend_compile.h | 1 - Zend/zend_language_parser.y | 3 --- Zend/zend_vm_def.h | 7 +----- Zend/zend_vm_execute.h | 45 ------------------------------------- Zend/zend_vm_opcodes.h | 3 +-- 6 files changed, 2 insertions(+), 80 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 563069118e43..d4201059b2e7 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2702,29 +2702,6 @@ void zend_do_yield(znode *result, znode *value, const znode *key, zend_bool is_v } /* }}} */ -void zend_do_delegate_yield(znode *result, const znode *value TSRMLS_DC) /* {{{ */ -{ - zend_op *opline; - - if (!CG(active_op_array)->function_name) { - zend_error(E_COMPILE_ERROR, "The \"yield*\" expression can only be used inside a function"); - } - - CG(active_op_array)->fn_flags |= ZEND_ACC_GENERATOR; - - opline = get_next_op(CG(active_op_array) TSRMLS_CC); - - opline->opcode = ZEND_DELEGATE_YIELD; - - SET_NODE(opline->op1, value); - SET_UNUSED(opline->op2); - - opline->result_type = IS_VAR; - opline->result.var = get_temporary_variable(CG(active_op_array)); - GET_NODE(result, opline->result); -} -/* }}} */ - static int zend_add_try_element(zend_uint try_op TSRMLS_DC) /* {{{ */ { int try_catch_offset = CG(active_op_array)->last_try_catch++; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index acff761f88a0..42f3ed405d47 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -496,7 +496,6 @@ int zend_do_begin_class_member_function_call(znode *class_name, znode *method_na void zend_do_end_function_call(znode *function_name, znode *result, const znode *argument_list, int is_method, int is_dynamic_fcall TSRMLS_DC); void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC); void zend_do_yield(znode *result, znode *value, const znode *key, zend_bool is_variable TSRMLS_DC); -void zend_do_delegate_yield(znode *result, const znode *value TSRMLS_DC); void zend_do_handle_exception(TSRMLS_D); void zend_do_begin_lambda_function_declaration(znode *result, znode *function_token, int return_reference, int is_static TSRMLS_DC); diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 29012b769ba7..8b0f500895bc 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -807,9 +807,6 @@ expr_without_variable: | '`' backticks_expr '`' { zend_do_shell_exec(&$$, &$2 TSRMLS_CC); } | T_PRINT expr { zend_do_print(&$$, &$2 TSRMLS_CC); } | T_YIELD { zend_do_yield(&$$, NULL, NULL, 0 TSRMLS_CC); } - /*| T_YIELD expr_without_variable { zend_do_yield(&$$, &$2, NULL, 0 TSRMLS_CC); } - | T_YIELD variable { zend_do_yield(&$$, &$2, NULL, 1 TSRMLS_CC); }*/ - | T_YIELD '*' expr { zend_do_delegate_yield(&$$, &$3 TSRMLS_CC); } | function is_reference { zend_do_begin_lambda_function_declaration(&$$, &$1, $2.op_type, 0 TSRMLS_CC); } '(' parameter_list ')' lexical_vars '{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); $$ = $3; } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 19031bc18dd5..101667957be9 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -3055,7 +3055,7 @@ ZEND_VM_HANDLER(111, ZEND_RETURN_BY_REF, CONST|TMP|VAR|CV, ANY) ZEND_VM_DISPATCH_TO_HELPER(zend_leave_helper); } -ZEND_VM_HANDLER(162, ZEND_GENERATOR_RETURN, ANY, ANY) +ZEND_VM_HANDLER(161, ZEND_GENERATOR_RETURN, ANY, ANY) { if (EX(op_array)->has_finally_block) { ZEND_VM_DISPATCH_TO_HELPER_EX(zend_finally_handler_leaving, type, ZEND_RETURN); @@ -5552,9 +5552,4 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSE ZEND_VM_RETURN(); } -ZEND_VM_HANDLER(161, ZEND_DELEGATE_YIELD, CONST|TMP|VAR|CV, ANY) -{ - ZEND_VM_NEXT_OPCODE(); -} - ZEND_VM_EXPORT_HELPER(zend_do_fcall, zend_do_fcall_common_helper) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 6a5e2fff1202..a6439674d5c2 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3177,11 +3177,6 @@ static int ZEND_FASTCALL ZEND_QM_ASSIGN_VAR_SPEC_CONST_HANDLER(ZEND_OPCODE_HAND ZEND_VM_NEXT_OPCODE(); } -static int ZEND_FASTCALL ZEND_DELEGATE_YIELD_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - ZEND_VM_NEXT_OPCODE(); -} - static int ZEND_FASTCALL ZEND_ADD_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -8556,11 +8551,6 @@ static int ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_A ZEND_VM_NEXT_OPCODE(); } -static int ZEND_FASTCALL ZEND_DELEGATE_YIELD_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - ZEND_VM_NEXT_OPCODE(); -} - static int ZEND_FASTCALL ZEND_ADD_SPEC_TMP_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -14014,11 +14004,6 @@ static int ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_A ZEND_VM_NEXT_OPCODE(); } -static int ZEND_FASTCALL ZEND_DELEGATE_YIELD_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - ZEND_VM_NEXT_OPCODE(); -} - static int ZEND_FASTCALL ZEND_ADD_SPEC_VAR_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -31430,11 +31415,6 @@ static int ZEND_FASTCALL ZEND_INSTANCEOF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_AR ZEND_VM_NEXT_OPCODE(); } -static int ZEND_FASTCALL ZEND_DELEGATE_YIELD_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) -{ - ZEND_VM_NEXT_OPCODE(); -} - static int ZEND_FASTCALL ZEND_ADD_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE @@ -45025,31 +45005,6 @@ void zend_init_opcodes_handlers(void) ZEND_YIELD_SPEC_CV_VAR_HANDLER, ZEND_YIELD_SPEC_CV_UNUSED_HANDLER, ZEND_YIELD_SPEC_CV_CV_HANDLER, - ZEND_DELEGATE_YIELD_SPEC_CONST_HANDLER, - ZEND_DELEGATE_YIELD_SPEC_CONST_HANDLER, - ZEND_DELEGATE_YIELD_SPEC_CONST_HANDLER, - ZEND_DELEGATE_YIELD_SPEC_CONST_HANDLER, - ZEND_DELEGATE_YIELD_SPEC_CONST_HANDLER, - ZEND_DELEGATE_YIELD_SPEC_TMP_HANDLER, - ZEND_DELEGATE_YIELD_SPEC_TMP_HANDLER, - ZEND_DELEGATE_YIELD_SPEC_TMP_HANDLER, - ZEND_DELEGATE_YIELD_SPEC_TMP_HANDLER, - ZEND_DELEGATE_YIELD_SPEC_TMP_HANDLER, - ZEND_DELEGATE_YIELD_SPEC_VAR_HANDLER, - ZEND_DELEGATE_YIELD_SPEC_VAR_HANDLER, - ZEND_DELEGATE_YIELD_SPEC_VAR_HANDLER, - ZEND_DELEGATE_YIELD_SPEC_VAR_HANDLER, - ZEND_DELEGATE_YIELD_SPEC_VAR_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_NULL_HANDLER, - ZEND_DELEGATE_YIELD_SPEC_CV_HANDLER, - ZEND_DELEGATE_YIELD_SPEC_CV_HANDLER, - ZEND_DELEGATE_YIELD_SPEC_CV_HANDLER, - ZEND_DELEGATE_YIELD_SPEC_CV_HANDLER, - ZEND_DELEGATE_YIELD_SPEC_CV_HANDLER, ZEND_GENERATOR_RETURN_SPEC_HANDLER, ZEND_GENERATOR_RETURN_SPEC_HANDLER, ZEND_GENERATOR_RETURN_SPEC_HANDLER, diff --git a/Zend/zend_vm_opcodes.h b/Zend/zend_vm_opcodes.h index 0b4903ac3651..7f9434995483 100644 --- a/Zend/zend_vm_opcodes.h +++ b/Zend/zend_vm_opcodes.h @@ -161,5 +161,4 @@ #define ZEND_JMP_SET_VAR 158 #define ZEND_LEAVE 159 #define ZEND_YIELD 160 -#define ZEND_DELEGATE_YIELD 161 -#define ZEND_GENERATOR_RETURN 162 +#define ZEND_GENERATOR_RETURN 161 From cc07038fa9b2a59893c52fb0c515a1fb03e56d5c Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 29 Aug 2012 20:31:34 +0200 Subject: [PATCH 68/70] Make sure that exception is thrown on rewind() after closing too --- Zend/tests/generators/generator_rewind.phpt | 16 ++++++++++++++++ Zend/zend_generators.c | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Zend/tests/generators/generator_rewind.phpt b/Zend/tests/generators/generator_rewind.phpt index 3224f6a9b6d8..af885ef38203 100644 --- a/Zend/tests/generators/generator_rewind.phpt +++ b/Zend/tests/generators/generator_rewind.phpt @@ -21,6 +21,14 @@ try { echo "\n", $e, "\n\n"; } +$gen = gen(); +foreach ($gen as $v) { } +try { + foreach ($gen as $v) { } +} catch (Exception $e) { + echo "\n", $e, "\n\n"; +} + function gen2() { echo "in generator\n"; @@ -40,4 +48,12 @@ Stack trace: #0 %s(%d): Generator->rewind() #1 {main} +before yield +after yield + +exception 'Exception' with message 'Cannot rewind a generator that was already run' in %s:%d +Stack trace: +#0 %s(%d): unknown() +#1 {main} + in generator diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 03294f7f0ea4..0eb17d02937b 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -500,7 +500,7 @@ void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */ static void zend_generator_ensure_initialized(zend_generator *generator TSRMLS_DC) /* {{{ */ { - if (!generator->value) { + if (generator->execute_data && !generator->value) { zend_generator_resume(generator TSRMLS_CC); generator->flags |= ZEND_GENERATOR_AT_FIRST_YIELD; } From bef79588d543db996d092191ac498751a1cc161f Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 29 Aug 2012 20:46:56 +0200 Subject: [PATCH 69/70] Fix segfault when traversing a by-ref generator twice If you try to traverse an already closed generator an exception will now be thrown. Furthermore this changes the error for traversing a by-val generator by-ref from an E_ERROR to an Exception. --- ...n_ref_generator_iterated_by_ref_error.phpt | 7 ++++++- Zend/tests/generators/generator_rewind.phpt | 19 +++++++++++-------- Zend/zend_generators.c | 8 +++++++- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/Zend/tests/generators/errors/non_ref_generator_iterated_by_ref_error.phpt b/Zend/tests/generators/errors/non_ref_generator_iterated_by_ref_error.phpt index 9c618d251561..de5b22f6ba63 100644 --- a/Zend/tests/generators/errors/non_ref_generator_iterated_by_ref_error.phpt +++ b/Zend/tests/generators/errors/non_ref_generator_iterated_by_ref_error.phpt @@ -10,4 +10,9 @@ foreach ($gen as &$value) { } ?> --EXPECTF-- -Fatal error: You can only iterate a generator by-reference if it declared that it yields by-reference in %s on line %d +Fatal error: Uncaught exception 'Exception' with message 'You can only iterate a generator by-reference if it declared that it yields by-reference' in %s:%d +Stack trace: +#0 %s(%d): unknown() +#1 {main} + thrown in %s on line %d + diff --git a/Zend/tests/generators/generator_rewind.phpt b/Zend/tests/generators/generator_rewind.phpt index af885ef38203..c4b5bbbdf494 100644 --- a/Zend/tests/generators/generator_rewind.phpt +++ b/Zend/tests/generators/generator_rewind.phpt @@ -21,21 +21,27 @@ try { echo "\n", $e, "\n\n"; } -$gen = gen(); +function &gen2() { + $foo = 'bar'; + yield $foo; + yield $foo; +} + +$gen = gen2(); foreach ($gen as $v) { } try { foreach ($gen as $v) { } } catch (Exception $e) { - echo "\n", $e, "\n\n"; + echo $e, "\n\n"; } -function gen2() { +function gen3() { echo "in generator\n"; if (false) yield; } -$gen = gen2(); +$gen = gen3(); $gen->rewind(); ?> @@ -48,10 +54,7 @@ Stack trace: #0 %s(%d): Generator->rewind() #1 {main} -before yield -after yield - -exception 'Exception' with message 'Cannot rewind a generator that was already run' in %s:%d +exception 'Exception' with message 'Cannot traverse an already closed generator' in %s:%d Stack trace: #0 %s(%d): unknown() #1 {main} diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 0eb17d02937b..60fa8b64911f 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -766,8 +766,14 @@ zend_object_iterator *zend_generator_get_iterator(zend_class_entry *ce, zval *ob generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC); + if (!generator->execute_data) { + zend_throw_exception(NULL, "Cannot traverse an already closed generator", 0 TSRMLS_CC); + return NULL; + } + if (by_ref && !(generator->execute_data->op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) { - zend_error(E_ERROR, "You can only iterate a generator by-reference if it declared that it yields by-reference"); + zend_throw_exception(NULL, "You can only iterate a generator by-reference if it declared that it yields by-reference", 0 TSRMLS_CC); + return NULL; } iterator = emalloc(sizeof(zend_generator_iterator)); From dbc7809b1746ef5dd895e6cd369134cb5270ae0b Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 29 Aug 2012 20:49:14 +0200 Subject: [PATCH 70/70] Fix typos --- Zend/zend_generators.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 60fa8b64911f..c22d745bc362 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -301,7 +301,7 @@ static void zend_generator_clone_storage(zend_generator *orig, zend_generator ** Z_ADDREF_P(execute_data->object); } - /* Prev execute data contains an additional stack frame (for proper) + /* Prev execute data contains an additional stack frame (for proper * backtraces) which has to be copied. */ clone->execute_data->prev_execute_data = emalloc(sizeof(zend_execute_data)); memcpy(clone->execute_data->prev_execute_data, execute_data->prev_execute_data, sizeof(zend_execute_data)); @@ -643,9 +643,9 @@ ZEND_METHOD(Generator, send) RETURN_ZVAL(generator->value, 1, 0); } } +/* }}} */ - -/* {{{ proto void Generator::__wakeup +/* {{{ proto void Generator::__wakeup() * Throws an Exception as generators can't be serialized */ ZEND_METHOD(Generator, __wakeup) {