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 #include "ecma-function-object.h"
17 #include "ecma-globals.h"
18 #include "ecma-helpers.h"
19 #include "ecma-jobqueue.h"
20 #include "ecma-objects.h"
21 #include "ecma-promise-object.h"
22 #include "jcontext.h"
23
24 #if ENABLED (JERRY_ES2015_BUILTIN_PROMISE)
25
26 /**
27 * Mask for job queue type.
28 */
29 #define ECMA_JOB_QUEURE_TYPE_MASK ((uintptr_t) 0x07)
30
31 /** \addtogroup ecma ECMA
32 * @{
33 *
34 * \addtogroup ecmajobqueue ECMA Job Queue related routines
35 * @{
36 */
37
38 /**
39 * Description of the PromiseReactionJob
40 */
41 typedef struct
42 {
43 ecma_job_queue_item_t header; /**< job queue item header */
44 ecma_value_t capability; /**< capability object */
45 ecma_value_t handler; /**< handler function */
46 ecma_value_t argument; /**< argument for the reaction */
47 } ecma_job_promise_reaction_t;
48
49 /**
50 * Description of the PromiseResolveThenableJob
51 */
52 typedef struct
53 {
54 ecma_job_queue_item_t header; /**< job queue item header */
55 ecma_value_t promise; /**< promise to be resolved */
56 ecma_value_t thenable; /**< thenable object */
57 ecma_value_t then; /**< 'then' function */
58 } ecma_job_promise_resolve_thenable_t;
59
60 /**
61 * Initialize the jobqueue.
62 */
ecma_job_queue_init(void)63 void ecma_job_queue_init (void)
64 {
65 JERRY_CONTEXT (job_queue_head_p) = NULL;
66 JERRY_CONTEXT (job_queue_tail_p) = NULL;
67 } /* ecma_job_queue_init */
68
69 /**
70 * Get the type of the job.
71 *
72 * @return type of the job
73 */
74 static inline ecma_job_queue_item_type_t JERRY_ATTR_ALWAYS_INLINE
ecma_job_queue_get_type(ecma_job_queue_item_t * job_p)75 ecma_job_queue_get_type (ecma_job_queue_item_t *job_p) /**< the job */
76 {
77 return (ecma_job_queue_item_type_t) (job_p->next_and_type & ECMA_JOB_QUEURE_TYPE_MASK);
78 } /* ecma_job_queue_get_type */
79
80 /**
81 * Get the next job of the job queue.
82 *
83 * @return next job
84 */
85 static inline ecma_job_queue_item_t *JERRY_ATTR_ALWAYS_INLINE
ecma_job_queue_get_next(ecma_job_queue_item_t * job_p)86 ecma_job_queue_get_next (ecma_job_queue_item_t *job_p) /**< the job */
87 {
88 return (ecma_job_queue_item_t *) (job_p->next_and_type & ~ECMA_JOB_QUEURE_TYPE_MASK);
89 } /* ecma_job_queue_get_next */
90
91 /**
92 * Free the heap and the member of the PromiseReactionJob.
93 */
94 static void
ecma_free_promise_reaction_job(ecma_job_promise_reaction_t * job_p)95 ecma_free_promise_reaction_job (ecma_job_promise_reaction_t *job_p) /**< points to the PromiseReactionJob */
96 {
97 JERRY_ASSERT (job_p != NULL);
98
99 ecma_free_value (job_p->capability);
100 ecma_free_value (job_p->handler);
101 ecma_free_value (job_p->argument);
102
103 jmem_heap_free_block (job_p, sizeof (ecma_job_promise_reaction_t));
104 } /* ecma_free_promise_reaction_job */
105
106 /**
107 * Free the heap and the member of the PromiseResolveThenableJob.
108 */
109 static void
ecma_free_promise_resolve_thenable_job(ecma_job_promise_resolve_thenable_t * job_p)110 ecma_free_promise_resolve_thenable_job (ecma_job_promise_resolve_thenable_t *job_p) /**< points to the
111 * PromiseResolveThenableJob */
112 {
113 JERRY_ASSERT (job_p != NULL);
114
115 ecma_free_value (job_p->promise);
116 ecma_free_value (job_p->thenable);
117 ecma_free_value (job_p->then);
118
119 jmem_heap_free_block (job_p, sizeof (ecma_job_promise_resolve_thenable_t));
120 } /* ecma_free_promise_resolve_thenable_job */
121
122 /**
123 * The processor for PromiseReactionJob.
124 *
125 * See also: ES2015 25.4.2.1
126 *
127 * @return ecma value
128 * Returned value must be freed with ecma_free_value
129 */
130 static ecma_value_t
ecma_process_promise_reaction_job(ecma_job_promise_reaction_t * job_p)131 ecma_process_promise_reaction_job (ecma_job_promise_reaction_t *job_p) /**< the job to be operated */
132 {
133 ecma_string_t *resolve_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_RESOLVE);
134 ecma_string_t *reject_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_REJECT);
135
136 /* 2. */
137 ecma_value_t capability = job_p->capability;
138 /* 3. */
139 ecma_value_t handler = job_p->handler;
140
141 JERRY_ASSERT (ecma_is_value_boolean (handler) || ecma_op_is_callable (handler));
142
143 ecma_value_t handler_result;
144
145 if (ecma_is_value_boolean (handler))
146 {
147 /* 4-5. True indicates "identity" and false indicates "thrower" */
148 handler_result = ecma_copy_value (job_p->argument);
149 }
150 else
151 {
152 /* 6. */
153 handler_result = ecma_op_function_call (ecma_get_object_from_value (handler),
154 ECMA_VALUE_UNDEFINED,
155 &(job_p->argument),
156 1);
157 }
158
159 ecma_value_t status;
160
161 if (ecma_is_value_false (handler) || ECMA_IS_VALUE_ERROR (handler_result))
162 {
163 if (ECMA_IS_VALUE_ERROR (handler_result))
164 {
165 handler_result = jcontext_take_exception ();
166 }
167
168 /* 7. */
169 ecma_value_t reject = ecma_op_object_get (ecma_get_object_from_value (capability), reject_str_p);
170
171 JERRY_ASSERT (ecma_op_is_callable (reject));
172
173 status = ecma_op_function_call (ecma_get_object_from_value (reject),
174 ECMA_VALUE_UNDEFINED,
175 &handler_result,
176 1);
177 ecma_free_value (reject);
178 }
179 else
180 {
181 /* 8. */
182 ecma_value_t resolve = ecma_op_object_get (ecma_get_object_from_value (capability), resolve_str_p);
183
184 JERRY_ASSERT (ecma_op_is_callable (resolve));
185
186 status = ecma_op_function_call (ecma_get_object_from_value (resolve),
187 ECMA_VALUE_UNDEFINED,
188 &handler_result,
189 1);
190 ecma_free_value (resolve);
191 }
192
193 ecma_free_value (handler_result);
194 ecma_free_promise_reaction_job (job_p);
195
196 return status;
197 } /* ecma_process_promise_reaction_job */
198
199 /**
200 * Process the PromiseResolveThenableJob.
201 *
202 * See also: ES2015 25.4.2.2
203 *
204 * @return ecma value
205 * Returned value must be freed with ecma_free_value
206 */
207 static ecma_value_t
ecma_process_promise_resolve_thenable_job(ecma_job_promise_resolve_thenable_t * job_p)208 ecma_process_promise_resolve_thenable_job (ecma_job_promise_resolve_thenable_t *job_p) /**< the job to be operated */
209 {
210 ecma_object_t *promise_p = ecma_get_object_from_value (job_p->promise);
211 ecma_promise_resolving_functions_t funcs;
212 ecma_promise_create_resolving_functions (promise_p, &funcs, true);
213
214 ecma_string_t *str_resolve_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_RESOLVE_FUNCTION);
215 ecma_string_t *str_reject_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_REJECT_FUNCTION);
216
217 ecma_op_object_put (promise_p,
218 str_resolve_p,
219 funcs.resolve,
220 false);
221 ecma_op_object_put (promise_p,
222 str_reject_p,
223 funcs.reject,
224 false);
225
226 ecma_value_t argv[] = { funcs.resolve, funcs.reject };
227 ecma_value_t ret;
228 ecma_value_t then_call_result = ecma_op_function_call (ecma_get_object_from_value (job_p->then),
229 job_p->thenable,
230 argv,
231 2);
232
233 ret = then_call_result;
234
235 if (ECMA_IS_VALUE_ERROR (then_call_result))
236 {
237 then_call_result = jcontext_take_exception ();
238
239 ret = ecma_op_function_call (ecma_get_object_from_value (funcs.reject),
240 ECMA_VALUE_UNDEFINED,
241 &then_call_result,
242 1);
243
244 ecma_free_value (then_call_result);
245 }
246
247 ecma_promise_free_resolving_functions (&funcs);
248 ecma_free_promise_resolve_thenable_job (job_p);
249
250 return ret;
251 } /* ecma_process_promise_resolve_thenable_job */
252
253 /**
254 * Enqueue a Promise job into the jobqueue.
255 */
256 static void
ecma_enqueue_job(ecma_job_queue_item_t * job_p)257 ecma_enqueue_job (ecma_job_queue_item_t *job_p) /**< the job */
258 {
259 JERRY_ASSERT (job_p->next_and_type <= ECMA_JOB_QUEURE_TYPE_MASK);
260
261 if (JERRY_CONTEXT (job_queue_head_p) == NULL)
262 {
263 JERRY_CONTEXT (job_queue_head_p) = job_p;
264 JERRY_CONTEXT (job_queue_tail_p) = job_p;
265 }
266 else
267 {
268 JERRY_ASSERT ((JERRY_CONTEXT (job_queue_tail_p)->next_and_type & ~ECMA_JOB_QUEURE_TYPE_MASK) == 0);
269
270 JERRY_CONTEXT (job_queue_tail_p)->next_and_type |= (uintptr_t) job_p;
271 JERRY_CONTEXT (job_queue_tail_p) = job_p;
272 }
273 } /* ecma_enqueue_job */
274
275 /**
276 * Enqueue a PromiseReactionJob into the jobqueue.
277 */
278 void
ecma_enqueue_promise_reaction_job(ecma_value_t capability,ecma_value_t handler,ecma_value_t argument)279 ecma_enqueue_promise_reaction_job (ecma_value_t capability, /**< capability object */
280 ecma_value_t handler, /**< handler function */
281 ecma_value_t argument) /**< argument for the reaction */
282 {
283 ecma_job_promise_reaction_t *job_p;
284 job_p = (ecma_job_promise_reaction_t *) jmem_heap_alloc_block (sizeof (ecma_job_promise_reaction_t));
285 job_p->header.next_and_type = ECMA_JOB_PROMISE_REACTION;
286 job_p->capability = ecma_copy_value (capability);
287 job_p->handler = ecma_copy_value (handler);
288 job_p->argument = ecma_copy_value (argument);
289
290 ecma_enqueue_job (&job_p->header);
291 } /* ecma_enqueue_promise_reaction_job */
292
293 /**
294 * Enqueue a PromiseResolveThenableJob into the jobqueue.
295 */
296 void
ecma_enqueue_promise_resolve_thenable_job(ecma_value_t promise,ecma_value_t thenable,ecma_value_t then)297 ecma_enqueue_promise_resolve_thenable_job (ecma_value_t promise, /**< promise to be resolved */
298 ecma_value_t thenable, /**< thenable object */
299 ecma_value_t then) /**< 'then' function */
300 {
301 JERRY_ASSERT (ecma_is_promise (ecma_get_object_from_value (promise)));
302 JERRY_ASSERT (ecma_is_value_object (thenable));
303 JERRY_ASSERT (ecma_op_is_callable (then));
304
305 ecma_job_promise_resolve_thenable_t *job_p;
306 job_p = (ecma_job_promise_resolve_thenable_t *) jmem_heap_alloc_block (sizeof (ecma_job_promise_resolve_thenable_t));
307 job_p->header.next_and_type = ECMA_JOB_PROMISE_THENABLE;
308 job_p->promise = ecma_copy_value (promise);
309 job_p->thenable = ecma_copy_value (thenable);
310 job_p->then = ecma_copy_value (then);
311
312 ecma_enqueue_job (&job_p->header);
313 } /* ecma_enqueue_promise_resolve_thenable_job */
314
315 /**
316 * Process enqueued Promise jobs until the first thrown error or until the
317 * jobqueue becomes empty.
318 *
319 * @return result of the last processed job - if the jobqueue was non-empty,
320 * undefined - otherwise.
321 */
322 ecma_value_t
ecma_process_all_enqueued_jobs(void)323 ecma_process_all_enqueued_jobs (void)
324 {
325 ecma_value_t ret = ECMA_VALUE_UNDEFINED;
326
327 while (JERRY_CONTEXT (job_queue_head_p) != NULL && !ECMA_IS_VALUE_ERROR (ret))
328 {
329 ecma_job_queue_item_t *job_p = JERRY_CONTEXT (job_queue_head_p);
330 JERRY_CONTEXT (job_queue_head_p) = ecma_job_queue_get_next (job_p);
331
332 ecma_fast_free_value (ret);
333
334 switch (ecma_job_queue_get_type (job_p))
335 {
336 case ECMA_JOB_PROMISE_REACTION:
337 {
338 ret = ecma_process_promise_reaction_job ((ecma_job_promise_reaction_t *) job_p);
339 break;
340 }
341 default:
342 {
343 JERRY_ASSERT (ecma_job_queue_get_type (job_p) == ECMA_JOB_PROMISE_THENABLE);
344
345 ret = ecma_process_promise_resolve_thenable_job ((ecma_job_promise_resolve_thenable_t *) job_p);
346 break;
347 }
348 }
349 }
350
351 return ret;
352 } /* ecma_process_all_enqueued_jobs */
353
354 /**
355 * Release enqueued Promise jobs.
356 */
357 void
ecma_free_all_enqueued_jobs(void)358 ecma_free_all_enqueued_jobs (void)
359 {
360 while (JERRY_CONTEXT (job_queue_head_p) != NULL)
361 {
362 ecma_job_queue_item_t *job_p = JERRY_CONTEXT (job_queue_head_p);
363 JERRY_CONTEXT (job_queue_head_p) = ecma_job_queue_get_next (job_p);
364
365 switch (ecma_job_queue_get_type (job_p))
366 {
367 case ECMA_JOB_PROMISE_REACTION:
368 {
369 ecma_free_promise_reaction_job ((ecma_job_promise_reaction_t *) job_p);
370 break;
371 }
372 default:
373 {
374 JERRY_ASSERT (ecma_job_queue_get_type (job_p) == ECMA_JOB_PROMISE_THENABLE);
375
376 ecma_free_promise_resolve_thenable_job ((ecma_job_promise_resolve_thenable_t *) job_p);
377 break;
378 }
379 }
380 }
381 } /* ecma_free_all_enqueued_jobs */
382
383 /**
384 * @}
385 * @}
386 */
387 #endif /* ENABLED (JERRY_ES2015_BUILTIN_PROMISE) */
388