Permalink
Browse files

Add YIELD opcode implementation

  • Loading branch information...
1 parent 1a99d1c commit fafce58683e74a397023cc1077aae210109b40b6 @nikic nikic committed May 26, 2012
Showing with 256 additions and 3 deletions.
  1. +7 −1 Zend/zend_compile.c
  2. +21 −2 Zend/zend_generators.c
  3. +41 −0 Zend/zend_vm_def.h
  4. +186 −0 Zend/zend_vm_execute.h
  5. +1 −0 Zend/zend_vm_opcodes.h
View
@@ -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);
}
/* }}} */
View
@@ -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);
View
@@ -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)
View
@@ -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;
View
@@ -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

0 comments on commit fafce58

Please sign in to comment.