Implement async function execution. (#3897)
JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
+71
-39
@@ -575,43 +575,54 @@ opfunc_append_array (ecma_value_t *stack_top_p, /**< current stack top */
|
||||
*
|
||||
* @return executable object
|
||||
*/
|
||||
ecma_value_t
|
||||
opfunc_create_executable_object (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
||||
vm_executable_object_t *
|
||||
opfunc_create_executable_object (vm_frame_ctx_t *frame_ctx_p, /**< frame context */
|
||||
vm_create_executable_object_type_t type) /**< executable object type */
|
||||
{
|
||||
const ecma_compiled_code_t *bytecode_header_p = frame_ctx_p->bytecode_header_p;
|
||||
size_t size;
|
||||
size_t size, register_end;
|
||||
|
||||
ecma_bytecode_ref ((ecma_compiled_code_t *) bytecode_header_p);
|
||||
|
||||
if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)
|
||||
{
|
||||
cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p;
|
||||
size = ((size_t) args_p->register_end + (size_t) args_p->stack_limit) * sizeof (ecma_value_t);
|
||||
register_end = (size_t) 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;
|
||||
size = ((size_t) args_p->register_end + (size_t) args_p->stack_limit) * sizeof (ecma_value_t);
|
||||
register_end = (size_t) args_p->register_end;
|
||||
size = (register_end + (size_t) args_p->stack_limit) * sizeof (ecma_value_t);
|
||||
}
|
||||
|
||||
size_t total_size = JERRY_ALIGNUP (sizeof (vm_executable_object_t) + size, sizeof (uintptr_t));
|
||||
|
||||
ecma_object_t *proto_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_function_obj_p),
|
||||
ECMA_BUILTIN_ID_GENERATOR_PROTOTYPE);
|
||||
ecma_object_t *proto_p = NULL;
|
||||
|
||||
if (type == VM_CREATE_EXECUTABLE_OBJECT_GENERATOR)
|
||||
{
|
||||
proto_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_function_obj_p),
|
||||
ECMA_BUILTIN_ID_GENERATOR_PROTOTYPE);
|
||||
}
|
||||
|
||||
ecma_object_t *object_p = ecma_create_object (proto_p,
|
||||
total_size,
|
||||
ECMA_OBJECT_TYPE_CLASS);
|
||||
|
||||
ecma_deref_object (proto_p);
|
||||
|
||||
vm_executable_object_t *executable_object_p = (vm_executable_object_t *) object_p;
|
||||
|
||||
if (type == VM_CREATE_EXECUTABLE_OBJECT_GENERATOR)
|
||||
{
|
||||
ecma_deref_object (proto_p);
|
||||
}
|
||||
|
||||
/* Async function objects are not accessible, so their class_id is not relevant. */
|
||||
executable_object_p->extended_object.u.class_prop.class_id = LIT_MAGIC_STRING_GENERATOR_UL;
|
||||
executable_object_p->extended_object.u.class_prop.extra_info = 0;
|
||||
|
||||
JERRY_ASSERT (!frame_ctx_p->is_eval_code);
|
||||
JERRY_ASSERT (frame_ctx_p->context_depth == 0);
|
||||
|
||||
vm_frame_ctx_t *new_frame_ctx_p = &(executable_object_p->frame_ctx);
|
||||
*new_frame_ctx_p = *frame_ctx_p;
|
||||
@@ -627,18 +638,51 @@ opfunc_create_executable_object (vm_frame_ctx_t *frame_ctx_p) /**< frame context
|
||||
|
||||
/* Initial state is "not running", so all object references are released. */
|
||||
|
||||
if (frame_ctx_p->context_depth > 0)
|
||||
{
|
||||
JERRY_ASSERT (type != VM_CREATE_EXECUTABLE_OBJECT_GENERATOR);
|
||||
|
||||
ecma_value_t *register_end_p = new_registers_p + register_end;
|
||||
|
||||
JERRY_ASSERT (register_end_p <= new_stack_top_p);
|
||||
|
||||
while (new_registers_p < register_end_p)
|
||||
{
|
||||
ecma_deref_if_object (*new_registers_p++);
|
||||
}
|
||||
|
||||
vm_ref_lex_env_chain (frame_ctx_p->lex_env_p,
|
||||
frame_ctx_p->context_depth,
|
||||
new_registers_p,
|
||||
false);
|
||||
|
||||
new_registers_p += frame_ctx_p->context_depth;
|
||||
|
||||
JERRY_ASSERT (new_registers_p <= new_stack_top_p);
|
||||
}
|
||||
|
||||
while (new_registers_p < new_stack_top_p)
|
||||
{
|
||||
ecma_deref_if_object (*new_registers_p++);
|
||||
}
|
||||
|
||||
JERRY_ASSERT (new_frame_ctx_p->block_result == ECMA_VALUE_UNDEFINED);
|
||||
|
||||
new_frame_ctx_p->this_binding = ecma_copy_value_if_not_object (new_frame_ctx_p->this_binding);
|
||||
|
||||
JERRY_CONTEXT (vm_top_context_p) = new_frame_ctx_p->prev_context_p;
|
||||
|
||||
return ecma_make_object_value (object_p);
|
||||
return executable_object_p;
|
||||
} /* opfunc_create_executable_object */
|
||||
|
||||
/**
|
||||
* Byte code which resumes an executable object with throw
|
||||
*/
|
||||
const uint8_t opfunc_resume_executable_object_with_throw[1] =
|
||||
{
|
||||
CBC_THROW
|
||||
};
|
||||
|
||||
/**
|
||||
* Resume the execution of an inactive executable object
|
||||
*
|
||||
@@ -663,13 +707,15 @@ opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, /*
|
||||
register_end_p = register_p + args_p->register_end;
|
||||
}
|
||||
|
||||
while (register_p < register_end_p)
|
||||
{
|
||||
ecma_ref_if_object (*register_p++);
|
||||
}
|
||||
ecma_value_t *stack_top_p = executable_object_p->frame_ctx.stack_top_p;
|
||||
|
||||
if (executable_object_p->frame_ctx.context_depth > 0)
|
||||
{
|
||||
while (register_p < register_end_p)
|
||||
{
|
||||
ecma_ref_if_object (*register_p++);
|
||||
}
|
||||
|
||||
vm_ref_lex_env_chain (executable_object_p->frame_ctx.lex_env_p,
|
||||
executable_object_p->frame_ctx.context_depth,
|
||||
register_p,
|
||||
@@ -678,13 +724,13 @@ opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, /*
|
||||
register_p += executable_object_p->frame_ctx.context_depth;
|
||||
}
|
||||
|
||||
ecma_value_t *stack_top_p = executable_object_p->frame_ctx.stack_top_p;
|
||||
|
||||
while (register_p < stack_top_p)
|
||||
{
|
||||
ecma_ref_if_object (*register_p++);
|
||||
}
|
||||
|
||||
ecma_ref_if_object (executable_object_p->frame_ctx.block_result);
|
||||
|
||||
*register_p++ = value;
|
||||
executable_object_p->frame_ctx.stack_top_p = register_p;
|
||||
|
||||
@@ -716,14 +762,15 @@ opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, /*
|
||||
JERRY_CONTEXT (vm_top_context_p) = executable_object_p->frame_ctx.prev_context_p;
|
||||
|
||||
register_p = VM_GET_REGISTERS (&executable_object_p->frame_ctx);
|
||||
|
||||
while (register_p < register_end_p)
|
||||
{
|
||||
ecma_deref_if_object (*register_p++);
|
||||
}
|
||||
stack_top_p = executable_object_p->frame_ctx.stack_top_p;
|
||||
|
||||
if (executable_object_p->frame_ctx.context_depth > 0)
|
||||
{
|
||||
while (register_p < register_end_p)
|
||||
{
|
||||
ecma_deref_if_object (*register_p++);
|
||||
}
|
||||
|
||||
vm_ref_lex_env_chain (executable_object_p->frame_ctx.lex_env_p,
|
||||
executable_object_p->frame_ctx.context_depth,
|
||||
register_p,
|
||||
@@ -732,31 +779,16 @@ opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, /*
|
||||
register_p += executable_object_p->frame_ctx.context_depth;
|
||||
}
|
||||
|
||||
stack_top_p = executable_object_p->frame_ctx.stack_top_p;
|
||||
|
||||
while (register_p < stack_top_p)
|
||||
{
|
||||
ecma_deref_if_object (*register_p++);
|
||||
}
|
||||
|
||||
ecma_deref_if_object (executable_object_p->frame_ctx.block_result);
|
||||
|
||||
return result;
|
||||
} /* opfunc_resume_executable_object */
|
||||
|
||||
/**
|
||||
* Create a Promise object if needed and resolve it with a value
|
||||
*
|
||||
* @return Promise object
|
||||
*/
|
||||
ecma_value_t
|
||||
opfunc_return_promise (ecma_value_t value) /**< value */
|
||||
{
|
||||
ecma_value_t promise = ecma_make_object_value (ecma_builtin_get (ECMA_BUILTIN_ID_PROMISE));
|
||||
ecma_value_t result = ecma_promise_reject_or_resolve (promise, value, true);
|
||||
|
||||
ecma_free_value (value);
|
||||
return result;
|
||||
} /* opfunc_return_promise */
|
||||
|
||||
/**
|
||||
* Implicit class constructor handler when the classHeritage is not present.
|
||||
*
|
||||
|
||||
+17
-5
@@ -54,6 +54,19 @@ typedef enum
|
||||
NUMBER_BITWISE_NOT, /**< bitwise NOT calculation */
|
||||
} number_bitwise_logic_op;
|
||||
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
|
||||
/**
|
||||
* Types for opfunc_create_executable_object.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
VM_CREATE_EXECUTABLE_OBJECT_GENERATOR, /**< create a generator function */
|
||||
VM_CREATE_EXECUTABLE_OBJECT_ASYNC, /**< create an async function */
|
||||
} vm_create_executable_object_type_t;
|
||||
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
|
||||
/**
|
||||
* The stack contains spread object during the upcoming APPEND_ARRAY operation
|
||||
*/
|
||||
@@ -113,15 +126,14 @@ ecma_value_t
|
||||
opfunc_append_array (ecma_value_t *stack_top_p, uint16_t values_length);
|
||||
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
ecma_value_t
|
||||
opfunc_create_executable_object (vm_frame_ctx_t *frame_ctx_p);
|
||||
vm_executable_object_t *
|
||||
opfunc_create_executable_object (vm_frame_ctx_t *frame_ctx_p, vm_create_executable_object_type_t type);
|
||||
|
||||
extern const uint8_t opfunc_resume_executable_object_with_throw[];
|
||||
|
||||
ecma_value_t
|
||||
opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, ecma_value_t value);
|
||||
|
||||
ecma_value_t
|
||||
opfunc_return_promise (ecma_value_t value);
|
||||
|
||||
ecma_value_t
|
||||
opfunc_create_implicit_class_constructor (uint8_t opcode);
|
||||
|
||||
|
||||
@@ -262,6 +262,20 @@ vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, /**< frame context */
|
||||
}
|
||||
|
||||
JERRY_ASSERT (byte_code_p[0] == CBC_EXT_OPCODE);
|
||||
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
if (JERRY_UNLIKELY (byte_code_p[1] == CBC_EXT_ASYNC_EXIT))
|
||||
{
|
||||
branch_offset = (uint32_t) (byte_code_p - frame_ctx_p->byte_code_start_p);
|
||||
vm_stack_top_p[-1] = VM_CREATE_CONTEXT ((uint32_t) finally_type, branch_offset);
|
||||
|
||||
frame_ctx_p->byte_code_p = byte_code_p;
|
||||
|
||||
*vm_stack_top_ref_p = vm_stack_top_p;
|
||||
return true;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
|
||||
JERRY_ASSERT (byte_code_p[1] >= CBC_EXT_FINALLY
|
||||
&& byte_code_p[1] <= CBC_EXT_FINALLY_3);
|
||||
|
||||
@@ -310,21 +324,17 @@ vm_get_context_value_offsets (ecma_value_t *context_item_p) /**< any item of a c
|
||||
{
|
||||
return PARSER_TRY_CONTEXT_STACK_ALLOCATION;
|
||||
}
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
case VM_CONTEXT_BLOCK:
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
case VM_CONTEXT_WITH:
|
||||
{
|
||||
return PARSER_WITH_CONTEXT_STACK_ALLOCATION;
|
||||
}
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
case VM_CONTEXT_FOR_OF:
|
||||
{
|
||||
return ((3 << (VM_CONTEXT_OFFSET_SHIFT * 2))
|
||||
| (2 << (VM_CONTEXT_OFFSET_SHIFT))
|
||||
| PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION);
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
default:
|
||||
{
|
||||
return (4 << (VM_CONTEXT_OFFSET_SHIFT)) | PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION;
|
||||
|
||||
+91
-11
@@ -30,6 +30,7 @@
|
||||
#include "ecma-lex-env.h"
|
||||
#include "ecma-objects.h"
|
||||
#include "ecma-objects-general.h"
|
||||
#include "ecma-promise-object.h"
|
||||
#include "ecma-regexp-object.h"
|
||||
#include "ecma-try-catch-macro.h"
|
||||
#include "jcontext.h"
|
||||
@@ -2176,14 +2177,11 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
||||
frame_ctx_p->call_operation = VM_EXEC_RETURN;
|
||||
frame_ctx_p->byte_code_p = byte_code_p;
|
||||
frame_ctx_p->stack_top_p = stack_top_p;
|
||||
result = opfunc_create_executable_object (frame_ctx_p);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (result))
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
vm_executable_object_t *executable_object_p;
|
||||
executable_object_p = opfunc_create_executable_object (frame_ctx_p, VM_CREATE_EXECUTABLE_OBJECT_GENERATOR);
|
||||
|
||||
return result;
|
||||
return ecma_make_object_value ((ecma_object_t *) executable_object_p);
|
||||
}
|
||||
case VM_OC_YIELD:
|
||||
{
|
||||
@@ -2194,7 +2192,52 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
||||
}
|
||||
case VM_OC_AWAIT:
|
||||
{
|
||||
continue;
|
||||
ecma_value_t promise = ecma_make_object_value (ecma_builtin_get (ECMA_BUILTIN_ID_PROMISE));
|
||||
ecma_value_t argument = *(--stack_top_p);
|
||||
|
||||
result = ecma_promise_reject_or_resolve (promise, argument, true);
|
||||
ecma_free_value (argument);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (result))
|
||||
{
|
||||
goto error;
|
||||
}
|
||||
|
||||
frame_ctx_p->call_operation = VM_EXEC_RETURN;
|
||||
frame_ctx_p->byte_code_p = byte_code_p;
|
||||
frame_ctx_p->stack_top_p = stack_top_p;
|
||||
|
||||
if (frame_ctx_p->block_result == ECMA_VALUE_UNDEFINED)
|
||||
{
|
||||
vm_executable_object_t *executable_object_p;
|
||||
executable_object_p = opfunc_create_executable_object (frame_ctx_p, VM_CREATE_EXECUTABLE_OBJECT_ASYNC);
|
||||
|
||||
ecma_promise_async_then (result, ecma_make_object_value ((ecma_object_t *) executable_object_p));
|
||||
ecma_deref_object ((ecma_object_t *) executable_object_p);
|
||||
ecma_free_value (result);
|
||||
|
||||
ecma_object_t *old_new_target_p = JERRY_CONTEXT (current_new_target);
|
||||
JERRY_CONTEXT (current_new_target) = ecma_builtin_get (ECMA_BUILTIN_ID_PROMISE);
|
||||
|
||||
result = ecma_op_create_promise_object (ECMA_VALUE_EMPTY, ECMA_PROMISE_EXECUTOR_EMPTY);
|
||||
|
||||
JERRY_ASSERT (ecma_is_value_object (result));
|
||||
executable_object_p->frame_ctx.block_result = result;
|
||||
|
||||
JERRY_CONTEXT (current_new_target) = old_new_target_p;
|
||||
}
|
||||
else
|
||||
{
|
||||
const uintptr_t object_offset = (uintptr_t) offsetof (vm_executable_object_t, frame_ctx);
|
||||
|
||||
ecma_object_t *object_p = (ecma_object_t *) (((uintptr_t) frame_ctx_p) - object_offset);
|
||||
ecma_promise_async_then (result, ecma_make_object_value (object_p));
|
||||
|
||||
ecma_free_value (result);
|
||||
result = ECMA_VALUE_UNDEFINED;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
case VM_OC_EXT_RETURN:
|
||||
{
|
||||
@@ -2210,11 +2253,48 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
||||
|
||||
goto error;
|
||||
}
|
||||
case VM_OC_RETURN_PROMISE:
|
||||
case VM_OC_ASYNC_EXIT:
|
||||
{
|
||||
result = opfunc_return_promise (left_value);
|
||||
left_value = ECMA_VALUE_UNDEFINED;
|
||||
goto error;
|
||||
JERRY_ASSERT (frame_ctx_p->context_depth == PARSER_TRY_CONTEXT_STACK_ALLOCATION);
|
||||
JERRY_ASSERT (VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth == stack_top_p);
|
||||
|
||||
result = frame_ctx_p->block_result;
|
||||
frame_ctx_p->block_result = ECMA_VALUE_UNDEFINED;
|
||||
|
||||
if (result == ECMA_VALUE_UNDEFINED)
|
||||
{
|
||||
ecma_object_t *old_new_target_p = JERRY_CONTEXT (current_new_target);
|
||||
JERRY_CONTEXT (current_new_target) = ecma_builtin_get (ECMA_BUILTIN_ID_PROMISE);
|
||||
|
||||
result = ecma_op_create_promise_object (ECMA_VALUE_EMPTY, ECMA_PROMISE_EXECUTOR_EMPTY);
|
||||
|
||||
JERRY_CONTEXT (current_new_target) = old_new_target_p;
|
||||
}
|
||||
|
||||
left_value = stack_top_p[-2];
|
||||
|
||||
if (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_THROW)
|
||||
{
|
||||
ecma_reject_promise (result, left_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
JERRY_ASSERT (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_TRY
|
||||
|| VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_RETURN);
|
||||
|
||||
if (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_TRY)
|
||||
{
|
||||
left_value = ECMA_VALUE_UNDEFINED;
|
||||
}
|
||||
|
||||
ecma_fulfill_promise (result, left_value);
|
||||
}
|
||||
|
||||
ecma_free_value (left_value);
|
||||
|
||||
frame_ctx_p->context_depth = 0;
|
||||
frame_ctx_p->call_operation = VM_NO_EXEC_OP;
|
||||
return result;
|
||||
}
|
||||
case VM_OC_STRING_CONCAT:
|
||||
{
|
||||
|
||||
+2
-2
@@ -273,7 +273,7 @@ typedef enum
|
||||
VM_OC_YIELD, /**< yield operation */
|
||||
VM_OC_AWAIT, /**< await operation */
|
||||
VM_OC_EXT_RETURN, /**< return which also clears the stack */
|
||||
VM_OC_RETURN_PROMISE, /**< return from an async function */
|
||||
VM_OC_ASYNC_EXIT, /**< return from async function */
|
||||
VM_OC_STRING_CONCAT, /**< string concatenation */
|
||||
VM_OC_GET_TEMPLATE_OBJECT, /**< GetTemplateObject operation */
|
||||
VM_OC_PUSH_NEW_TARGET, /**< push new.target onto the stack */
|
||||
@@ -342,7 +342,7 @@ typedef enum
|
||||
VM_OC_YIELD = VM_OC_NONE, /**< yield operation */
|
||||
VM_OC_AWAIT = VM_OC_NONE, /**< await operation */
|
||||
VM_OC_EXT_RETURN = VM_OC_NONE, /**< return which also clears the stack */
|
||||
VM_OC_RETURN_PROMISE = VM_OC_NONE, /**< return from an async function */
|
||||
VM_OC_ASYNC_EXIT = VM_OC_NONE, /**< return from async function */
|
||||
VM_OC_STRING_CONCAT = VM_OC_NONE, /**< string concatenation */
|
||||
VM_OC_GET_TEMPLATE_OBJECT = VM_OC_NONE, /**< GetTemplateObject operation */
|
||||
VM_OC_PUSH_NEW_TARGET = VM_OC_NONE, /**< push new.target onto the stack */
|
||||
|
||||
Reference in New Issue
Block a user