Skip to content

Commit 562dcc8

Browse files
authored
Fix Promise thenable. (#3799)
A single promise can be resolved any number of times due to thenable functions, but each resolver pair can only be called once. JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg [email protected]
1 parent c2b6621 commit 562dcc8

File tree

5 files changed

+101
-14
lines changed

5 files changed

+101
-14
lines changed

jerry-core/ecma/operations/ecma-jobqueue.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ ecma_process_promise_resolve_thenable_job (ecma_job_promise_resolve_thenable_t *
209209
{
210210
ecma_object_t *promise_p = ecma_get_object_from_value (job_p->promise);
211211
ecma_promise_resolving_functions_t funcs;
212-
ecma_promise_create_resolving_functions (promise_p, &funcs);
212+
ecma_promise_create_resolving_functions (promise_p, &funcs, true);
213213

214214
ecma_string_t *str_resolve_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_RESOLVE_FUNCTION);
215215
ecma_string_t *str_reject_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_REJECT_FUNCTION);

jerry-core/ecma/operations/ecma-promise-object.c

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,38 @@ ecma_promise_trigger_reactions (ecma_collection_t *reactions, /**< lists of reac
161161
}
162162
} /* ecma_promise_trigger_reactions */
163163

164+
/**
165+
* Checks whether a resolver is called before.
166+
*
167+
* @return true if it was called before, false otherwise
168+
*/
169+
static bool
170+
ecma_is_resolver_already_called (ecma_object_t *resolver_p, /**< resolver */
171+
ecma_object_t *promise_obj_p) /**< promise */
172+
{
173+
ecma_string_t *str_already_resolved_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ALREADY_RESOLVED);
174+
ecma_property_t *property_p = ecma_find_named_property (resolver_p, str_already_resolved_p);
175+
176+
if (property_p == NULL)
177+
{
178+
return (ecma_promise_get_flags (promise_obj_p) & ECMA_PROMISE_ALREADY_RESOLVED) != 0;
179+
}
180+
181+
JERRY_ASSERT (ECMA_PROPERTY_GET_TYPE (*property_p) == ECMA_PROPERTY_TYPE_NAMEDDATA);
182+
183+
ecma_value_t already_resolved = ECMA_PROPERTY_VALUE_PTR (property_p)->value;
184+
ecma_object_t *object_p = ecma_get_object_from_value (already_resolved);
185+
JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_CLASS);
186+
187+
ecma_extended_object_t *already_resolved_p = (ecma_extended_object_t *) object_p;
188+
JERRY_ASSERT (already_resolved_p->u.class_prop.class_id == LIT_MAGIC_STRING_BOOLEAN_UL);
189+
190+
ecma_value_t current_value = already_resolved_p->u.class_prop.u.value;
191+
already_resolved_p->u.class_prop.u.value = ECMA_VALUE_TRUE;
192+
193+
return current_value == ECMA_VALUE_TRUE;
194+
} /* ecma_is_resolver_already_called */
195+
164196
/**
165197
* Reject a Promise with a reason.
166198
*
@@ -242,18 +274,16 @@ ecma_promise_reject_handler (const ecma_value_t function, /**< the function itse
242274
JERRY_ASSERT (ecma_is_promise (promise_obj_p));
243275

244276
/* 3., 4. */
245-
if (ecma_promise_get_flags (promise_obj_p) & ECMA_PROMISE_ALREADY_RESOLVED)
277+
if (!ecma_is_resolver_already_called (function_p, promise_obj_p))
246278
{
247-
ecma_free_value (promise);
248-
return ECMA_VALUE_UNDEFINED;
249-
}
279+
/* 5. */
280+
((ecma_extended_object_t *) promise_obj_p)->u.class_prop.extra_info |= ECMA_PROMISE_ALREADY_RESOLVED;
250281

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

