mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
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:
parent
eb8e81d682
commit
8719f72e61
@ -463,6 +463,11 @@ ecma_gc_mark_executable_object (ecma_object_t *object_p) /**< object */
|
||||
|
||||
register_p++;
|
||||
}
|
||||
|
||||
if (ecma_is_value_object (executable_object_p->frame_ctx.block_result))
|
||||
{
|
||||
ecma_gc_set_object_visited (ecma_get_object_from_value (executable_object_p->frame_ctx.block_result));
|
||||
}
|
||||
} /* ecma_gc_mark_executable_object */
|
||||
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
|
||||
@ -66,14 +66,6 @@ static const uint8_t ecma_builtin_generator_prototype_return[2] =
|
||||
CBC_EXT_OPCODE, CBC_EXT_RETURN
|
||||
};
|
||||
|
||||
/**
|
||||
* Byte code sequence which throws an exception.
|
||||
*/
|
||||
static const uint8_t ecma_builtin_generator_prototype_throw[1] =
|
||||
{
|
||||
CBC_THROW
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function for next / return / throw
|
||||
*
|
||||
@ -132,7 +124,7 @@ ecma_builtin_generator_prototype_object_do (vm_executable_object_t *executable_o
|
||||
}
|
||||
else if (resume_mode == ECMA_ITERATOR_THROW)
|
||||
{
|
||||
executable_object_p->frame_ctx.byte_code_p = ecma_builtin_generator_prototype_throw;
|
||||
executable_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_throw;
|
||||
}
|
||||
|
||||
ecma_value_t value = opfunc_resume_executable_object (executable_object_p, arg);
|
||||
|
||||
@ -1289,6 +1289,11 @@ ecma_op_function_construct (ecma_object_t *func_obj_p, /**< Function object */
|
||||
message_p = ECMA_ERR_MSG ("Generator functions cannot be invoked with 'new'.");
|
||||
break;
|
||||
}
|
||||
case CBC_FUNCTION_ASYNC:
|
||||
{
|
||||
message_p = ECMA_ERR_MSG ("Async functions cannot be invoked with 'new'.");
|
||||
break;
|
||||
}
|
||||
case CBC_FUNCTION_ARROW:
|
||||
{
|
||||
message_p = ECMA_ERR_MSG ("Arrow functions cannot be invoked with 'new'.");
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
#include "ecma-objects.h"
|
||||
#include "ecma-promise-object.h"
|
||||
#include "jcontext.h"
|
||||
#include "opcodes.h"
|
||||
|
||||
#if ENABLED (JERRY_BUILTIN_PROMISE)
|
||||
|
||||
@ -46,6 +47,16 @@ typedef struct
|
||||
ecma_value_t argument; /**< argument for the reaction */
|
||||
} ecma_job_promise_reaction_t;
|
||||
|
||||
/**
|
||||
* Description of the PromiseAsyncReactionJob
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
ecma_job_queue_item_t header; /**< job queue item header */
|
||||
ecma_value_t executable_object; /**< executable object */
|
||||
ecma_value_t argument; /**< argument for the reaction */
|
||||
} ecma_job_promise_async_reaction_t;
|
||||
|
||||
/**
|
||||
* Description of the PromiseResolveThenableJob
|
||||
*/
|
||||
@ -103,6 +114,21 @@ ecma_free_promise_reaction_job (ecma_job_promise_reaction_t *job_p) /**< points
|
||||
jmem_heap_free_block (job_p, sizeof (ecma_job_promise_reaction_t));
|
||||
} /* ecma_free_promise_reaction_job */
|
||||
|
||||
/**
|
||||
* Free the heap and the member of the PromiseAsyncReactionJob.
|
||||
*/
|
||||
static void
|
||||
ecma_free_promise_async_reaction_job (ecma_job_promise_async_reaction_t *job_p) /**< points to the
|
||||
* PromiseAsyncReactionJob */
|
||||
{
|
||||
JERRY_ASSERT (job_p != NULL);
|
||||
|
||||
ecma_free_value (job_p->executable_object);
|
||||
ecma_free_value (job_p->argument);
|
||||
|
||||
jmem_heap_free_block (job_p, sizeof (ecma_job_promise_async_reaction_t));
|
||||
} /* ecma_free_promise_async_reaction_job */
|
||||
|
||||
/**
|
||||
* Free the heap and the member of the PromiseResolveThenableJob.
|
||||
*/
|
||||
@ -196,6 +222,31 @@ ecma_process_promise_reaction_job (ecma_job_promise_reaction_t *job_p) /**< the
|
||||
return status;
|
||||
} /* ecma_process_promise_reaction_job */
|
||||
|
||||
/**
|
||||
* The processor for PromiseAsyncReactionJob.
|
||||
*
|
||||
* @return ecma value
|
||||
* Returned value must be freed with ecma_free_value
|
||||
*/
|
||||
static ecma_value_t
|
||||
ecma_process_promise_async_reaction_job (ecma_job_promise_async_reaction_t *job_p) /**< the job to be operated */
|
||||
{
|
||||
ecma_object_t *object_p = ecma_get_object_from_value (job_p->executable_object);
|
||||
vm_executable_object_t *executable_object_p = (vm_executable_object_t *) object_p;
|
||||
|
||||
if (ecma_job_queue_get_type (&job_p->header) == ECMA_JOB_PROMISE_ASYNC_REACTION_REJECTED)
|
||||
{
|
||||
executable_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_throw;
|
||||
}
|
||||
|
||||
ecma_value_t result = opfunc_resume_executable_object (executable_object_p, job_p->argument);
|
||||
/* Argument reference is taken by opfunc_resume_executable_object. */
|
||||
job_p->argument = ECMA_VALUE_UNDEFINED;
|
||||
ecma_free_promise_async_reaction_job (job_p);
|
||||
|
||||
return result;
|
||||
} /* ecma_process_promise_async_reaction_job */
|
||||
|
||||
/**
|
||||
* Process the PromiseResolveThenableJob.
|
||||
*
|
||||
@ -273,7 +324,7 @@ ecma_enqueue_job (ecma_job_queue_item_t *job_p) /**< the job */
|
||||
} /* ecma_enqueue_job */
|
||||
|
||||
/**
|
||||
* Enqueue a PromiseReactionJob into the jobqueue.
|
||||
* Enqueue a PromiseReactionJob into the job queue.
|
||||
*/
|
||||
void
|
||||
ecma_enqueue_promise_reaction_job (ecma_value_t capability, /**< capability object */
|
||||
@ -291,7 +342,25 @@ ecma_enqueue_promise_reaction_job (ecma_value_t capability, /**< capability obje
|
||||
} /* ecma_enqueue_promise_reaction_job */
|
||||
|
||||
/**
|
||||
* Enqueue a PromiseResolveThenableJob into the jobqueue.
|
||||
* Enqueue a PromiseAsyncReactionJob into the job queue.
|
||||
*/
|
||||
void
|
||||
ecma_enqueue_promise_async_reaction_job (ecma_value_t executable_object, /**< executable object */
|
||||
ecma_value_t argument, /**< argument */
|
||||
bool is_rejected) /**< is_fulfilled */
|
||||
{
|
||||
ecma_job_promise_async_reaction_t *job_p;
|
||||
job_p = (ecma_job_promise_async_reaction_t *) jmem_heap_alloc_block (sizeof (ecma_job_promise_async_reaction_t));
|
||||
job_p->header.next_and_type = (is_rejected ? ECMA_JOB_PROMISE_ASYNC_REACTION_REJECTED
|
||||
: ECMA_JOB_PROMISE_ASYNC_REACTION_FULFILLED);
|
||||
job_p->executable_object = ecma_copy_value (executable_object);
|
||||
job_p->argument = ecma_copy_value (argument);
|
||||
|
||||
ecma_enqueue_job (&job_p->header);
|
||||
} /* ecma_enqueue_promise_async_reaction_job */
|
||||
|
||||
/**
|
||||
* Enqueue a PromiseResolveThenableJob into the job queue.
|
||||
*/
|
||||
void
|
||||
ecma_enqueue_promise_resolve_thenable_job (ecma_value_t promise, /**< promise to be resolved */
|
||||
@ -338,6 +407,12 @@ ecma_process_all_enqueued_jobs (void)
|
||||
ret = ecma_process_promise_reaction_job ((ecma_job_promise_reaction_t *) job_p);
|
||||
break;
|
||||
}
|
||||
case ECMA_JOB_PROMISE_ASYNC_REACTION_FULFILLED:
|
||||
case ECMA_JOB_PROMISE_ASYNC_REACTION_REJECTED:
|
||||
{
|
||||
ret = ecma_process_promise_async_reaction_job ((ecma_job_promise_async_reaction_t *) job_p);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
JERRY_ASSERT (ecma_job_queue_get_type (job_p) == ECMA_JOB_PROMISE_THENABLE);
|
||||
@ -369,6 +444,12 @@ ecma_free_all_enqueued_jobs (void)
|
||||
ecma_free_promise_reaction_job ((ecma_job_promise_reaction_t *) job_p);
|
||||
break;
|
||||
}
|
||||
case ECMA_JOB_PROMISE_ASYNC_REACTION_FULFILLED:
|
||||
case ECMA_JOB_PROMISE_ASYNC_REACTION_REJECTED:
|
||||
{
|
||||
ecma_free_promise_async_reaction_job ((ecma_job_promise_async_reaction_t *) job_p);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
JERRY_ASSERT (ecma_job_queue_get_type (job_p) == ECMA_JOB_PROMISE_THENABLE);
|
||||
|
||||
@ -31,6 +31,8 @@
|
||||
typedef enum
|
||||
{
|
||||
ECMA_JOB_PROMISE_REACTION, /**< promise reaction job */
|
||||
ECMA_JOB_PROMISE_ASYNC_REACTION_FULFILLED, /**< fulfilled promise async reaction job */
|
||||
ECMA_JOB_PROMISE_ASYNC_REACTION_REJECTED, /**< rejected promise async reaction job */
|
||||
ECMA_JOB_PROMISE_THENABLE, /**< promise thenable job */
|
||||
} ecma_job_queue_item_type_t;
|
||||
|
||||
@ -45,6 +47,8 @@ typedef struct
|
||||
void ecma_job_queue_init (void);
|
||||
|
||||
void ecma_enqueue_promise_reaction_job (ecma_value_t capability, ecma_value_t handler, ecma_value_t argument);
|
||||
void ecma_enqueue_promise_async_reaction_job (ecma_value_t executable_object,
|
||||
ecma_value_t argument, bool is_rejected);
|
||||
void ecma_enqueue_promise_resolve_thenable_job (ecma_value_t promise, ecma_value_t thenable, ecma_value_t then);
|
||||
void ecma_free_all_enqueued_jobs (void);
|
||||
|
||||
|
||||
@ -123,22 +123,28 @@ ecma_promise_trigger_reactions (ecma_collection_t *reactions, /**< lists of reac
|
||||
|
||||
while (buffer_p < buffer_end_p)
|
||||
{
|
||||
ecma_value_t capability_with_tag = *buffer_p++;
|
||||
ecma_object_t *capability_obj_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, capability_with_tag);
|
||||
ecma_value_t capability = ecma_make_object_value (capability_obj_p);
|
||||
ecma_value_t object_with_tag = *buffer_p++;
|
||||
ecma_object_t *object_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, object_with_tag);
|
||||
ecma_value_t object = ecma_make_object_value (object_p);
|
||||
|
||||
if (JMEM_CP_GET_THIRD_BIT_FROM_POINTER_TAG (object_with_tag))
|
||||
{
|
||||
ecma_enqueue_promise_async_reaction_job (object, value, is_reject);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_reject)
|
||||
{
|
||||
ecma_value_t handler = ECMA_VALUE_TRUE;
|
||||
|
||||
if (JMEM_CP_GET_FIRST_BIT_FROM_POINTER_TAG (capability_with_tag))
|
||||
if (JMEM_CP_GET_FIRST_BIT_FROM_POINTER_TAG (object_with_tag))
|
||||
{
|
||||
handler = *buffer_p++;
|
||||
}
|
||||
|
||||
ecma_enqueue_promise_reaction_job (capability, handler, value);
|
||||
ecma_enqueue_promise_reaction_job (object, handler, value);
|
||||
}
|
||||
else if (JMEM_CP_GET_FIRST_BIT_FROM_POINTER_TAG (capability_with_tag))
|
||||
else if (JMEM_CP_GET_FIRST_BIT_FROM_POINTER_TAG (object_with_tag))
|
||||
{
|
||||
buffer_p++;
|
||||
}
|
||||
@ -147,14 +153,14 @@ ecma_promise_trigger_reactions (ecma_collection_t *reactions, /**< lists of reac
|
||||
{
|
||||
ecma_value_t handler = ECMA_VALUE_FALSE;
|
||||
|
||||
if (JMEM_CP_GET_SECOND_BIT_FROM_POINTER_TAG (capability_with_tag))
|
||||
if (JMEM_CP_GET_SECOND_BIT_FROM_POINTER_TAG (object_with_tag))
|
||||
{
|
||||
handler = *buffer_p++;
|
||||
}
|
||||
|
||||
ecma_enqueue_promise_reaction_job (capability, handler, value);
|
||||
ecma_enqueue_promise_reaction_job (object, handler, value);
|
||||
}
|
||||
else if (JMEM_CP_GET_SECOND_BIT_FROM_POINTER_TAG (capability_with_tag))
|
||||
else if (JMEM_CP_GET_SECOND_BIT_FROM_POINTER_TAG (object_with_tag))
|
||||
{
|
||||
buffer_p++;
|
||||
}
|
||||
@ -198,7 +204,7 @@ ecma_is_resolver_already_called (ecma_object_t *resolver_p, /**< resolver */
|
||||
*
|
||||
* See also: ES2015 25.4.1.7
|
||||
*/
|
||||
static void
|
||||
void
|
||||
ecma_reject_promise (ecma_value_t promise, /**< promise */
|
||||
ecma_value_t reason) /**< reason for reject */
|
||||
{
|
||||
@ -227,7 +233,7 @@ ecma_reject_promise (ecma_value_t promise, /**< promise */
|
||||
*
|
||||
* See also: ES2015 25.4.1.4
|
||||
*/
|
||||
static void
|
||||
void
|
||||
ecma_fulfill_promise (ecma_value_t promise, /**< promise */
|
||||
ecma_value_t value) /**< fulfilled value */
|
||||
{
|
||||
@ -235,6 +241,37 @@ ecma_fulfill_promise (ecma_value_t promise, /**< promise */
|
||||
|
||||
JERRY_ASSERT (ecma_promise_get_flags (obj_p) & ECMA_PROMISE_IS_PENDING);
|
||||
|
||||
if (promise == value)
|
||||
{
|
||||
ecma_raise_type_error (ECMA_ERR_MSG ("A promise cannot be resolved with itself."));
|
||||
ecma_value_t exception = jcontext_take_exception ();
|
||||
ecma_reject_promise (promise, exception);
|
||||
ecma_free_value (exception);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ecma_is_value_object (value))
|
||||
{
|
||||
ecma_value_t then = ecma_op_object_get_by_magic_id (ecma_get_object_from_value (value), LIT_MAGIC_STRING_THEN);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (then))
|
||||
{
|
||||
then = jcontext_take_exception ();
|
||||
ecma_reject_promise (promise, then);
|
||||
ecma_free_value (then);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ecma_op_is_callable (then))
|
||||
{
|
||||
ecma_enqueue_promise_resolve_thenable_job (promise, value, then);
|
||||
ecma_free_value (then);
|
||||
return;
|
||||
}
|
||||
|
||||
ecma_free_value (then);
|
||||
}
|
||||
|
||||
ecma_promise_set_state (obj_p, true);
|
||||
ecma_promise_set_result (obj_p, ecma_copy_value_if_not_object (value));
|
||||
ecma_promise_object_t *promise_p = (ecma_promise_object_t *) obj_p;
|
||||
@ -311,62 +348,16 @@ ecma_promise_resolve_handler (const ecma_value_t function, /**< the function its
|
||||
JERRY_ASSERT (ecma_is_promise (promise_obj_p));
|
||||
|
||||
/* 3., 4. */
|
||||
if (ecma_is_resolver_already_called (function_p, promise_obj_p))
|
||||
if (!ecma_is_resolver_already_called (function_p, promise_obj_p))
|
||||
{
|
||||
goto end_of_resolve_function;
|
||||
/* 5. */
|
||||
((ecma_extended_object_t *) promise_obj_p)->u.class_prop.extra_info |= ECMA_PROMISE_ALREADY_RESOLVED;
|
||||
|
||||
ecma_fulfill_promise (promise, (argc == 0) ? ECMA_VALUE_UNDEFINED : argv[0]);
|
||||
}
|
||||
|
||||
/* 5. */
|
||||
((ecma_extended_object_t *) promise_obj_p)->u.class_prop.extra_info |= ECMA_PROMISE_ALREADY_RESOLVED;
|
||||
|
||||
/* If the argc is 0, then fulfill the `undefined`. */
|
||||
if (argc == 0)
|
||||
{
|
||||
ecma_fulfill_promise (promise, ECMA_VALUE_UNDEFINED);
|
||||
goto end_of_resolve_function;
|
||||
}
|
||||
|
||||
/* 6. */
|
||||
if (argv[0] == promise)
|
||||
{
|
||||
ecma_object_t *error_p = ecma_new_standard_error (ECMA_ERROR_TYPE);
|
||||
ecma_reject_promise (promise, ecma_make_object_value (error_p));
|
||||
ecma_deref_object (error_p);
|
||||
goto end_of_resolve_function;
|
||||
}
|
||||
|
||||
/* 7. */
|
||||
if (!ecma_is_value_object (argv[0]))
|
||||
{
|
||||
ecma_fulfill_promise (promise, argv[0]);
|
||||
goto end_of_resolve_function;
|
||||
}
|
||||
|
||||
/* 8. */
|
||||
ecma_value_t then = ecma_op_object_get_by_magic_id (ecma_get_object_from_value (argv[0]),
|
||||
LIT_MAGIC_STRING_THEN);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (then))
|
||||
{
|
||||
/* 9. */
|
||||
then = jcontext_take_exception ();
|
||||
ecma_reject_promise (promise, then);
|
||||
}
|
||||
else if (!ecma_op_is_callable (then))
|
||||
{
|
||||
/* 11 .*/
|
||||
ecma_fulfill_promise (promise, argv[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 12 */
|
||||
ecma_enqueue_promise_resolve_thenable_job (promise, argv[0], then);
|
||||
}
|
||||
|
||||
ecma_free_value (then);
|
||||
|
||||
end_of_resolve_function:
|
||||
ecma_free_value (promise);
|
||||
|
||||
return ECMA_VALUE_UNDEFINED;
|
||||
} /* ecma_promise_resolve_handler */
|
||||
|
||||
@ -963,6 +954,31 @@ ecma_promise_then (ecma_value_t promise, /**< the promise which call 'then' */
|
||||
return ret;
|
||||
} /* ecma_promise_then */
|
||||
|
||||
/**
|
||||
* Resume the execution of an async function after the promise is resolved
|
||||
*/
|
||||
void
|
||||
ecma_promise_async_then (ecma_value_t promise, /**< promise object */
|
||||
ecma_value_t executable_object) /**< executable object of the async function */
|
||||
{
|
||||
ecma_object_t *promise_obj_p = ecma_get_object_from_value (promise);
|
||||
uint16_t flags = ecma_promise_get_flags (promise_obj_p);
|
||||
|
||||
if (flags & ECMA_PROMISE_IS_PENDING)
|
||||
{
|
||||
ecma_value_t executable_object_with_tag;
|
||||
ECMA_SET_NON_NULL_POINTER_TAG (executable_object_with_tag, ecma_get_object_from_value (executable_object), 0);
|
||||
ECMA_SET_THIRD_BIT_TO_POINTER_TAG (executable_object_with_tag);
|
||||
|
||||
ecma_collection_push_back (((ecma_promise_object_t *) promise_obj_p)->reactions, executable_object_with_tag);
|
||||
return;
|
||||
}
|
||||
|
||||
ecma_value_t value = ecma_promise_get_result (promise_obj_p);
|
||||
ecma_enqueue_promise_async_reaction_job (executable_object, value, !(flags & ECMA_PROMISE_IS_FULFILLED));
|
||||
ecma_free_value (value);
|
||||
} /* ecma_promise_async_then */
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
|
||||
@ -86,9 +86,12 @@ bool ecma_is_promise (ecma_object_t *obj_p);
|
||||
ecma_value_t ecma_op_create_promise_object (ecma_value_t executor, ecma_promise_executor_type_t type);
|
||||
uint16_t ecma_promise_get_flags (ecma_object_t *promise_p);
|
||||
ecma_value_t ecma_promise_get_result (ecma_object_t *promise_p);
|
||||
void ecma_reject_promise (ecma_value_t promise, ecma_value_t reason);
|
||||
void ecma_fulfill_promise (ecma_value_t promise, ecma_value_t value);
|
||||
ecma_value_t ecma_promise_new_capability (ecma_value_t constructor);
|
||||
ecma_value_t ecma_promise_reject_or_resolve (ecma_value_t this_arg, ecma_value_t value, bool is_resolve);
|
||||
ecma_value_t ecma_promise_then (ecma_value_t promise, ecma_value_t on_fulfilled, ecma_value_t on_rejected);
|
||||
void ecma_promise_async_then (ecma_value_t promise, ecma_value_t executable_object);
|
||||
void ecma_promise_create_resolving_functions (ecma_object_t *object_p, ecma_promise_resolving_functions_t *funcs,
|
||||
bool create_already_resolved);
|
||||
void ecma_promise_free_resolving_functions (ecma_promise_resolving_functions_t *funcs);
|
||||
|
||||
@ -30,7 +30,7 @@ extern "C"
|
||||
/**
|
||||
* Jerry snapshot format version.
|
||||
*/
|
||||
#define JERRY_SNAPSHOT_VERSION (48u)
|
||||
#define JERRY_SNAPSHOT_VERSION (49u)
|
||||
|
||||
/**
|
||||
* Flags for jerry_generate_snapshot and jerry_generate_function_snapshot.
|
||||
|
||||
@ -728,12 +728,12 @@
|
||||
VM_OC_YIELD) \
|
||||
CBC_OPCODE (CBC_EXT_AWAIT, CBC_NO_FLAG, 0, \
|
||||
VM_OC_AWAIT) \
|
||||
CBC_OPCODE (CBC_EXT_ASYNC_EXIT, CBC_NO_FLAG, 0, \
|
||||
VM_OC_ASYNC_EXIT) \
|
||||
CBC_OPCODE (CBC_EXT_RETURN, CBC_NO_FLAG, -1, \
|
||||
VM_OC_EXT_RETURN | VM_OC_GET_STACK) \
|
||||
CBC_OPCODE (CBC_EXT_RETURN_PROMISE, CBC_NO_FLAG, -1, \
|
||||
VM_OC_RETURN_PROMISE | VM_OC_GET_STACK) \
|
||||
CBC_OPCODE (CBC_EXT_RETURN_PROMISE_UNDEFINED, CBC_NO_FLAG, 0, \
|
||||
VM_OC_RETURN_PROMISE) \
|
||||
CBC_OPCODE (CBC_EXT_RETURN_UNDEFINED, CBC_NO_FLAG, 0, \
|
||||
VM_OC_EXT_RETURN) \
|
||||
CBC_OPCODE (CBC_EXT_PUSH_NEW_TARGET, CBC_NO_FLAG, 1, \
|
||||
VM_OC_PUSH_NEW_TARGET | VM_OC_PUT_STACK) \
|
||||
\
|
||||
@ -843,6 +843,7 @@ typedef enum
|
||||
|
||||
/* The following functions cannot be constructed (see CBC_FUNCTION_IS_CONSTRUCTABLE) */
|
||||
CBC_FUNCTION_GENERATOR, /**< generator function */
|
||||
CBC_FUNCTION_ASYNC, /**< async function */
|
||||
|
||||
/* The following functions has no prototype (see CBC_FUNCTION_HAS_PROTOTYPE) */
|
||||
CBC_FUNCTION_ARROW, /**< arrow function */
|
||||
|
||||
@ -3017,7 +3017,7 @@ parser_parse_statements (parser_context_t *context_p) /**< context */
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION)
|
||||
{
|
||||
parser_emit_cbc_ext (context_p, CBC_EXT_RETURN_PROMISE_UNDEFINED);
|
||||
parser_emit_cbc_ext (context_p, CBC_EXT_RETURN_UNDEFINED);
|
||||
break;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
@ -3028,14 +3028,6 @@ parser_parse_statements (parser_context_t *context_p) /**< context */
|
||||
|
||||
parser_parse_expression (context_p, PARSE_EXPR);
|
||||
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION)
|
||||
{
|
||||
parser_emit_cbc_ext (context_p, CBC_EXT_RETURN_PROMISE);
|
||||
break;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
|
||||
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
|
||||
{
|
||||
context_p->last_cbc_opcode = CBC_RETURN_WITH_LITERAL;
|
||||
|
||||
@ -687,6 +687,11 @@ parse_print_final_cbc (ecma_compiled_code_t *compiled_code_p, /**< compiled code
|
||||
JERRY_DEBUG_MSG (",generator");
|
||||
break;
|
||||
}
|
||||
case CBC_FUNCTION_ASYNC:
|
||||
{
|
||||
JERRY_DEBUG_MSG (",async");
|
||||
break;
|
||||
}
|
||||
case CBC_FUNCTION_ARROW:
|
||||
{
|
||||
JERRY_DEBUG_MSG (",arrow");
|
||||
@ -948,6 +953,23 @@ parser_post_processing (parser_context_t *context_p) /**< context */
|
||||
parser_branch_t branch;
|
||||
parser_stack_pop (context_p, &branch, sizeof (parser_branch_t));
|
||||
parser_set_branch_to_current_position (context_p, &branch);
|
||||
|
||||
JERRY_ASSERT (!(context_p->status_flags & PARSER_NO_END_LABEL));
|
||||
}
|
||||
|
||||
if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION)
|
||||
{
|
||||
PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION);
|
||||
#ifndef JERRY_NDEBUG
|
||||
PARSER_MINUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION);
|
||||
#endif /* !JERRY_NDEBUG */
|
||||
|
||||
parser_branch_t branch;
|
||||
|
||||
parser_stack_pop (context_p, &branch, sizeof (parser_branch_t));
|
||||
parser_set_branch_to_current_position (context_p, &branch);
|
||||
|
||||
JERRY_ASSERT (!(context_p->status_flags & PARSER_NO_END_LABEL));
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
|
||||
@ -1031,14 +1053,6 @@ parser_post_processing (parser_context_t *context_p) /**< context */
|
||||
PARSER_NEXT_BYTE (page_p, offset);
|
||||
length++;
|
||||
|
||||
#if ENABLED (JERRY_ESNEXT)
|
||||
if (ext_opcode == CBC_EXT_RETURN_PROMISE
|
||||
|| ext_opcode == CBC_EXT_RETURN_PROMISE_UNDEFINED)
|
||||
{
|
||||
last_opcode = CBC_RETURN;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
|
||||
#if ENABLED (JERRY_LINE_INFO)
|
||||
if (ext_opcode == CBC_EXT_LINE)
|
||||
{
|
||||
@ -1339,14 +1353,18 @@ parser_post_processing (parser_context_t *context_p) /**< context */
|
||||
{
|
||||
compiled_code_p->status_flags |= CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_ARROW);
|
||||
}
|
||||
else if (context_p->status_flags & PARSER_CLASS_CONSTRUCTOR)
|
||||
{
|
||||
compiled_code_p->status_flags |= CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_CONSTRUCTOR);
|
||||
}
|
||||
else if (context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION)
|
||||
{
|
||||
compiled_code_p->status_flags |= CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_GENERATOR);
|
||||
}
|
||||
else if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION)
|
||||
{
|
||||
compiled_code_p->status_flags |= CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_ASYNC);
|
||||
}
|
||||
else if (context_p->status_flags & PARSER_CLASS_CONSTRUCTOR)
|
||||
{
|
||||
compiled_code_p->status_flags |= CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_CONSTRUCTOR);
|
||||
}
|
||||
else
|
||||
{
|
||||
compiled_code_p->status_flags |= CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_NORMAL);
|
||||
@ -1562,7 +1580,7 @@ parser_post_processing (parser_context_t *context_p) /**< context */
|
||||
if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION)
|
||||
{
|
||||
dst_p[-1] = CBC_EXT_OPCODE;
|
||||
dst_p[0] = CBC_EXT_RETURN_PROMISE_UNDEFINED;
|
||||
dst_p[0] = CBC_EXT_ASYNC_EXIT;
|
||||
dst_p++;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
@ -1728,6 +1746,17 @@ parser_parse_function_arguments (parser_context_t *context_p, /**< context */
|
||||
{
|
||||
context_p->status_flags &= (uint32_t) ~PARSER_IS_ASYNC_FUNCTION;
|
||||
}
|
||||
|
||||
if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION)
|
||||
{
|
||||
parser_branch_t branch;
|
||||
parser_emit_cbc_ext_forward_branch (context_p, CBC_EXT_TRY_CREATE_CONTEXT, &branch);
|
||||
parser_stack_push (context_p, &branch, sizeof (parser_branch_t));
|
||||
|
||||
#ifndef JERRY_NDEBUG
|
||||
context_p->context_stack_depth = PARSER_TRY_CONTEXT_STACK_ALLOCATION;
|
||||
#endif /* !JERRY_NDEBUG */
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ESNEXT) */
|
||||
|
||||
if (context_p->token.type == end_type)
|
||||
@ -1968,7 +1997,7 @@ parser_parse_function_arguments (parser_context_t *context_p, /**< context */
|
||||
parser_stack_push (context_p, &branch, sizeof (parser_branch_t));
|
||||
|
||||
#ifndef JERRY_NDEBUG
|
||||
context_p->context_stack_depth = PARSER_BLOCK_CONTEXT_STACK_ALLOCATION;
|
||||
PARSER_PLUS_EQUAL_U16 (context_p->context_stack_depth, PARSER_BLOCK_CONTEXT_STACK_ALLOCATION);
|
||||
#endif /* !JERRY_NDEBUG */
|
||||
}
|
||||
else
|
||||
@ -2570,20 +2599,13 @@ parser_parse_arrow_function (parser_context_t *context_p, /**< context */
|
||||
|
||||
parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA);
|
||||
|
||||
if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION)
|
||||
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
|
||||
{
|
||||
parser_emit_cbc_ext (context_p, CBC_EXT_RETURN_PROMISE);
|
||||
context_p->last_cbc_opcode = CBC_RETURN_WITH_LITERAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL)
|
||||
{
|
||||
context_p->last_cbc_opcode = CBC_RETURN_WITH_LITERAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
parser_emit_cbc (context_p, CBC_RETURN);
|
||||
}
|
||||
parser_emit_cbc (context_p, CBC_RETURN);
|
||||
}
|
||||
parser_flush_cbc (context_p);
|
||||
|
||||
|
||||
@ -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.
|
||||
*
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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:
|
||||
{
|
||||
|
||||
@ -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 */
|
||||
|
||||
166
tests/jerry/es.next/function-await2.js
Normal file
166
tests/jerry/es.next/function-await2.js
Normal file
@ -0,0 +1,166 @@
|
||||
// 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.
|
||||
|
||||
var successCount = 0
|
||||
var p, r
|
||||
|
||||
// Test 1
|
||||
|
||||
async function f1(p)
|
||||
{
|
||||
assert(await p === 1)
|
||||
return 2
|
||||
}
|
||||
|
||||
p = new Promise(function(resolve, reject) { r = resolve })
|
||||
|
||||
f1(p).then(function (v) {
|
||||
assert(v === 2)
|
||||
successCount++
|
||||
})
|
||||
|
||||
r(1)
|
||||
|
||||
// Test 2
|
||||
|
||||
var f2 = async(p) =>
|
||||
{
|
||||
assert(await p === 3)
|
||||
}
|
||||
|
||||
p = new Promise(function(resolve, reject) { r = resolve })
|
||||
|
||||
f2(p).then(function (v) {
|
||||
assert(v === undefined)
|
||||
successCount++
|
||||
})
|
||||
|
||||
r(3)
|
||||
|
||||
// Test 3
|
||||
|
||||
var thenableCounter = 0
|
||||
|
||||
async function f3()
|
||||
{
|
||||
return new Promise(function(resolve) { resolve(f3) })
|
||||
}
|
||||
|
||||
f3.then = function(resolve) {
|
||||
// Repeating resolve with 'then'
|
||||
if (++thenableCounter < 5) {
|
||||
resolve(f3)
|
||||
} else {
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
|
||||
f3()
|
||||
|
||||
// Test 4
|
||||
|
||||
async function f4(p)
|
||||
{
|
||||
try {
|
||||
throw 4
|
||||
} catch (e) {
|
||||
throw 5
|
||||
}
|
||||
}
|
||||
|
||||
p = new Promise(function(resolve, reject) { r = resolve })
|
||||
|
||||
f4(p).then(undefined, function (v) {
|
||||
assert(v === 5)
|
||||
successCount++
|
||||
})
|
||||
|
||||
r(1)
|
||||
|
||||
// Test 5
|
||||
|
||||
async function f5(p)
|
||||
{
|
||||
try {
|
||||
return 6
|
||||
} finally {
|
||||
throw 7
|
||||
}
|
||||
}
|
||||
|
||||
p = new Promise(function(resolve, reject) { r = resolve })
|
||||
|
||||
f5(p).then(undefined, function (v) {
|
||||
assert(v === 7)
|
||||
successCount++
|
||||
})
|
||||
|
||||
r(1)
|
||||
|
||||
// Test 6
|
||||
|
||||
p = new Promise(function(resolve, reject) { r = resolve })
|
||||
|
||||
async function f6(p)
|
||||
{
|
||||
await p
|
||||
return self
|
||||
}
|
||||
|
||||
var self = f6()
|
||||
|
||||
self.then(undefined, function (v) {
|
||||
assert(v instanceof TypeError)
|
||||
successCount++
|
||||
})
|
||||
|
||||
r(1)
|
||||
|
||||
// Test 7
|
||||
|
||||
async function f7(p)
|
||||
{
|
||||
var x = {}
|
||||
assert((await x) === x)
|
||||
|
||||
x = 3.14
|
||||
assert((await x) === x)
|
||||
|
||||
x = "Test string"
|
||||
assert((await x) === x)
|
||||
|
||||
successCount++
|
||||
}
|
||||
f7();
|
||||
|
||||
// Test 8
|
||||
|
||||
async function f8() {
|
||||
var p = new Promise(function() {});
|
||||
Object.defineProperty(p, 'constructor', { get() { throw "Error!" } });
|
||||
|
||||
await p
|
||||
}
|
||||
|
||||
f8().then(undefined, function (v) {
|
||||
assert(v === "Error!")
|
||||
successCount++
|
||||
})
|
||||
|
||||
// END
|
||||
|
||||
function __checkAsync() {
|
||||
assert(successCount === 8)
|
||||
assert(thenableCounter === 5)
|
||||
}
|
||||
69
tests/jerry/es.next/function-await3.js
Normal file
69
tests/jerry/es.next/function-await3.js
Normal file
@ -0,0 +1,69 @@
|
||||
// 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.
|
||||
|
||||
var successCount = 0
|
||||
|
||||
async function f1()
|
||||
{
|
||||
throw 1
|
||||
}
|
||||
|
||||
async function f2()
|
||||
{
|
||||
try {
|
||||
assert(await f1() && false)
|
||||
} catch (e) {
|
||||
assert(e === 1)
|
||||
return 2
|
||||
} finally {
|
||||
return 3
|
||||
}
|
||||
}
|
||||
|
||||
async function f3()
|
||||
{
|
||||
return await f2() + 1
|
||||
}
|
||||
|
||||
async function f4()
|
||||
{
|
||||
return await f1()
|
||||
}
|
||||
|
||||
async function f5()
|
||||
{
|
||||
var o = { a:f2, b:f2, c:f2, d:f2 }
|
||||
|
||||
for (i in o) {
|
||||
var p1 = f3()
|
||||
var p2 = f4()
|
||||
|
||||
assert(await o[i]() === 3)
|
||||
assert(await p1 === 4)
|
||||
|
||||
try {
|
||||
assert(await p2 && false)
|
||||
} catch (e) {
|
||||
assert(e === 1)
|
||||
}
|
||||
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
|
||||
f5()
|
||||
|
||||
function __checkAsync() {
|
||||
assert(successCount === 4)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user