Implement the core of the generator functions. (#3368)

Some things are missing:
 - yield* support
 - generator definition in object literal
 - the hidden GeneratorFunction

JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
Zoltan Herczeg
2019-11-28 11:54:27 +01:00
committed by Dániel Bátyai
parent 14e95a4775
commit 110f75c99d
35 changed files with 1535 additions and 85 deletions
+176
View File
@@ -293,6 +293,7 @@ ecma_gc_mark_promise_object (ecma_extended_object_t *ext_object_p) /**< extended
#endif /* ENABLED (JERRY_ES2015_BUILTIN_PROMISE) */
#if ENABLED (JERRY_ES2015_BUILTIN_CONTAINER)
/**
* Mark objects referenced by Map/Set built-in.
*/
@@ -359,6 +360,73 @@ ecma_gc_mark_container_object (ecma_object_t *object_p) /**< object */
#endif /* ENABLED (JERRY_ES2015_BUILTIN_CONTAINER) */
#if ENABLED (JERRY_ES2015)
/**
* Mark objects referenced by inactive generator functions, async functions, etc.
*/
static void
ecma_gc_mark_executable_object (ecma_object_t *object_p) /**< object */
{
vm_executable_object_t *executable_object_p = (vm_executable_object_t *) object_p;
if (!ECMA_EXECUTABLE_OBJECT_IS_SUSPENDED (executable_object_p->extended_object.u.class_prop.extra_info))
{
/* All objects referenced by running executable objects are strong roots,
* and a finished executable object cannot refer to other values. */
return;
}
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))
{
ecma_gc_set_object_visited (ecma_get_object_from_value (executable_object_p->frame_ctx.this_binding));
}
const ecma_compiled_code_t *bytecode_header_p = executable_object_p->frame_ctx.bytecode_header_p;
size_t register_end;
if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)
{
cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p;
register_end = args_p->register_end;
}
else
{
cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p;
register_end = args_p->register_end;
}
ecma_value_t *register_p = VM_GET_REGISTERS (&executable_object_p->frame_ctx);
ecma_value_t *register_end_p = register_p + register_end;
while (register_p < register_end_p)
{
if (ecma_is_value_object (*register_p))
{
ecma_gc_set_object_visited (ecma_get_object_from_value (*register_p));
}
register_p++;
}
register_p += executable_object_p->frame_ctx.context_depth;
register_end_p = executable_object_p->frame_ctx.stack_top_p;
while (register_p < register_end_p)
{
if (ecma_is_value_object (*register_p))
{
ecma_gc_set_object_visited (ecma_get_object_from_value (*register_p));
}
register_p++;
}
} /* ecma_gc_mark_executable_object */
#endif /* ENABLED (JERRY_ES2015) */
/**
* Mark objects as visited starting from specified object as root
*/
@@ -435,6 +503,13 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */
break;
}
#endif /* ENABLED (JERRY_ES2015_BUILTIN_CONTAINER) */
#if ENABLED (JERRY_ES2015)
case LIT_MAGIC_STRING_GENERATOR_UL:
{
ecma_gc_mark_executable_object (object_p);
break;
}
#endif /* ENABLED (JERRY_ES2015) */
default:
{
break;
@@ -641,6 +716,100 @@ ecma_free_fast_access_array (ecma_object_t *object_p) /**< fast access mode arra
ecma_dealloc_extended_object (object_p, sizeof (ecma_extended_object_t));
} /* ecma_free_fast_access_array */
#if ENABLED (JERRY_ES2015)
/**
* Free non-objects referenced by inactive generator functions, async functions, etc.
*
* @return total object size
*/
static size_t
ecma_gc_free_executable_object (ecma_object_t *object_p) /**< object */
{
vm_executable_object_t *executable_object_p = (vm_executable_object_t *) object_p;
const ecma_compiled_code_t *bytecode_header_p = executable_object_p->frame_ctx.bytecode_header_p;
size_t size, register_end;
if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)
{
cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p;
register_end = args_p->register_end;
size = (register_end + (size_t) args_p->stack_limit) * sizeof (ecma_value_t);
}
else
{
cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p;
register_end = args_p->register_end;
size = (register_end + (size_t) args_p->stack_limit) * sizeof (ecma_value_t);
}
size = JERRY_ALIGNUP (sizeof (vm_executable_object_t) + size, sizeof (uintptr_t));
JERRY_ASSERT (!(executable_object_p->extended_object.u.class_prop.extra_info & ECMA_EXECUTABLE_OBJECT_RUNNING));
ecma_bytecode_deref ((ecma_compiled_code_t *) bytecode_header_p);
if (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_EXECUTABLE_OBJECT_COMPLETED)
{
return size;
}
ecma_free_value_if_not_object (executable_object_p->frame_ctx.this_binding);
ecma_value_t *register_p = VM_GET_REGISTERS (&executable_object_p->frame_ctx);
ecma_value_t *register_end_p = register_p + register_end;
while (register_p < register_end_p)
{
ecma_free_value_if_not_object (*register_p++);
}
if (executable_object_p->frame_ctx.context_depth > 0)
{
ecma_value_t *context_end_p = register_p;
register_p += executable_object_p->frame_ctx.context_depth;
ecma_value_t *context_top_p = register_p;
do
{
context_top_p[-1] &= (uint32_t) ~VM_CONTEXT_HAS_LEX_ENV;
uint32_t offsets = vm_get_context_value_offsets (context_top_p);
while (VM_CONTEXT_HAS_NEXT_OFFSET (offsets))
{
int32_t offset = VM_CONTEXT_GET_NEXT_OFFSET (offsets);
if (ecma_is_value_object (context_top_p[offset]))
{
context_top_p[offset] = ECMA_VALUE_UNDEFINED;
}
offsets >>= VM_CONTEXT_OFFSET_SHIFT;
}
context_top_p = vm_stack_context_abort (&executable_object_p->frame_ctx, context_top_p);
}
while (context_top_p > context_end_p);
}
register_end_p = executable_object_p->frame_ctx.stack_top_p;
while (register_p < register_end_p)
{
ecma_free_value_if_not_object (*register_p++);
}
return size;
} /* ecma_gc_free_executable_object */
#endif /* ENABLED (JERRY_ES2015) */
/**
* Free properties of an object
*/
@@ -930,6 +1099,13 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */
break;
}
#endif /* ENABLED (JERRY_ES2015_BUILTIN_DATAVIEW) */
#if ENABLED (JERRY_ES2015)
case LIT_MAGIC_STRING_GENERATOR_UL:
{
ext_object_size = ecma_gc_free_executable_object (object_p);
break;
}
#endif /* ENABLED (JERRY_ES2015) */
default:
{
/* The undefined id represents an uninitialized class. */
+29
View File
@@ -1750,6 +1750,35 @@ typedef struct
#endif /* ENABLED (JERRY_ES2015_BUILTIN_TYPEDARRAY) */
#if ENABLED (JERRY_ES2015)
/**
* Executable (e.g. generator, async) object flags.
*/
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 */
} ecma_executable_object_flags_t;
/**
* Checks whether the executable object is waiting for resuming.
*/
#define ECMA_EXECUTABLE_OBJECT_IS_SUSPENDED(extra_info) \
(!((extra_info) & (ECMA_EXECUTABLE_OBJECT_COMPLETED | ECMA_EXECUTABLE_OBJECT_RUNNING)))
/**
* Executable (e.g. generator, async) object 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_yield_mode_t;
#endif /* ENABLED (JERRY_ES2015) */
#if ENABLED (JERRY_ES2015_BUILTIN_DATAVIEW)
/**
* Description of DataView objects.
+26 -2
View File
@@ -855,10 +855,10 @@ ecma_fast_copy_value (ecma_value_t value) /**< value description */
*
* @return copy of the given value
*/
ecma_value_t
inline ecma_value_t JERRY_ATTR_ALWAYS_INLINE
ecma_copy_value_if_not_object (ecma_value_t value) /**< value description */
{
if (ecma_get_value_type_field (value) != ECMA_TYPE_OBJECT)
if (!ecma_is_value_object (value))
{
return ecma_copy_value (value);
}
@@ -866,6 +866,30 @@ ecma_copy_value_if_not_object (ecma_value_t value) /**< value description */
return value;
} /* ecma_copy_value_if_not_object */
/**
* Increase reference counter of a value if it is an object.
*/
inline void JERRY_ATTR_ALWAYS_INLINE
ecma_ref_if_object (ecma_value_t value) /**< value description */
{
if (ecma_is_value_object (value))
{
ecma_ref_object (ecma_get_object_from_value (value));
}
} /* ecma_ref_if_object */
/**
* Decrease reference counter of a value if it is an object.
*/
inline void JERRY_ATTR_ALWAYS_INLINE
ecma_deref_if_object (ecma_value_t value) /**< value description */
{
if (ecma_is_value_object (value))
{
ecma_deref_object (ecma_get_object_from_value (value));
}
} /* ecma_deref_if_object */
/**
* Assign a new value to an ecma-value
*
+2
View File
@@ -218,6 +218,8 @@ ecma_value_t JERRY_ATTR_CONST ecma_invert_boolean_value (ecma_value_t value);
ecma_value_t ecma_copy_value (ecma_value_t value);
ecma_value_t ecma_fast_copy_value (ecma_value_t value);
ecma_value_t ecma_copy_value_if_not_object (ecma_value_t value);
void ecma_ref_if_object (ecma_value_t value);
void ecma_deref_if_object (ecma_value_t value);
ecma_value_t ecma_update_float_number (ecma_value_t float_value, ecma_number_t new_number);
void ecma_value_assign_value (ecma_value_t *value_p, ecma_value_t ecma_value);
void ecma_value_assign_number (ecma_value_t *value_p, ecma_number_t ecma_number);
@@ -0,0 +1,156 @@
/* 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.
*/
#include "ecma-builtins.h"
#include "ecma-exceptions.h"
#include "ecma-globals.h"
#include "ecma-helpers.h"
#include "ecma-iterator-object.h"
#include "opcodes.h"
#include "vm-defines.h"
#if ENABLED (JERRY_ES2015)
#define ECMA_BUILTINS_INTERNAL
#include "ecma-builtins-internal.h"
#define BUILTIN_INC_HEADER_NAME "ecma-builtin-generator-prototype.inc.h"
#define BUILTIN_UNDERSCORED_ID generator_prototype
#include "ecma-builtin-internal-routines-template.inc.h"
/** \addtogroup ecma ECMA
* @{
*
* \addtogroup ecmabuiltins
* @{
*
* \addtogroup generator ECMA Generator object built-in
* @{
*/
/**
* Helper function for next / return / throw
*
* @return ecma value
* Returned value must be freed with ecma_free_value.
*/
static ecma_value_t
ecma_builtin_generator_prototype_object_do (ecma_value_t this_arg, /**< this argument */
ecma_value_t arg, /**< argument */
uint8_t yield_mode) /**< yield mode */
{
vm_executable_object_t *executable_object_p = NULL;
if (ecma_is_value_object (this_arg))
{
ecma_object_t *object_p = ecma_get_object_from_value (this_arg);
if (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_CLASS)
{
ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p;
if (ext_object_p->u.class_prop.class_id == LIT_MAGIC_STRING_GENERATOR_UL)
{
executable_object_p = (vm_executable_object_t *) ext_object_p;
}
}
}
if (executable_object_p == NULL)
{
return ecma_raise_type_error (ECMA_ERR_MSG ("Argument 'this' is not a generator object."));
}
if (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_EXECUTABLE_OBJECT_RUNNING)
{
return ecma_raise_type_error (ECMA_ERR_MSG ("Generator is currently under execution."));
}
if (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_EXECUTABLE_OBJECT_COMPLETED)
{
return ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE);
}
executable_object_p->frame_ctx.call_operation = yield_mode;
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 */
/**
* The Generator.prototype object's 'next' routine
*
* See also:
* ECMA-262 v6, 25.3.1.2
*
* @return ecma value
* Returned value must be freed with ecma_free_value.
*/
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);
} /* ecma_builtin_generator_prototype_object_next */
/**
* The Generator.prototype object's 'return' routine
*
* See also:
* ECMA-262 v6, 25.3.1.3
*
* @return ecma value
* Returned value must be freed with ecma_free_value.
*/
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);
} /* ecma_builtin_generator_prototype_object_return */
/**
* The Generator.prototype object's 'throw' routine
*
* See also:
* ECMA-262 v6, 25.3.1.4
*
* @return ecma value
* Returned value must be freed with ecma_free_value.
*/
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);
} /* ecma_builtin_generator_prototype_object_throw */
/**
* @}
* @}
* @}
*/
#endif /* ENABLED (JERRY_ES2015) */
@@ -0,0 +1,40 @@
/* 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.
*/
/*
* Generator.prototype built-in description
*/
#include "ecma-builtin-helpers-macro-defines.inc.h"
#if ENABLED (JERRY_ES2015)
/* Object properties:
* (property name, object pointer getter) */
/* ECMA-262 v6, 25.3.1.5 */
STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG,
LIT_MAGIC_STRING_GENERATOR_UL,
ECMA_PROPERTY_FLAG_CONFIGURABLE)
/* Routine properties:
* (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */
ROUTINE (LIT_MAGIC_STRING_NEXT, ecma_builtin_generator_prototype_object_next, 1, 1)
ROUTINE (LIT_MAGIC_STRING_RETURN, ecma_builtin_generator_prototype_object_return, 1, 1)
ROUTINE (LIT_MAGIC_STRING_THROW, ecma_builtin_generator_prototype_object_throw, 1, 1)
#endif /* ENABLED (JERRY_ES2015) */
#include "ecma-builtin-helpers-macro-undefs.inc.h"
@@ -524,7 +524,14 @@ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_SYMBOL,
true,
symbol)
BUILTIN (ECMA_BUILTIN_ID_GENERATOR_PROTOTYPE,
ECMA_OBJECT_TYPE_GENERAL,
ECMA_BUILTIN_ID_ITERATOR_PROTOTYPE,
true,
generator_prototype)
#endif /* ENABLED (JERRY_ES2015) */
#if ENABLED (JERRY_ES2015_BUILTIN_ITERATOR)
/* The %IteratorPrototype% object (ECMA-262 v6, 25.1.2) */
BUILTIN (ECMA_BUILTIN_ID_ITERATOR_PROTOTYPE,
@@ -789,21 +789,24 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */
bool free_this_binding = false;
const ecma_compiled_code_t *bytecode_data_p = ecma_op_function_get_compiled_code (ext_func_p);
uint16_t status_flags = bytecode_data_p->status_flags;
#if ENABLED (JERRY_ES2015)
bool is_class_constructor = (bytecode_data_p->status_flags & CBC_CODE_FLAGS_CONSTRUCTOR) != 0;
if (is_class_constructor && !ecma_op_function_has_construct_flag (arguments_list_p))
if (JERRY_UNLIKELY (status_flags & (CBC_CODE_FLAGS_CONSTRUCTOR | CBC_CODE_FLAGS_GENERATOR)))
{
return ecma_raise_type_error (ECMA_ERR_MSG ("Class constructor cannot be invoked without 'new'."));
if ((status_flags & CBC_CODE_FLAGS_CONSTRUCTOR) && !ecma_op_function_has_construct_flag (arguments_list_p))
{
return ecma_raise_type_error (ECMA_ERR_MSG ("Class constructor cannot be invoked without 'new'."));
}
if ((status_flags & CBC_CODE_FLAGS_GENERATOR) && ecma_op_function_has_construct_flag (arguments_list_p))
{
return ecma_raise_type_error (ECMA_ERR_MSG ("Generator functions cannot be invoked with 'new'."));
}
}
#endif /* ENABLED (JERRY_ES2015) */
bool is_strict = (bytecode_data_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) != 0;
bool is_no_lex_env = (bytecode_data_p->status_flags & CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED) != 0;
/* 1. */
if (!is_strict)
if (!(status_flags & CBC_CODE_FLAGS_STRICT_MODE))
{
if (ecma_is_value_undefined (this_binding)
|| ecma_is_value_null (this_binding))
@@ -825,7 +828,7 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */
/* 5. */
ecma_object_t *local_env_p;
if (is_no_lex_env)
if (status_flags & CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED)
{
local_env_p = scope_p;
}
@@ -841,7 +844,7 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */
bytecode_data_p);
}
#if ENABLED (JERRY_ES2015)
if (JERRY_UNLIKELY (is_class_constructor))
if (JERRY_UNLIKELY (status_flags & CBC_CODE_FLAGS_CONSTRUCTOR))
{
ecma_op_set_class_this_binding (local_env_p, this_binding);
}
@@ -854,7 +857,7 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */
arguments_list_p,
arguments_list_len);
if (!is_no_lex_env)
if (!(status_flags & CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED))
{
ecma_deref_object (local_env_p);
}