Implement yield* operation in generator functions. (#3407)

JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
Zoltan Herczeg
2019-12-05 13:50:53 +01:00
committed by Dániel Bátyai
parent 1829d2df55
commit 1a4972fc3f
13 changed files with 476 additions and 143 deletions
+6
View File
@@ -377,6 +377,12 @@ ecma_gc_mark_executable_object (ecma_object_t *object_p) /**< object */
return;
}
if (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_GENERATOR_ITERATE_AND_YIELD)
{
ecma_value_t iterator = executable_object_p->extended_object.u.class_prop.u.value;
ecma_gc_set_object_visited (ecma_get_object_from_value (iterator));
}
ecma_gc_set_object_visited (executable_object_p->frame_ctx.lex_env_p);
if (ecma_is_value_object (executable_object_p->frame_ctx.this_binding))
+2
View File
@@ -1761,6 +1761,8 @@ typedef enum
{
ECMA_EXECUTABLE_OBJECT_COMPLETED = (1u << 0), /**< executable object is completed and cannot be resumed */
ECMA_EXECUTABLE_OBJECT_RUNNING = (1u << 1), /**< executable object is currently running */
/* Generator specific flags. */
ECMA_GENERATOR_ITERATE_AND_YIELD = (1u << 2), /**< the generator performs a yield* operation */
} ecma_executable_object_flags_t;
/**
@@ -15,9 +15,11 @@
#include "ecma-builtins.h"
#include "ecma-exceptions.h"
#include "ecma-gc.h"
#include "ecma-globals.h"
#include "ecma-helpers.h"
#include "ecma-iterator-object.h"
#include "jcontext.h"
#include "opcodes.h"
#include "vm-defines.h"
@@ -40,16 +42,6 @@
* @{
*/
/**
* Generator resume execution flags.
*/
typedef enum
{
ECMA_GENERATOR_NEXT, /**< generator should continue its execution */
ECMA_GENERATOR_RETURN, /**< generator should perform a return operation */
ECMA_GENERATOR_THROW, /**< generator should perform a throw operation */
} ecma_generator_resume_mode_t;
/**
* Byte code sequence which returns from the generator.
*/
@@ -75,7 +67,7 @@ static const uint8_t ecma_builtin_generator_prototype_throw[1] =
static ecma_value_t
ecma_builtin_generator_prototype_object_do (ecma_value_t this_arg, /**< this argument */
ecma_value_t arg, /**< argument */
ecma_generator_resume_mode_t resume_mode) /**< resume mode */
ecma_iterator_command_type_t resume_mode) /**< resume mode */
{
vm_executable_object_t *executable_object_p = NULL;
@@ -109,27 +101,98 @@ ecma_builtin_generator_prototype_object_do (ecma_value_t this_arg, /**< this arg
return ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE);
}
if (resume_mode == ECMA_GENERATOR_RETURN)
arg = ecma_copy_value (arg);
while (true)
{
executable_object_p->frame_ctx.byte_code_p = ecma_builtin_generator_prototype_return;
if (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_GENERATOR_ITERATE_AND_YIELD)
{
ecma_value_t iterator = executable_object_p->extended_object.u.class_prop.u.value;
bool done = false;
ecma_value_t result = ecma_op_iterator_do (resume_mode, iterator, arg, &done);
ecma_free_value (arg);
if (ECMA_IS_VALUE_ERROR (result))
{
arg = result;
}
else if (done)
{
arg = ecma_op_iterator_value (result);
ecma_free_value (result);
if (resume_mode == ECMA_ITERATOR_THROW)
{
/* This part is changed in the newest ECMAScript standard.
* It was ECMA_ITERATOR_RETURN in ES2015, but I think it was a typo. */
resume_mode = ECMA_ITERATOR_NEXT;
}
}
else
{
return result;
}
executable_object_p->extended_object.u.class_prop.extra_info &= (uint16_t) ~ECMA_GENERATOR_ITERATE_AND_YIELD;
if (ECMA_IS_VALUE_ERROR (arg))
{
arg = JERRY_CONTEXT (error_value);
JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_EXCEPTION;
resume_mode = ECMA_ITERATOR_THROW;
}
}
if (resume_mode == ECMA_ITERATOR_RETURN)
{
executable_object_p->frame_ctx.byte_code_p = ecma_builtin_generator_prototype_return;
}
else if (resume_mode == ECMA_ITERATOR_THROW)
{
executable_object_p->frame_ctx.byte_code_p = ecma_builtin_generator_prototype_throw;
}
ecma_value_t value = opfunc_resume_executable_object (executable_object_p, arg);
if (ECMA_IS_VALUE_ERROR (value))
{
return value;
}
bool done = (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_EXECUTABLE_OBJECT_COMPLETED);
if (!done)
{
const uint8_t *byte_code_p = executable_object_p->frame_ctx.byte_code_p;
JERRY_ASSERT (byte_code_p[-2] == CBC_EXT_OPCODE
&& (byte_code_p[-1] == CBC_EXT_YIELD || byte_code_p[-1] == CBC_EXT_YIELD_ITERATOR));
if (byte_code_p[-1] == CBC_EXT_YIELD_ITERATOR)
{
ecma_value_t iterator = ecma_op_get_iterator (value, ECMA_VALUE_EMPTY);
ecma_free_value (value);
if (ECMA_IS_VALUE_ERROR (iterator))
{
resume_mode = ECMA_ITERATOR_THROW;
arg = JERRY_CONTEXT (error_value);
JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_EXCEPTION;
continue;
}
ecma_deref_object (ecma_get_object_from_value (iterator));
executable_object_p->extended_object.u.class_prop.extra_info |= ECMA_GENERATOR_ITERATE_AND_YIELD;
executable_object_p->extended_object.u.class_prop.u.value = iterator;
arg = ECMA_VALUE_UNDEFINED;
continue;
}
}
ecma_value_t result = ecma_create_iter_result_object (value, ecma_make_boolean_value (done));
ecma_fast_free_value (value);
return result;
}
else if (resume_mode == ECMA_GENERATOR_THROW)
{
executable_object_p->frame_ctx.byte_code_p = ecma_builtin_generator_prototype_throw;
}
ecma_value_t value = opfunc_resume_executable_object (executable_object_p, arg);
if (JERRY_UNLIKELY (ECMA_IS_VALUE_ERROR (value)))
{
return value;
}
bool done = (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_EXECUTABLE_OBJECT_COMPLETED);
ecma_value_t result = ecma_create_iter_result_object (value, ecma_make_boolean_value (done));
ecma_fast_free_value (value);
return result;
} /* ecma_builtin_generator_prototype_object_do */
/**
@@ -145,7 +208,7 @@ static ecma_value_t
ecma_builtin_generator_prototype_object_next (ecma_value_t this_arg, /**< this argument */
ecma_value_t next_arg) /**< next argument */
{
return ecma_builtin_generator_prototype_object_do (this_arg, next_arg, ECMA_GENERATOR_NEXT);
return ecma_builtin_generator_prototype_object_do (this_arg, next_arg, ECMA_ITERATOR_NEXT);
} /* ecma_builtin_generator_prototype_object_next */
/**
@@ -161,7 +224,7 @@ static ecma_value_t
ecma_builtin_generator_prototype_object_return (ecma_value_t this_arg, /**< this argument */
ecma_value_t return_arg) /**< return argument */
{
return ecma_builtin_generator_prototype_object_do (this_arg, return_arg, ECMA_GENERATOR_RETURN);
return ecma_builtin_generator_prototype_object_do (this_arg, return_arg, ECMA_ITERATOR_RETURN);
} /* ecma_builtin_generator_prototype_object_return */
/**
@@ -177,7 +240,7 @@ static ecma_value_t
ecma_builtin_generator_prototype_object_throw (ecma_value_t this_arg, /**< this argument */
ecma_value_t throw_arg) /**< throw argument */
{
return ecma_builtin_generator_prototype_object_do (this_arg, throw_arg, ECMA_GENERATOR_THROW);
return ecma_builtin_generator_prototype_object_do (this_arg, throw_arg, ECMA_ITERATOR_THROW);
} /* ecma_builtin_generator_prototype_object_throw */
/**
+197 -83
View File
@@ -248,20 +248,20 @@ ecma_op_iterator_next (ecma_value_t iterator, /**< iterator value */
/* 1 - 2. */
ecma_object_t *obj_p = ecma_get_object_from_value (iterator);
ecma_value_t next = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_NEXT);
ecma_value_t func_next = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_NEXT);
if (ECMA_IS_VALUE_ERROR (next))
if (ECMA_IS_VALUE_ERROR (func_next))
{
return next;
return func_next;
}
if (!ecma_is_value_object (next) || !ecma_op_is_callable (next))
if (!ecma_is_value_object (func_next) || !ecma_op_is_callable (func_next))
{
ecma_free_value (next);
return ecma_raise_type_error (ECMA_ERR_MSG ("Next is not callable."));
ecma_free_value (func_next);
return ecma_raise_type_error (ECMA_ERR_MSG ("Iterator next() is not callable."));
}
ecma_object_t *next_obj_p = ecma_get_object_from_value (next);
ecma_object_t *next_obj_p = ecma_get_object_from_value (func_next);
bool has_value = !ecma_is_value_empty (value);
@@ -275,58 +275,107 @@ ecma_op_iterator_next (ecma_value_t iterator, /**< iterator value */
result = ecma_op_function_call (next_obj_p, iterator, NULL, 0);
}
ecma_free_value (next);
/* 3. */
if (ECMA_IS_VALUE_ERROR (result))
{
return result;
}
/* 4. */
if (!ecma_is_value_object (result))
{
ecma_free_value (result);
return ecma_raise_type_error (ECMA_ERR_MSG ("Iterator result is not an object."));
}
ecma_free_value (func_next);
/* 5. */
return result;
} /* ecma_op_iterator_next */
/**
* IteratorComplete operation
* IteratorReturn operation
*
* See also: ECMA-262 v6, 7.4.3
* See also: ECMA-262 v6, 14.4.14 (last part)
*
* Note:
* Returned value must be freed with ecma_free_value.
*
* @return ECMA_VALUE_{FALSE, TRUE} - if success
* @return iterator result object - if success
* raised error - otherwise
*/
static ecma_value_t
ecma_op_iterator_complete (ecma_value_t iter_result) /**< iterator value */
ecma_op_iterator_return (ecma_value_t iterator, /**< iterator value */
ecma_value_t value) /**< the routines's value argument */
{
/* 1. */
JERRY_ASSERT (ecma_is_value_object (iter_result));
JERRY_ASSERT (ecma_is_value_object (iterator));
/* 2. */
ecma_object_t *obj_p = ecma_get_object_from_value (iter_result);
ecma_object_t *obj_p = ecma_get_object_from_value (iterator);
ecma_value_t func_return = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_RETURN);
ecma_value_t done = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_DONE);
if (ECMA_IS_VALUE_ERROR (done))
if (ECMA_IS_VALUE_ERROR (func_return))
{
return done;
return func_return;
}
bool is_done = ecma_op_to_boolean (done);
if (func_return == ECMA_VALUE_UNDEFINED)
{
return ecma_create_iter_result_object (value, true);
}
ecma_free_value (done);
if (!ecma_is_value_object (func_return) || !ecma_op_is_callable (func_return))
{
ecma_free_value (func_return);
return ecma_raise_type_error (ECMA_ERR_MSG ("Iterator return() is not callable."));
}
return ecma_make_boolean_value (is_done);
} /* ecma_op_iterator_complete */
ecma_object_t *return_obj_p = ecma_get_object_from_value (func_return);
ecma_value_t result = ecma_op_function_call (return_obj_p, iterator, &value, 1);
ecma_free_value (func_return);
return result;
} /* ecma_op_iterator_return */
/**
* IteratorThrow operation
*
* See also: ECMA-262 v6, 14.4.14 (last part)
*
* Note:
* Returned value must be freed with ecma_free_value.
*
* @return iterator result object - if success
* raised error - otherwise
*/
static ecma_value_t
ecma_op_iterator_throw (ecma_value_t iterator, /**< iterator value */
ecma_value_t value) /**< the routines's value argument */
{
JERRY_ASSERT (ecma_is_value_object (iterator));
ecma_object_t *obj_p = ecma_get_object_from_value (iterator);
ecma_value_t func_throw = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_THROW);
if (ECMA_IS_VALUE_ERROR (func_throw))
{
return func_throw;
}
if (func_throw == ECMA_VALUE_UNDEFINED)
{
ecma_value_t result = ecma_op_iterator_close (iterator);
if (ECMA_IS_VALUE_ERROR (result))
{
return result;
}
ecma_free_value (result);
return ecma_raise_type_error (ECMA_ERR_MSG ("Iterator throw() is not available."));
}
if (!ecma_is_value_object (func_throw) || !ecma_op_is_callable (func_throw))
{
ecma_free_value (func_throw);
return ecma_raise_type_error (ECMA_ERR_MSG ("Iterator throw() is not callable."));
}
ecma_object_t *return_obj_p = ecma_get_object_from_value (func_throw);
ecma_value_t result = ecma_op_function_call (return_obj_p, iterator, &value, 1);
ecma_free_value (func_throw);
return result;
} /* ecma_op_iterator_throw */
/**
* IteratorValue operation
@@ -349,52 +398,6 @@ ecma_op_iterator_value (ecma_value_t iter_result) /**< iterator value */
return ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_VALUE);
} /* ecma_op_iterator_value */
/**
* IteratorStep operation
*
* See also: ECMA-262 v6, 7.4.5
*
* Note:
* Returned value must be freed with ecma_free_value.
*
* @return iterator object or ECMA_VALUE_FALSE - if success
* raised error - otherwise
*/
ecma_value_t
ecma_op_iterator_step (ecma_value_t iterator) /**< iterator value */
{
/* 1. */
ecma_value_t result = ecma_op_iterator_next (iterator, ECMA_VALUE_EMPTY);
/* 2. */
if (ECMA_IS_VALUE_ERROR (result))
{
return result;
}
/* 3. */
ecma_value_t done = ecma_op_iterator_complete (result);
/* 4. */
if (ECMA_IS_VALUE_ERROR (done))
{
ecma_free_value (result);
return done;
}
ecma_free_value (done);
/* 5. */
if (ecma_is_value_true (done))
{
ecma_free_value (result);
return ECMA_VALUE_FALSE;
}
/* 6. */
return result;
} /* ecma_op_iterator_step */
/**
* IteratorClose operation
*
@@ -487,6 +490,117 @@ ecma_op_iterator_close (ecma_value_t iterator) /**< iterator value */
return ECMA_VALUE_ERROR;
} /* ecma_op_iterator_close */
/**
* IteratorStep operation
*
* See also: ECMA-262 v6, 7.4.5
*
* Note:
* Returned value must be freed with ecma_free_value.
*
* @return iterator object or ECMA_VALUE_FALSE - if success
* raised error - otherwise
*/
ecma_value_t
ecma_op_iterator_step (ecma_value_t iterator) /**< iterator value */
{
/* 1. */
ecma_value_t result = ecma_op_iterator_next (iterator, ECMA_VALUE_EMPTY);
/* 2. */
if (ECMA_IS_VALUE_ERROR (result))
{
return result;
}
if (!ecma_is_value_object (result))
{
ecma_free_value (result);
return ecma_raise_type_error (ECMA_ERR_MSG ("Iterator result is not an object."));
}
/* 3. */
ecma_object_t *obj_p = ecma_get_object_from_value (result);
ecma_value_t done = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_DONE);
/* 4. */
if (ECMA_IS_VALUE_ERROR (done))
{
ecma_free_value (result);
return done;
}
bool is_done = ecma_op_to_boolean (done);
ecma_free_value (done);
/* 5. */
if (is_done)
{
ecma_free_value (result);
return ECMA_VALUE_FALSE;
}
/* 6. */
return result;
} /* ecma_op_iterator_step */
/**
* Perform a command specified by the command argument
*
* Note:
* Returned value must be freed with ecma_free_value.
*
* @return iterator object - if success
* raised error - otherwise
*/
ecma_value_t
ecma_op_iterator_do (ecma_iterator_command_type_t command, /**< command to be executed */
ecma_value_t iterator, /**< iterator object */
ecma_value_t value, /**< the routines's value argument */
bool *done_p) /**< it contains the logical value of the done property */
{
ecma_value_t result;
if (command == ECMA_ITERATOR_NEXT)
{
result = ecma_op_iterator_next (iterator, value);
}
else if (command == ECMA_ITERATOR_RETURN)
{
result = ecma_op_iterator_return (iterator, value);
}
else
{
JERRY_ASSERT (command == ECMA_ITERATOR_THROW);
result = ecma_op_iterator_throw (iterator, value);
}
if (ECMA_IS_VALUE_ERROR (result))
{
return result;
}
if (!ecma_is_value_object (result))
{
ecma_free_value (result);
return ecma_raise_type_error (ECMA_ERR_MSG ("Iterator result is not an object."));
}
ecma_object_t *obj_p = ecma_get_object_from_value (result);
ecma_value_t done = ecma_op_object_get_by_magic_id (obj_p, LIT_MAGIC_STRING_DONE);
if (ECMA_IS_VALUE_ERROR (done))
{
ecma_free_value (result);
return done;
}
*done_p = ecma_op_to_boolean (done);
ecma_free_value (done);
return result;
} /* ecma_op_iterator_do */
#endif /* ENABLED (JERRY_ES2015) */
/**
@@ -27,6 +27,16 @@
* @{
*/
/**
* Generator resume execution flags.
*/
typedef enum
{
ECMA_ITERATOR_NEXT, /**< generator should continue its execution */
ECMA_ITERATOR_RETURN, /**< generator should perform a return operation */
ECMA_ITERATOR_THROW, /**< generator should perform a throw operation */
} ecma_iterator_command_type_t;
/**
* Maximum value of [[%Iterator%NextIndex]] until it can be stored
* in an ecma pseudo array object structure element.
@@ -49,11 +59,15 @@ ecma_op_get_iterator (ecma_value_t value, ecma_value_t method);
ecma_value_t
ecma_op_iterator_value (ecma_value_t iter_result);
ecma_value_t
ecma_op_iterator_close (ecma_value_t iterator);
ecma_value_t
ecma_op_iterator_step (ecma_value_t iterator);
ecma_value_t
ecma_op_iterator_close (ecma_value_t iterator);
ecma_op_iterator_do (ecma_iterator_command_type_t command, ecma_value_t iterator,
ecma_value_t value, bool *done_p);
#endif /* ENABLED (JERRY_ES2015) */