Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion jerry-core/ecma/operations/ecma-jobqueue.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ ecma_process_promise_resolve_thenable_job (ecma_job_promise_resolve_thenable_t *
{
ecma_object_t *promise_p = ecma_get_object_from_value (job_p->promise);
ecma_promise_resolving_functions_t funcs;
ecma_promise_create_resolving_functions (promise_p, &funcs);
ecma_promise_create_resolving_functions (promise_p, &funcs, true);

ecma_string_t *str_resolve_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_RESOLVE_FUNCTION);
ecma_string_t *str_reject_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_REJECT_FUNCTION);
Expand Down
77 changes: 65 additions & 12 deletions jerry-core/ecma/operations/ecma-promise-object.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,38 @@ ecma_promise_trigger_reactions (ecma_collection_t *reactions, /**< lists of reac
}
} /* ecma_promise_trigger_reactions */

/**
* Checks whether a resolver is called before.
*
* @return true if it was called before, false otherwise
*/
static bool
ecma_is_resolver_already_called (ecma_object_t *resolver_p, /**< resolver */
ecma_object_t *promise_obj_p) /**< promise */
{
ecma_string_t *str_already_resolved_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ALREADY_RESOLVED);
ecma_property_t *property_p = ecma_find_named_property (resolver_p, str_already_resolved_p);

if (property_p == NULL)
{
return (ecma_promise_get_flags (promise_obj_p) & ECMA_PROMISE_ALREADY_RESOLVED) != 0;
}

JERRY_ASSERT (ECMA_PROPERTY_GET_TYPE (*property_p) == ECMA_PROPERTY_TYPE_NAMEDDATA);

ecma_value_t already_resolved = ECMA_PROPERTY_VALUE_PTR (property_p)->value;
ecma_object_t *object_p = ecma_get_object_from_value (already_resolved);
JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_CLASS);

ecma_extended_object_t *already_resolved_p = (ecma_extended_object_t *) object_p;
JERRY_ASSERT (already_resolved_p->u.class_prop.class_id == LIT_MAGIC_STRING_BOOLEAN_UL);

ecma_value_t current_value = already_resolved_p->u.class_prop.u.value;
already_resolved_p->u.class_prop.u.value = ECMA_VALUE_TRUE;

return current_value == ECMA_VALUE_TRUE;
} /* ecma_is_resolver_already_called */

/**
* Reject a Promise with a reason.
*
Expand Down Expand Up @@ -242,18 +274,16 @@ ecma_promise_reject_handler (const ecma_value_t function, /**< the function itse
JERRY_ASSERT (ecma_is_promise (promise_obj_p));

/* 3., 4. */
if (ecma_promise_get_flags (promise_obj_p) & ECMA_PROMISE_ALREADY_RESOLVED)
if (!ecma_is_resolver_already_called (function_p, promise_obj_p))
{
ecma_free_value (promise);
return ECMA_VALUE_UNDEFINED;
}
/* 5. */
((ecma_extended_object_t *) promise_obj_p)->u.class_prop.extra_info |= ECMA_PROMISE_ALREADY_RESOLVED;

/* 5. */
((ecma_extended_object_t *) promise_obj_p)->u.class_prop.extra_info |= ECMA_PROMISE_ALREADY_RESOLVED;
/* 6. */
ecma_value_t reject_value = (argc == 0) ? ECMA_VALUE_UNDEFINED : argv[0];
ecma_reject_promise (promise, reject_value);
}

