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:
committed by
Dániel Bátyai
parent
1829d2df55
commit
1a4972fc3f
@@ -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))
|
||||
|
||||
@@ -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 */
|
||||
|
||||
/**
|
||||
|
||||
@@ -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) */
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ extern "C"
|
||||
/**
|
||||
* Jerry snapshot format version.
|
||||
*/
|
||||
#define JERRY_SNAPSHOT_VERSION (33u)
|
||||
#define JERRY_SNAPSHOT_VERSION (34u)
|
||||
|
||||
/**
|
||||
* Flags for jerry_generate_snapshot and jerry_generate_function_snapshot.
|
||||
|
||||
@@ -658,6 +658,8 @@
|
||||
VM_OC_CREATE_GENERATOR) \
|
||||
CBC_OPCODE (CBC_EXT_YIELD, CBC_NO_FLAG, 0, \
|
||||
VM_OC_YIELD | VM_OC_GET_STACK) \
|
||||
CBC_OPCODE (CBC_EXT_YIELD_ITERATOR, CBC_NO_FLAG, 0, \
|
||||
VM_OC_YIELD | VM_OC_GET_STACK) \
|
||||
CBC_OPCODE (CBC_EXT_RETURN, CBC_NO_FLAG, -1, \
|
||||
VM_OC_EXT_RETURN | VM_OC_GET_STACK) \
|
||||
\
|
||||
|
||||
@@ -1563,8 +1563,16 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */
|
||||
parser_check_assignment_expr (context_p);
|
||||
lexer_next_token (context_p);
|
||||
|
||||
cbc_ext_opcode_t opcode = CBC_EXT_YIELD;
|
||||
|
||||
if (!lexer_check_yield_no_arg (context_p))
|
||||
{
|
||||
if (context_p->token.type == LEXER_MULTIPLY)
|
||||
{
|
||||
lexer_next_token (context_p);
|
||||
opcode = CBC_EXT_YIELD_ITERATOR;
|
||||
}
|
||||
|
||||
parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA);
|
||||
}
|
||||
else
|
||||
@@ -1572,7 +1580,7 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */
|
||||
parser_emit_cbc (context_p, CBC_PUSH_UNDEFINED);
|
||||
}
|
||||
|
||||
parser_emit_cbc_ext (context_p, CBC_EXT_YIELD);
|
||||
parser_emit_cbc_ext (context_p, opcode);
|
||||
|
||||
return (context_p->token.type != LEXER_RIGHT_PAREN
|
||||
&& context_p->token.type != LEXER_COMMA);
|
||||
|
||||
@@ -648,6 +648,10 @@ scanner_scan_primary_expression (parser_context_t *context_p, /**< context */
|
||||
scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END;
|
||||
}
|
||||
|
||||
if (context_p->token.type == LEXER_MULTIPLY)
|
||||
{
|
||||
return SCAN_NEXT_TOKEN;
|
||||
}
|
||||
return SCAN_KEEP_TOKEN;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
+12
-12
@@ -319,23 +319,23 @@ opfunc_append_to_spread_array (ecma_value_t *stack_top_p, /**< current stack top
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
ecma_value_t next = ecma_op_iterator_step (iterator);
|
||||
ecma_value_t next_value = ecma_op_iterator_step (iterator);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (next))
|
||||
if (ECMA_IS_VALUE_ERROR (next_value))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (ecma_is_value_false (next))
|
||||
if (ecma_is_value_false (next_value))
|
||||
{
|
||||
idx--;
|
||||
ret_value = ECMA_VALUE_EMPTY;
|
||||
break;
|
||||
}
|
||||
|
||||
ecma_value_t value = ecma_op_iterator_value (next);
|
||||
ecma_value_t value = ecma_op_iterator_value (next_value);
|
||||
|
||||
ecma_free_value (next);
|
||||
ecma_free_value (next_value);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (value))
|
||||
{
|
||||
@@ -412,22 +412,22 @@ opfunc_spread_arguments (ecma_value_t *stack_top_p, /**< pointer to the current
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
ecma_value_t next = ecma_op_iterator_step (iterator);
|
||||
ecma_value_t next_value = ecma_op_iterator_step (iterator);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (next))
|
||||
if (ECMA_IS_VALUE_ERROR (next_value))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (ecma_is_value_false (next))
|
||||
if (ecma_is_value_false (next_value))
|
||||
{
|
||||
ret_value = ECMA_VALUE_EMPTY;
|
||||
break;
|
||||
}
|
||||
|
||||
ecma_value_t value = ecma_op_iterator_value (next);
|
||||
ecma_value_t value = ecma_op_iterator_value (next_value);
|
||||
|
||||
ecma_free_value (next);
|
||||
ecma_free_value (next_value);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (value))
|
||||
{
|
||||
@@ -615,7 +615,7 @@ opfunc_create_executable_object (vm_frame_ctx_t *frame_ctx_p) /**< frame context
|
||||
*/
|
||||
ecma_value_t
|
||||
opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, /**< executable object */
|
||||
ecma_value_t value) /**< value pushed onto the stack */
|
||||
ecma_value_t value) /**< value pushed onto the stack (takes the reference) */
|
||||
{
|
||||
const ecma_compiled_code_t *bytecode_header_p = executable_object_p->frame_ctx.bytecode_header_p;
|
||||
ecma_value_t *register_p = VM_GET_REGISTERS (&executable_object_p->frame_ctx);
|
||||
@@ -654,7 +654,7 @@ opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, /*
|
||||
ecma_ref_if_object (*register_p++);
|
||||
}
|
||||
|
||||
*register_p++ = ecma_copy_value (value);
|
||||
*register_p++ = value;
|
||||
executable_object_p->frame_ctx.stack_top_p = register_p;
|
||||
|
||||
JERRY_ASSERT (ECMA_EXECUTABLE_OBJECT_IS_SUSPENDED (executable_object_p->extended_object.u.class_prop.extra_info));
|
||||
|
||||
+10
-11
@@ -3423,16 +3423,16 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
||||
goto error;
|
||||
}
|
||||
|
||||
ecma_value_t iterator_step = ecma_op_iterator_step (iterator);
|
||||
ecma_value_t next_value = ecma_op_iterator_step (iterator);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (iterator_step))
|
||||
if (ECMA_IS_VALUE_ERROR (next_value))
|
||||
{
|
||||
ecma_free_value (iterator);
|
||||
result = iterator_step;
|
||||
result = next_value;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (ecma_is_value_false (iterator_step))
|
||||
if (ecma_is_value_false (next_value))
|
||||
{
|
||||
ecma_free_value (iterator);
|
||||
byte_code_p = byte_code_start_p + branch_offset;
|
||||
@@ -3444,7 +3444,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
||||
VM_PLUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION);
|
||||
stack_top_p += PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION;
|
||||
stack_top_p[-1] = VM_CREATE_CONTEXT (VM_CONTEXT_FOR_OF, branch_offset);
|
||||
stack_top_p[-2] = iterator_step;
|
||||
stack_top_p[-2] = next_value;
|
||||
stack_top_p[-3] = iterator;
|
||||
|
||||
if (byte_code_p[0] == CBC_EXT_OPCODE && byte_code_p[1] == CBC_EXT_CLONE_CONTEXT)
|
||||
@@ -3474,18 +3474,18 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
||||
{
|
||||
JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p);
|
||||
|
||||
ecma_value_t iterator_step = ecma_op_iterator_step (stack_top_p[-3]);
|
||||
ecma_value_t next_value = ecma_op_iterator_step (stack_top_p[-3]);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (iterator_step))
|
||||
if (ECMA_IS_VALUE_ERROR (next_value))
|
||||
{
|
||||
result = iterator_step;
|
||||
result = next_value;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!ecma_is_value_false (iterator_step))
|
||||
if (!ecma_is_value_false (next_value))
|
||||
{
|
||||
ecma_free_value (stack_top_p[-2]);
|
||||
stack_top_p[-2] = iterator_step;
|
||||
stack_top_p[-2] = next_value;
|
||||
byte_code_p = byte_code_start_p + branch_offset;
|
||||
continue;
|
||||
}
|
||||
@@ -3494,7 +3494,6 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
||||
ecma_free_value (stack_top_p[-3]);
|
||||
VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION);
|
||||
stack_top_p -= PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION;
|
||||
|
||||
continue;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/* Copyright JS Foundation and other contributors, http://js.foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
function check_result(result, value, done)
|
||||
{
|
||||
assert(result.value === value)
|
||||
assert(result.done === done)
|
||||
}
|
||||
|
||||
function *gen1() {
|
||||
yield 1
|
||||
yield *[2,3,4]
|
||||
yield 5
|
||||
}
|
||||
|
||||
var g = gen1()
|
||||
check_result(g.next(), 1, false)
|
||||
check_result(g.next(), 2, false)
|
||||
check_result(g.next(), 3, false)
|
||||
check_result(g.next(), 4, false)
|
||||
check_result(g.next(), 5, false)
|
||||
check_result(g.next(), undefined, true)
|
||||
|
||||
function *gen2() {
|
||||
yield * true
|
||||
}
|
||||
|
||||
try {
|
||||
g = gen2()
|
||||
g.next()
|
||||
assert(false)
|
||||
} catch (e) {
|
||||
assert(e instanceof TypeError)
|
||||
}
|
||||
|
||||
var t0 = 0, t1 = 0
|
||||
|
||||
function *gen3() {
|
||||
function *f() {
|
||||
try {
|
||||
yield 5
|
||||
} finally {
|
||||
t0 = 1
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
yield *f()
|
||||
} finally {
|
||||
t1 = 1
|
||||
}
|
||||
}
|
||||
|
||||
g = gen3()
|
||||
check_result(g.next(), 5, false)
|
||||
check_result(g.return(13), 13, true)
|
||||
assert(t0 === 1)
|
||||
assert(t1 === 1)
|
||||
|
||||
t0 = -1
|
||||
t1 = 0
|
||||
|
||||
function *gen4() {
|
||||
function next(arg)
|
||||
{
|
||||
t0++;
|
||||
|
||||
if (t0 === 0)
|
||||
{
|
||||
assert(arg === undefined);
|
||||
return { value:2, done:false }
|
||||
}
|
||||
if (t0 === 1)
|
||||
{
|
||||
assert(arg === -3);
|
||||
return { value:3, done:false }
|
||||
}
|
||||
assert(arg === -4);
|
||||
return { value:4, done:true }
|
||||
}
|
||||
|
||||
var o = { [Symbol.iterator]() { return { next } } }
|
||||
assert((yield *o) === 4)
|
||||
return 5;
|
||||
}
|
||||
|
||||
g = gen4()
|
||||
check_result(g.next(-2), 2, false)
|
||||
check_result(g.next(-3), 3, false)
|
||||
check_result(g.next(-4), 5, true)
|
||||
|
||||
function *gen5() {
|
||||
function *f() {
|
||||
try {
|
||||
yield 1
|
||||
assert(false)
|
||||
} catch (e) {
|
||||
assert(e === 10)
|
||||
}
|
||||
return 2
|
||||
}
|
||||
|
||||
assert((yield *f()) === 2)
|
||||
yield 3
|
||||
}
|
||||
|
||||
g = gen5()
|
||||
check_result(g.next(), 1, false)
|
||||
check_result(g.throw(10), 3, false)
|
||||
@@ -223,7 +223,7 @@ main (void)
|
||||
/* Check the snapshot data. Unused bytes should be filled with zeroes */
|
||||
const uint8_t expected_data[] =
|
||||
{
|
||||
0x4A, 0x52, 0x52, 0x59, 0x21, 0x00, 0x00, 0x00,
|
||||
0x4A, 0x52, 0x52, 0x59, 0x22, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
|
||||
0x03, 0x00, 0x01, 0x00, 0x41, 0x00, 0x01, 0x00,
|
||||
|
||||
Reference in New Issue
Block a user