Skip to content

Commit 130af1d

Browse files
committed
Fix Promise thenable.
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 dd6d148 commit 130af1d

File tree

5 files changed

+97
-8
lines changed

5 files changed

+97
-8
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: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,37 @@ ecma_promise_trigger_reactions (ecma_collection_t *reactions, /**< lists of reac
161161
}
162162
} /* ecma_promise_trigger_reactions */
163163

164+
static bool
165+
ecma_is_resolver_already_called (ecma_object_t *resolver_p, /**< resolver */
166+
ecma_object_t *promise_obj_p) /**< promise */
167+
{
168+
ecma_string_t *str_already_resolved_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ALREADY_RESOLVED);
169+
ecma_property_t *property_p = ecma_find_named_property (resolver_p, str_already_resolved_p);
170+
171+
if (property_p == NULL)
172+
{
173+
return (ecma_promise_get_flags (promise_obj_p) & ECMA_PROMISE_ALREADY_RESOLVED) != 0;
174+
}
175+
176+
JERRY_ASSERT (ECMA_PROPERTY_GET_TYPE (*property_p) == ECMA_PROPERTY_TYPE_NAMEDDATA);
177+
178+
ecma_value_t already_resolved = ECMA_PROPERTY_VALUE_PTR (property_p)->value;
179+
180+
ecma_object_t *object_p = ecma_get_object_from_value (already_resolved);
181+
182+
JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_CLASS);
183+
184+
ecma_extended_object_t *already_resolved_p = (ecma_extended_object_t *) object_p;
185+
186+
JERRY_ASSERT (already_resolved_p->u.class_prop.class_id == LIT_MAGIC_STRING_BOOLEAN_UL);
187+
188+
ecma_value_t current_value = already_resolved_p->u.class_prop.u.value;
189+
190+
already_resolved_p->u.class_prop.u.value = ECMA_VALUE_TRUE;
191+
192+
return current_value == ECMA_VALUE_TRUE;
193+
} /* ecma_is_resolver_already_called */
194+
164195
/**
165196
* Reject a Promise with a reason.
166197
*
@@ -242,10 +273,9 @@ ecma_promise_reject_handler (const ecma_value_t function, /**< the function itse
242273
JERRY_ASSERT (ecma_is_promise (promise_obj_p));
243274

244275
/* 3., 4. */
245-
if (ecma_promise_get_flags (promise_obj_p) & ECMA_PROMISE_ALREADY_RESOLVED)
276+
if (ecma_is_resolver_already_called (function_p, promise_obj_p))
246277
{
247-
ecma_free_value (promise);
248-
return ECMA_VALUE_UNDEFINED;
278+
goto end_of_resolve_function;
249279
}
250280

251281
/* 5. */
@@ -254,6 +284,8 @@ ecma_promise_reject_handler (const ecma_value_t function, /**< the function itse
254284
/* 6. */
255285
ecma_value_t reject_value = (argc == 0) ? ECMA_VALUE_UNDEFINED : argv[0];
256286
ecma_reject_promise (promise, reject_value);
287+
288+
end_of_resolve_function:
257289
ecma_free_value (promise);
258290
return ECMA_VALUE_UNDEFINED;
259291
} /* ecma_promise_reject_handler */
@@ -281,7 +313,7 @@ ecma_promise_resolve_handler (const ecma_value_t function, /**< the function its
281313
JERRY_ASSERT (ecma_is_promise (promise_obj_p));
282314

283315
/* 3., 4. */
284-
if (ecma_promise_get_flags (promise_obj_p) & ECMA_PROMISE_ALREADY_RESOLVED)
316+
if (ecma_is_resolver_already_called (function_p, promise_obj_p))
285317
{
286318
goto end_of_resolve_function;
287319
}
@@ -428,7 +460,8 @@ ecma_promise_create_resolving_functions_helper (ecma_object_t *obj_p, /**< Promi
428460
*/
429461
void
430462
ecma_promise_create_resolving_functions (ecma_object_t *object_p, /**< the promise object */
431-
ecma_promise_resolving_functions_t *funcs) /**< [out] resolving functions */
463+
ecma_promise_resolving_functions_t *funcs, /**< [out] resolving functions */
464+
bool create_already_resolved) /**< create already resolved flag */
432465
{
433466
/* 2. - 4. */
434467
funcs->resolve = ecma_promise_create_resolving_functions_helper (object_p,
@@ -437,6 +470,28 @@ ecma_promise_create_resolving_functions (ecma_object_t *object_p, /**< the promi
437470
/* 5. - 7. */
438471
funcs->reject = ecma_promise_create_resolving_functions_helper (object_p,
439472
ecma_promise_reject_handler);
473+
if (!create_already_resolved)
474+
{
475+
return;
476+
}
477+
478+
ecma_value_t already_resolved = ecma_op_create_boolean_object (ECMA_VALUE_FALSE);
479+
ecma_string_t *str_already_resolved_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ALREADY_RESOLVED);
480+
ecma_property_value_t *value_p;
481+
482+
value_p = ecma_create_named_data_property (ecma_get_object_from_value (funcs->resolve),
483+
str_already_resolved_p,
484+
ECMA_PROPERTY_FIXED,
485+
NULL);
486+
value_p->value = already_resolved;
487+
488+
value_p = ecma_create_named_data_property (ecma_get_object_from_value (funcs->reject),
489+
str_already_resolved_p,
490+
ECMA_PROPERTY_FIXED,
491+
NULL);
492+
value_p->value = already_resolved;
493+
494+
ecma_free_value (already_resolved);
440495
} /* ecma_promise_create_resolving_functions */
441496

442497
/**
@@ -490,7 +545,7 @@ ecma_op_create_promise_object (ecma_value_t executor, /**< the executor function
490545
promise_object_p->reactions = reactions;
491546
/* 8. */
492547
ecma_promise_resolving_functions_t funcs;
493-
ecma_promise_create_resolving_functions (object_p, &funcs);
548+
ecma_promise_create_resolving_functions (object_p, &funcs, false);
494549

495550
ecma_string_t *str_resolve_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_RESOLVE_FUNCTION);
496551
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)