/* 6. */
ecma_value_t reject_value = (argc == 0) ? ECMA_VALUE_UNDEFINED : argv[0];
ecma_reject_promise (promise, reject_value);
ecma_free_value (promise);
return ECMA_VALUE_UNDEFINED;
} /* ecma_promise_reject_handler */
Expand Down Expand Up @@ -281,7 +311,7 @@ ecma_promise_resolve_handler (const ecma_value_t function, /**< the function its
JERRY_ASSERT (ecma_is_promise (promise_obj_p));

/* 3., 4. */
if (ecma_promise_get_flags (promise_obj_p) & ECMA_PROMISE_ALREADY_RESOLVED)
if (ecma_is_resolver_already_called (function_p, promise_obj_p))
{
goto end_of_resolve_function;
}
Expand Down Expand Up @@ -428,7 +458,8 @@ ecma_promise_create_resolving_functions_helper (ecma_object_t *obj_p, /**< Promi
*/
void
ecma_promise_create_resolving_functions (ecma_object_t *object_p, /**< the promise object */
ecma_promise_resolving_functions_t *funcs) /**< [out] resolving functions */
ecma_promise_resolving_functions_t *funcs, /**< [out] resolving functions */
bool create_already_resolved) /**< create already resolved flag */
{
/* 2. - 4. */
funcs->resolve = ecma_promise_create_resolving_functions_helper (object_p,
Expand All @@ -437,6 +468,28 @@ ecma_promise_create_resolving_functions (ecma_object_t *object_p, /**< the promi
/* 5. - 7. */
funcs->reject = ecma_promise_create_resolving_functions_helper (object_p,
ecma_promise_reject_handler);
if (!create_already_resolved)
{
return;
}

ecma_value_t already_resolved = ecma_op_create_boolean_object (ECMA_VALUE_FALSE);
ecma_string_t *str_already_resolved_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ALREADY_RESOLVED);
ecma_property_value_t *value_p;

value_p = ecma_create_named_data_property (ecma_get_object_from_value (funcs->resolve),
str_already_resolved_p,
ECMA_PROPERTY_FIXED,
NULL);
value_p->value = already_resolved;

value_p = ecma_create_named_data_property (ecma_get_object_from_value (funcs->reject),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to have the same kind of test for "reject" handler like the "resolve" one has?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A test case just like the one added in this PR. But probably reject behaves a bit differently.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I get it now. Reject cannot be "delayed", that is a special feature of "resolve". Probably it would be too easy to start an infinite reject loop :)

str_already_resolved_p,
ECMA_PROPERTY_FIXED,
NULL);
value_p->value = already_resolved;

ecma_free_value (already_resolved);
} /* ecma_promise_create_resolving_functions */

/**
Expand Down Expand Up @@ -490,7 +543,7 @@ ecma_op_create_promise_object (ecma_value_t executor, /**< the executor function
promise_object_p->reactions = reactions;
/* 8. */
ecma_promise_resolving_functions_t funcs;
ecma_promise_create_resolving_functions (object_p, &funcs);
ecma_promise_create_resolving_functions (object_p, &funcs, false);

ecma_string_t *str_resolve_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_RESOLVE_FUNCTION);
ecma_string_t *str_reject_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_REJECT_FUNCTION);
Expand Down
3 changes: 2 additions & 1 deletion jerry-core/ecma/operations/ecma-promise-object.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ ecma_value_t ecma_promise_get_result (ecma_object_t *promise_p);
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_create_resolving_functions (ecma_object_t *object_p, ecma_promise_resolving_functions_t *funcs);
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);

/**
Expand Down
1 change: 1 addition & 0 deletions jerry-core/lit/lit-magic-strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ typedef enum
LIT_NON_INTERNAL_MAGIC_STRING__COUNT, /**< number of non-internal magic strings */
LIT_INTERNAL_MAGIC_STRING_PROMISE = LIT_NON_INTERNAL_MAGIC_STRING__COUNT, /**< [[Promise]] of promise
* reject or resolve functions */
LIT_INTERNAL_MAGIC_STRING_ALREADY_RESOLVED, /**< [[AlreadyResolved]] of promise reject or resolve functions */
LIT_INTERNAL_MAGIC_STRING_RESOLVE_FUNCTION, /**< the resolve funtion of the promise object */
LIT_INTERNAL_MAGIC_STRING_REJECT_FUNCTION, /**< the reject function of the promise object */
LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_PROMISE, /**< [[Promise]] property */
Expand Down
32 changes: 32 additions & 0 deletions tests/jerry/es2015/promise-thenable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* 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 counter = 0;

function f() { }

f.then = function(resolve) {
if (++counter < 10) {
resolve(f)
}
}

new Promise(function(resolve) {
resolve(f)
})

function __checkAsync() {
assert(counter == 10)
}