/* 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. */ #include "jerryscript.h" #include "test-common.h" /* Note: RS = ReSolve, RJ = ReJect */ typedef enum { C = JERRY_PROMISE_EVENT_CREATE, /**< same as JERRY_PROMISE_CALLBACK_CREATE with undefined value */ RS = JERRY_PROMISE_EVENT_RESOLVE, /**< same as JERRY_PROMISE_CALLBACK_RESOLVE */ RJ = JERRY_PROMISE_EVENT_REJECT, /**< same as JERRY_PROMISE_CALLBACK_REJECT */ RSF = JERRY_PROMISE_EVENT_RESOLVE_FULFILLED, /**< same as JERRY_PROMISE_EVENT_RESOLVE_FULFILLED */ RJF = JERRY_PROMISE_EVENT_REJECT_FULFILLED, /**< same as JERRY_PROMISE_EVENT_REJECT_FULFILLED */ RWH = JERRY_PROMISE_EVENT_REJECT_WITHOUT_HANDLER, /**< same as JERRY_PROMISE_EVENT_REJECT_WITHOUT_HANDLER */ CHA = JERRY_PROMISE_EVENT_CATCH_HANDLER_ADDED, /**< same as JERRY_PROMISE_EVENT_CATCH_HANDLER_ADDED */ BR = JERRY_PROMISE_EVENT_BEFORE_REACTION_JOB, /**< same as JERRY_PROMISE_CALLBACK_BEFORE_REACTION_JOB */ AR = JERRY_PROMISE_EVENT_AFTER_REACTION_JOB, /**< same as JERRY_PROMISE_CALLBACK_AFTER_REACTION_JOB */ A = JERRY_PROMISE_EVENT_ASYNC_AWAIT, /**< same as JERRY_PROMISE_CALLBACK_ASYNC_AWAIT */ BRS = JERRY_PROMISE_EVENT_ASYNC_BEFORE_RESOLVE, /**< same as JERRY_PROMISE_CALLBACK_ASYNC_BEFORE_RESOLVE */ BRJ = JERRY_PROMISE_EVENT_ASYNC_BEFORE_REJECT, /**< same as JERRY_PROMISE_CALLBACK_ASYNC_BEFORE_REJECT */ ARS = JERRY_PROMISE_EVENT_ASYNC_AFTER_RESOLVE, /**< same as JERRY_PROMISE_CALLBACK_ASYNC_AFTER_RESOLVE */ ARJ = JERRY_PROMISE_EVENT_ASYNC_AFTER_REJECT, /**< same as JERRY_PROMISE_CALLBACK_ASYNC_AFTER_REJECT */ CP = UINT8_MAX - 1, /**< same as JERRY_PROMISE_CALLBACK_CREATE with Promise value */ E = UINT8_MAX, /**< marks the end of the event list */ } jerry_promise_callback_event_abbreviations_t; static int user; static const uint8_t *next_event_p; static void promise_callback (jerry_promise_event_type_t event_type, /**< event type */ const jerry_value_t object, /**< target object */ const jerry_value_t value, /**< optional argument */ void *user_p) /**< user pointer passed to the callback */ { TEST_ASSERT (user_p == (void *) &user); switch (event_type) { case JERRY_PROMISE_EVENT_CREATE: { TEST_ASSERT (jerry_value_is_promise (object)); if (jerry_value_is_undefined (value)) { break; } TEST_ASSERT (jerry_value_is_promise (value)); TEST_ASSERT (*next_event_p++ == CP); return; } case JERRY_PROMISE_EVENT_RESOLVE: case JERRY_PROMISE_EVENT_REJECT: case JERRY_PROMISE_EVENT_RESOLVE_FULFILLED: case JERRY_PROMISE_EVENT_REJECT_FULFILLED: case JERRY_PROMISE_EVENT_REJECT_WITHOUT_HANDLER: { TEST_ASSERT (jerry_value_is_promise (object)); break; } case JERRY_PROMISE_EVENT_CATCH_HANDLER_ADDED: case JERRY_PROMISE_EVENT_BEFORE_REACTION_JOB: case JERRY_PROMISE_EVENT_AFTER_REACTION_JOB: { TEST_ASSERT (jerry_value_is_promise (object)); TEST_ASSERT (jerry_value_is_undefined (value)); break; } case JERRY_PROMISE_EVENT_ASYNC_AWAIT: { TEST_ASSERT (jerry_value_is_object (object)); TEST_ASSERT (jerry_value_is_promise (value)); break; } default: { TEST_ASSERT (event_type == JERRY_PROMISE_EVENT_ASYNC_BEFORE_RESOLVE || event_type == JERRY_PROMISE_EVENT_ASYNC_BEFORE_REJECT || event_type == JERRY_PROMISE_EVENT_ASYNC_AFTER_RESOLVE || event_type == JERRY_PROMISE_EVENT_ASYNC_AFTER_REJECT); TEST_ASSERT (jerry_value_is_object (object)); break; } } TEST_ASSERT (*next_event_p++ == (uint8_t) event_type); } /* promise_callback */ static void run_eval (const uint8_t *event_list_p, /**< event list */ const char *source_p) /**< source code */ { next_event_p = event_list_p; jerry_value_t result = jerry_eval ((const jerry_char_t *) source_p, strlen (source_p), 0); TEST_ASSERT (!jerry_value_is_error (result)); jerry_release_value (result); result = jerry_run_all_enqueued_jobs (); TEST_ASSERT (!jerry_value_is_error (result)); jerry_release_value (result); TEST_ASSERT (*next_event_p == UINT8_MAX); } /* run_eval */ int main (void) { TEST_INIT (); if (!jerry_is_feature_enabled (JERRY_FEATURE_PROMISE)) { jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Promise is disabled!\n"); return 0; } /* The test system enables this feature when Promises are enabled. */ TEST_ASSERT (jerry_is_feature_enabled (JERRY_FEATURE_PROMISE_CALLBACK)); jerry_init (JERRY_INIT_EMPTY); jerry_promise_event_filter_t filters = (JERRY_PROMISE_EVENT_FILTER_CREATE | JERRY_PROMISE_EVENT_FILTER_RESOLVE | JERRY_PROMISE_EVENT_FILTER_REJECT | JERRY_PROMISE_EVENT_FILTER_ERROR | JERRY_PROMISE_EVENT_FILTER_REACTION_JOB | JERRY_PROMISE_EVENT_FILTER_ASYNC_MAIN | JERRY_PROMISE_EVENT_FILTER_ASYNC_REACTION_JOB); jerry_promise_set_callback (filters, promise_callback, (void *) &user); /* Test promise creation. */ static uint8_t events1[] = { C, C, C, E }; run_eval (events1, "'use strict'\n" "new Promise((res, rej) => {})\n" "new Promise((res, rej) => {})\n" "new Promise((res, rej) => {})\n"); /* Test then call. */ static uint8_t events2[] = { C, CP, E }; run_eval (events2, "'use strict'\n" "var promise = new Promise((res, rej) => {})\n" "promise.then(() => {}, () => {})\n"); /* Test then call with extended Promise. */ static uint8_t events3[] = { C, C, E }; run_eval (events3, "'use strict'\n" "var P = class extends Promise {}\n" "var promise = new P((res, rej) => {})\n" "promise.then(() => {})\n"); /* Test resolve and reject calls. */ static uint8_t events4[] = { C, C, RS, RJ, RWH, E }; run_eval (events4, "'use strict'\n" "var resolve\n" "var reject\n" "new Promise((res, rej) => resolve = res)\n" "new Promise((res, rej) => reject = rej)\n" "resolve(1)\n" "reject(1)\n"); /* Test then and resolve calls. */ static uint8_t events5[] = { C, CP, RS, BR, RS, AR, E }; run_eval (events5, "'use strict'\n" "var resolve\n" "var promise = new Promise((res, rej) => resolve = res)\n" "promise.then(() => {})\n" "resolve(1)\n"); /* Test resolve and then calls. */ static uint8_t events6[] = { C, RS, CP, BR, RS, AR, E }; run_eval (events6, "'use strict'\n" "var promise = new Promise((res, rej) => res(1))\n" "promise.then(() => {})\n"); /* Test Promise.resolve. */ static uint8_t events7[] = { C, RS, CP, BR, RS, AR, E }; run_eval (events7, "Promise.resolve(4).then(() => {})\n"); /* Test Promise.reject. */ static uint8_t events8[] = { C, RJ, RWH, CP, CHA, BR, RJ, RWH, AR, E }; run_eval (events8, "Promise.reject(4).catch(() => { throw 'Error' })\n"); /* Test Promise.race without resolve */ static uint8_t events9[] = { C, C, C, CP, CP, E }; run_eval (events9, "'use strict'\n" "var p1 = new Promise((res, rej) => {})\n" "var p2 = new Promise((res, rej) => {})\n" "Promise.race([p1,p2])\n"); /* Test Promise.race with resolve. */ static uint8_t events10[] = { C, RS, C, RJ, RWH, C, CP, CP, CHA, BR, RS, RS, AR, BR, RJF, RS, AR, E }; run_eval (events10, "'use strict'\n" "var p1 = new Promise((res, rej) => res(1))\n" "var p2 = new Promise((res, rej) => rej(1))\n" "Promise.race([p1,p2])\n"); /* Test Promise.all without resolve. */ static uint8_t events11[] = { C, C, C, CP, CP, E }; run_eval (events11, "'use strict'\n" "var p1 = new Promise((res, rej) => {})\n" "var p2 = new Promise((res, rej) => {})\n" "Promise.all([p1,p2])\n"); /* Test Promise.all with resolve. */ static uint8_t events12[] = { C, RS, C, RJ, RWH, C, CP, CP, CHA, BR, RS, AR, BR, RJ, RWH, RS, AR, E }; run_eval (events12, "'use strict'\n" "var p1 = new Promise((res, rej) => res(1))\n" "var p2 = new Promise((res, rej) => rej(1))\n" "Promise.all([p1,p2])\n"); /* Test async function. */ static uint8_t events13[] = { C, RS, E }; run_eval (events13, "'use strict'\n" "async function f() {}\n" "f()\n"); /* Test await with resolved Promise. */ static uint8_t events14[] = { C, RS, A, C, BRS, RS, ARS, E }; run_eval (events14, "'use strict'\n" "async function f(p) { await p }\n" "f(Promise.resolve(1))\n"); /* Test await with non-Promise value. */ static uint8_t events15[] = { C, RS, A, C, BRS, C, RS, A, ARS, BRS, RS, ARS, E }; run_eval (events15, "'use strict'\n" "async function f(p) { await p; await 'X' }\n" "f(Promise.resolve(1))\n"); /* Test await with rejected Promise. */ static uint8_t events16[] = { C, RJ, RWH, A, CHA, C, BRJ, C, RS, RS, ARJ, E }; run_eval (events16, "'use strict'\n" "async function f(p) { try { await p; } catch (e) { Promise.resolve(1) } }\n" "f(Promise.reject(1))\n"); /* Test async generator function. */ static uint8_t events17[] = { C, RS, C, A, BRS, RS, ARS, E }; run_eval (events17, "'use strict'\n" "async function *f(p) { await p; return 4 }\n" "f(Promise.resolve(1)).next()\n"); /* Test yield* operation. */ static uint8_t events18[] = { C, C, RS, A, BRS, C, RS, A, ARS, BRS, RS, ARS, E }; run_eval (events18, "'use strict'\n" "async function *f(p) { yield 1 }\n" "async function *g() { yield* f() }\n" "g().next()\n"); /* Test multiple fulfill operations. */ static uint8_t events19[] = { C, RS, RSF, RJF, E }; run_eval (events19, "'use strict'\n" "var resolve, reject\n" "var p1 = new Promise((res, rej) => { resolve = res, reject = rej })\n" "resolve(1)\n" "resolve(2)\n" "reject(3)\n"); /* Test multiple fulfill operations. */ static uint8_t events20[] = { C, RJ, RWH, RSF, RJF, E }; run_eval (events20, "'use strict'\n" "var resolve, reject\n" "var p1 = new Promise((res, rej) => { resolve = res, reject = rej })\n" "reject(1)\n" "resolve(2)\n" "reject(3)\n"); /* Test catch handler added later is reported only once. */ static uint8_t events21[] = { C, RJ, RWH, CP, CHA, CP, CP, BR, RS, AR, BR, RS, AR, BR, RS, AR, E }; run_eval (events21, "'use strict'\n" "var rej = Promise.reject(4)\n" "rej.catch(() => {})\n" "rej.catch(() => {})\n" "rej.catch(() => {})\n"); /* Test catch handler added later is reported only once. */ static uint8_t events22[] = { C, RJ, RWH, A, CHA, C, BRJ, A, ARJ, BRJ, RJ, RWH, ARJ, E }; run_eval (events22, "'use strict'\n" "async function f(p) { try { await p; } catch(e) { await p; } }" "f(Promise.reject(4))\n"); /* Test chained then. */ static uint8_t events23[] = { C, RJ, RWH, CP, CHA, CP, BR, RJ, AR, BR, RS, AR, E }; run_eval (events23, "'use strict'\n" "var p = Promise.reject(0)\n" "p.then(() => {}).catch(() => {})\n"); /* Test disabled filters. */ jerry_promise_set_callback (JERRY_PROMISE_EVENT_FILTER_DISABLE, promise_callback, (void *) &user); static uint8_t events24[] = { E }; run_eval (events24, "'use strict'\n" "async function f(p) { await p }" "f(Promise.resolve(1))\n"); /* Test filtered events. */ filters = JERRY_PROMISE_EVENT_FILTER_REACTION_JOB | JERRY_PROMISE_EVENT_FILTER_ASYNC_REACTION_JOB; jerry_promise_set_callback (filters, promise_callback, (void *) &user); static uint8_t events25[] = { BR, AR, BRS, ARS, E }; run_eval (events25, "'use strict'\n" "async function f(p) { await p }" "f(Promise.resolve(1).then(() => {}))\n"); jerry_cleanup (); return 0; } /* main */