254-
/* 6. */
255-
ecma_value_t reject_value = (argc == 0) ? ECMA_VALUE_UNDEFINED : argv[0];
256-
ecma_reject_promise (promise, reject_value);
257287
ecma_free_value (promise);
258288
return ECMA_VALUE_UNDEFINED;
259289
} /* ecma_promise_reject_handler */
@@ -281,7 +311,7 @@ ecma_promise_resolve_handler (const ecma_value_t function, /**< the function its
281311
JERRY_ASSERT (ecma_is_promise (promise_obj_p));
282312

283313
/* 3., 4. */
284-
if (ecma_promise_get_flags (promise_obj_p) & ECMA_PROMISE_ALREADY_RESOLVED)
314+
if (ecma_is_resolver_already_called (function_p, promise_obj_p))
285315
{
286316
goto end_of_resolve_function;
287317
}
@@ -428,7 +458,8 @@ ecma_promise_create_resolving_functions_helper (ecma_object_t *obj_p, /**< Promi
428458
*/
429459
void
430460
ecma_promise_create_resolving_functions (ecma_object_t *object_p, /**< the promise object */
431-
ecma_promise_resolving_functions_t *funcs) /**< [out] resolving functions */
461+
ecma_promise_resolving_functions_t *funcs, /**< [out] resolving functions */
462+
bool create_already_resolved) /**< create already resolved flag */
432463
{
433464
/* 2. - 4. */
434465
funcs->resolve = ecma_promise_create_resolving_functions_helper (object_p,
@@ -437,6 +468,28 @@ ecma_promise_create_resolving_functions (ecma_object_t *object_p, /**< the promi
437468
/* 5. - 7. */
438469
funcs->reject = ecma_promise_create_resolving_functions_helper (object_p,
439470
ecma_promise_reject_handler);
471+
if (!create_already_resolved)
472+
{
473+
return;
474+
}
475+
476+
ecma_value_t already_resolved = ecma_op_create_boolean_object (ECMA_VALUE_FALSE);
477+
ecma_string_t *str_already_resolved_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ALREADY_RESOLVED);
478+
ecma_property_value_t *value_p;
479+
480+
value_p = ecma_create_named_data_property (ecma_get_object_from_value (funcs->resolve),
481+
str_already_resolved_p,
482+
ECMA_PROPERTY_FIXED,
483+
NULL);
484+
value_p->value = already_resolved;
485+
486+
value_p = ecma_create_named_data_property (ecma_get_object_from_value (funcs->reject),
487+
str_already_resolved_p,
488+
ECMA_PROPERTY_FIXED,
489+
NULL);
490+
value_p->value = already_resolved;
491+
492+
ecma_free_value (already_resolved);
440493
} /* ecma_promise_create_resolving_functions */
441494

442495
/**
@@ -490,7 +543,7 @@ ecma_op_create_promise_object (ecma_value_t executor, /**< the executor function
490543
promise_object_p->reactions = reactions;
491544
/* 8. */
492545
ecma_promise_resolving_functions_t funcs;
493-
ecma_promise_create_resolving_functions (object_p, &funcs);
546+
ecma_promise_create_resolving_functions (object_p, &funcs, false);
494547

495548
ecma_string_t *str_resolve_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_RESOLVE_FUNCTION);
496549
ecma_string_t *str_reject_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_REJECT_FUNCTION);

jerry-core/ecma/operations/ecma-promise-object.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ ecma_value_t ecma_promise_get_result (ecma_object_t *promise_p);
8989
ecma_value_t ecma_promise_new_capability (ecma_value_t constructor);
9090
ecma_value_t ecma_promise_reject_or_resolve (ecma_value_t this_arg, ecma_value_t value, bool is_resolve);
9191
ecma_value_t ecma_promise_then (ecma_value_t promise, ecma_value_t on_fulfilled, ecma_value_t on_rejected);
92-
void ecma_promise_create_resolving_functions (ecma_object_t *object_p, ecma_promise_resolving_functions_t *funcs);
92+
void ecma_promise_create_resolving_functions (ecma_object_t *object_p, ecma_promise_resolving_functions_t *funcs,
93+
bool create_already_resolved);
9394
void ecma_promise_free_resolving_functions (ecma_promise_resolving_functions_t *funcs);
9495

9596
/**

jerry-core/lit/lit-magic-strings.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ typedef enum
3434
LIT_NON_INTERNAL_MAGIC_STRING__COUNT, /**< number of non-internal magic strings */
3535
LIT_INTERNAL_MAGIC_STRING_PROMISE = LIT_NON_INTERNAL_MAGIC_STRING__COUNT, /**< [[Promise]] of promise
3636
* reject or resolve functions */
37+
LIT_INTERNAL_MAGIC_STRING_ALREADY_RESOLVED, /**< [[AlreadyResolved]] of promise reject or resolve functions */
3738
LIT_INTERNAL_MAGIC_STRING_RESOLVE_FUNCTION, /**< the resolve funtion of the promise object */
3839
LIT_INTERNAL_MAGIC_STRING_REJECT_FUNCTION, /**< the reject function of the promise object */
3940
LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_PROMISE, /**< [[Promise]] property */
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/* Copyright JS Foundation and other contributors, http://js.foundation
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
var counter = 0;
17+
18+
function f() { }
19+
20+
f.then = function(resolve) {
21+
if (++counter < 10) {
22+
resolve(f)
23+
}
24+
}
25+
26+
new Promise(function(resolve) {
27+
resolve(f)
28+
})
29+
30+
function __checkAsync() {
31+
assert(counter == 10)
32+
}

0 commit comments

Comments
 (0)