Index: test/cctest/test-api.cc |
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc |
index 1fb5e5620a684dba9a23af511eaffcf54e10a47b..ca2c8dea063c4fca55ad398b7e1c8897f15900b6 100644 |
--- a/test/cctest/test-api.cc |
+++ b/test/cctest/test-api.cc |
@@ -18139,6 +18139,290 @@ TEST(InlineScriptWithSourceURLInStackTrace) { |
CHECK(CompileRunWithOrigin(code.start(), "url", 0, 1)->IsUndefined()); |
} |
+void SetPromise(const char* name, v8::Local<v8::Promise> promise) { |
+ CcTest::global() |
+ ->Set(CcTest::isolate()->GetCurrentContext(), v8_str(name), promise) |
+ .FromJust(); |
+} |
+ |
+class PromiseHookData { |
+ public: |
+ int before_hook_count = 0; |
+ int after_hook_count = 0; |
+ int promise_hook_count = 0; |
+ int parent_promise_count = 0; |
+ bool check_value = true; |
+ std::string promise_hook_value; |
+ |
+ void Reset() { |
+ before_hook_count = 0; |
+ after_hook_count = 0; |
+ promise_hook_count = 0; |
+ parent_promise_count = 0; |
+ check_value = true; |
+ promise_hook_value = ""; |
+ } |
+}; |
+ |
+PromiseHookData* promise_hook_data; |
+ |
+void CustomPromiseHook(v8::PromiseHookType type, v8::Local<v8::Promise> promise, |
+ v8::Local<v8::Value> parentPromise) { |
+ promise_hook_data->promise_hook_count++; |
+ switch (type) { |
+ case v8::PromiseHookType::kInit: |
+ SetPromise("init", promise); |
+ |
+ if (!parentPromise->IsUndefined()) { |
+ promise_hook_data->parent_promise_count++; |
+ SetPromise("parent", v8::Local<v8::Promise>::Cast(parentPromise)); |
+ } |
+ |
+ break; |
+ case v8::PromiseHookType::kResolve: |
+ SetPromise("resolve", promise); |
+ break; |
+ case v8::PromiseHookType::kBefore: |
+ promise_hook_data->before_hook_count++; |
+ CHECK(promise_hook_data->before_hook_count > |
+ promise_hook_data->after_hook_count); |
+ CHECK(CcTest::global() |
+ ->Get(CcTest::isolate()->GetCurrentContext(), v8_str("value")) |
+ .ToLocalChecked() |
+ ->Equals(CcTest::isolate()->GetCurrentContext(), v8_str("")) |
+ .FromJust()); |
+ SetPromise("before", promise); |
+ break; |
+ case v8::PromiseHookType::kAfter: |
+ promise_hook_data->after_hook_count++; |
+ CHECK(promise_hook_data->after_hook_count <= |
+ promise_hook_data->before_hook_count); |
+ if (promise_hook_data->check_value) { |
+ CHECK( |
+ CcTest::global() |
+ ->Get(CcTest::isolate()->GetCurrentContext(), v8_str("value")) |
+ .ToLocalChecked() |
+ ->Equals(CcTest::isolate()->GetCurrentContext(), |
+ v8_str(promise_hook_data->promise_hook_value.c_str())) |
+ .FromJust()); |
+ } |
+ SetPromise("after", promise); |
+ break; |
+ } |
+} |
+ |
+TEST(PromiseHook) { |
+ LocalContext env; |
+ v8::Isolate* isolate = env->GetIsolate(); |
+ v8::HandleScope scope(isolate); |
+ |
+ v8::Local<v8::Object> global = CcTest::global(); |
+ v8::Local<v8::Context> context = CcTest::isolate()->GetCurrentContext(); |
+ |
+ promise_hook_data = new PromiseHookData(); |
+ promise_hook_data->promise_hook_value = "fulfilled"; |
+ const char* source = |
+ "var resolve, value = ''; \n" |
+ "var p = new Promise(r => resolve = r); \n"; |
+ |
+ isolate->SetPromiseHook(CustomPromiseHook); |
+ |
+ CompileRun(source); |
+ auto init_promise = global->Get(context, v8_str("init")).ToLocalChecked(); |
+ CHECK(GetPromise("p")->Equals(env.local(), init_promise).FromJust()); |
+ CHECK_EQ(1, promise_hook_data->promise_hook_count); |
+ CHECK_EQ(0, promise_hook_data->parent_promise_count); |
+ |
+ CompileRun("var p1 = p.then(() => { value = 'fulfilled'; }); \n"); |
+ init_promise = global->Get(context, v8_str("init")).ToLocalChecked(); |
+ auto parent_promise = global->Get(context, v8_str("parent")).ToLocalChecked(); |
+ CHECK(GetPromise("p1")->Equals(env.local(), init_promise).FromJust()); |
+ CHECK(GetPromise("p")->Equals(env.local(), parent_promise).FromJust()); |
+ CHECK_EQ(2, promise_hook_data->promise_hook_count); |
+ CHECK_EQ(1, promise_hook_data->parent_promise_count); |
+ |
+ CompileRun("resolve(); \n"); |
+ auto resolve_promise = |
+ global->Get(context, v8_str("resolve")).ToLocalChecked(); |
+ auto before_promise = global->Get(context, v8_str("before")).ToLocalChecked(); |
+ auto after_promise = global->Get(context, v8_str("after")).ToLocalChecked(); |
+ CHECK(GetPromise("p")->Equals(env.local(), before_promise).FromJust()); |
+ CHECK(GetPromise("p")->Equals(env.local(), after_promise).FromJust()); |
+ CHECK(GetPromise("p1")->Equals(env.local(), resolve_promise).FromJust()); |
+ CHECK_EQ(6, promise_hook_data->promise_hook_count); |
+ |
+ promise_hook_data->Reset(); |
+ promise_hook_data->promise_hook_value = "rejected"; |
+ source = |
+ "var reject, value = ''; \n" |
+ "var p = new Promise((_, r) => reject = r); \n"; |
+ |
+ CompileRun(source); |
+ init_promise = global->Get(context, v8_str("init")).ToLocalChecked(); |
+ CHECK(GetPromise("p")->Equals(env.local(), init_promise).FromJust()); |
+ CHECK_EQ(1, promise_hook_data->promise_hook_count); |
+ CHECK_EQ(0, promise_hook_data->parent_promise_count); |
+ |
+ CompileRun("var p1 = p.catch(() => { value = 'rejected'; }); \n"); |
+ init_promise = global->Get(context, v8_str("init")).ToLocalChecked(); |
+ parent_promise = global->Get(context, v8_str("parent")).ToLocalChecked(); |
+ CHECK(GetPromise("p1")->Equals(env.local(), init_promise).FromJust()); |
+ CHECK(GetPromise("p")->Equals(env.local(), parent_promise).FromJust()); |
+ CHECK_EQ(2, promise_hook_data->promise_hook_count); |
+ CHECK_EQ(1, promise_hook_data->parent_promise_count); |
+ |
+ CompileRun("reject(); \n"); |
+ resolve_promise = global->Get(context, v8_str("resolve")).ToLocalChecked(); |
+ before_promise = global->Get(context, v8_str("before")).ToLocalChecked(); |
+ after_promise = global->Get(context, v8_str("after")).ToLocalChecked(); |
+ CHECK(GetPromise("p")->Equals(env.local(), before_promise).FromJust()); |
+ CHECK(GetPromise("p")->Equals(env.local(), after_promise).FromJust()); |
+ CHECK(GetPromise("p1")->Equals(env.local(), resolve_promise).FromJust()); |
+ CHECK_EQ(6, promise_hook_data->promise_hook_count); |
+ |
+ promise_hook_data->Reset(); |
+ promise_hook_data->promise_hook_value = "Promise.resolve"; |
+ source = |
+ "var value = ''; \n" |
+ "var p = Promise.resolve('Promise.resolve'); \n"; |
+ |
+ CompileRun(source); |
+ init_promise = global->Get(context, v8_str("init")).ToLocalChecked(); |
+ CHECK(GetPromise("p")->Equals(env.local(), init_promise).FromJust()); |
+ // init hook and resolve hook |
+ CHECK_EQ(2, promise_hook_data->promise_hook_count); |
+ CHECK_EQ(0, promise_hook_data->parent_promise_count); |
+ resolve_promise = global->Get(context, v8_str("resolve")).ToLocalChecked(); |
+ CHECK(GetPromise("p")->Equals(env.local(), resolve_promise).FromJust()); |
+ |
+ CompileRun("var p1 = p.then((v) => { value = v; }); \n"); |
+ init_promise = global->Get(context, v8_str("init")).ToLocalChecked(); |
+ resolve_promise = global->Get(context, v8_str("resolve")).ToLocalChecked(); |
+ parent_promise = global->Get(context, v8_str("parent")).ToLocalChecked(); |
+ before_promise = global->Get(context, v8_str("before")).ToLocalChecked(); |
+ after_promise = global->Get(context, v8_str("after")).ToLocalChecked(); |
+ CHECK(GetPromise("p1")->Equals(env.local(), init_promise).FromJust()); |
+ CHECK(GetPromise("p1")->Equals(env.local(), resolve_promise).FromJust()); |
+ CHECK(GetPromise("p")->Equals(env.local(), parent_promise).FromJust()); |
+ CHECK(GetPromise("p")->Equals(env.local(), before_promise).FromJust()); |
+ CHECK(GetPromise("p")->Equals(env.local(), after_promise).FromJust()); |
+ CHECK_EQ(6, promise_hook_data->promise_hook_count); |
+ CHECK_EQ(1, promise_hook_data->parent_promise_count); |
+ |
+ promise_hook_data->Reset(); |
+ source = |
+ "var resolve, value = ''; \n" |
+ "var p = new Promise((_, r) => resolve = r); \n"; |
+ |
+ CompileRun(source); |
+ init_promise = global->Get(context, v8_str("init")).ToLocalChecked(); |
+ CHECK(GetPromise("p")->Equals(env.local(), init_promise).FromJust()); |
+ CHECK_EQ(1, promise_hook_data->promise_hook_count); |
+ CHECK_EQ(0, promise_hook_data->parent_promise_count); |
+ |
+ CompileRun("resolve(); \n"); |
+ resolve_promise = global->Get(context, v8_str("resolve")).ToLocalChecked(); |
+ CHECK(GetPromise("p")->Equals(env.local(), resolve_promise).FromJust()); |
+ CHECK_EQ(2, promise_hook_data->promise_hook_count); |
+ |
+ promise_hook_data->Reset(); |
+ source = |
+ "var reject, value = ''; \n" |
+ "var p = new Promise((_, r) => reject = r); \n"; |
+ |
+ CompileRun(source); |
+ init_promise = global->Get(context, v8_str("init")).ToLocalChecked(); |
+ CHECK(GetPromise("p")->Equals(env.local(), init_promise).FromJust()); |
+ CHECK_EQ(1, promise_hook_data->promise_hook_count); |
+ CHECK_EQ(0, promise_hook_data->parent_promise_count); |
+ |
+ CompileRun("reject(); \n"); |
+ resolve_promise = global->Get(context, v8_str("resolve")).ToLocalChecked(); |
+ CHECK(GetPromise("p")->Equals(env.local(), resolve_promise).FromJust()); |
+ CHECK_EQ(2, promise_hook_data->promise_hook_count); |
+ |
+ promise_hook_data->Reset(); |
+ // This test triggers after callbacks right after each other, so |
+ // lets just check the value at the end. |
+ promise_hook_data->check_value = false; |
+ promise_hook_data->promise_hook_value = "Promise.all"; |
+ source = |
+ "var resolve, value = ''; \n" |
+ "var tempPromise = new Promise(r => resolve = r); \n" |
+ "var p = Promise.all([tempPromise]);\n " |
+ "var p1 = p.then(v => value = v[0]); \n"; |
+ |
+ CompileRun(source); |
+ // 1) init hook (tempPromise) |
+ // 2) init hook (p) |
+ // 3) init hook (throwaway Promise in Promise.all, p) |
+ // 4) init hook (p1, p) |
+ CHECK_EQ(4, promise_hook_data->promise_hook_count); |
+ CHECK_EQ(2, promise_hook_data->parent_promise_count); |
+ |
+ promise_hook_data->promise_hook_value = "Promise.all"; |
+ CompileRun("resolve('Promise.all'); \n"); |
+ resolve_promise = global->Get(context, v8_str("resolve")).ToLocalChecked(); |
+ CHECK(GetPromise("p1")->Equals(env.local(), resolve_promise).FromJust()); |
+ // 5) resolve hook (tempPromise) |
+ // 6) resolve hook (throwaway Promise in Promise.all) |
+ // 6) before hook (throwaway Promise in Promise.all) |
+ // 7) after hook (throwaway Promise in Promise.all) |
+ // 8) before hook (p) |
+ // 9) after hook (p) |
+ // 10) resolve hook (p1) |
+ // 11) before hook (p1) |
+ // 12) after hook (p1) |
+ CHECK_EQ(12, promise_hook_data->promise_hook_count); |
+ CHECK(CcTest::global() |
+ ->Get(CcTest::isolate()->GetCurrentContext(), v8_str("value")) |
+ .ToLocalChecked() |
+ ->Equals(CcTest::isolate()->GetCurrentContext(), |
+ v8_str(promise_hook_data->promise_hook_value.c_str())) |
+ .FromJust()); |
+ |
+ promise_hook_data->Reset(); |
+ // This test triggers after callbacks right after each other, so |
+ // lets just check the value at the end. |
+ promise_hook_data->check_value = false; |
+ promise_hook_data->promise_hook_value = "Promise.race"; |
+ source = |
+ "var resolve, value = ''; \n" |
+ "var tempPromise = new Promise(r => resolve = r); \n" |
+ "var p = Promise.race([tempPromise]);\n " |
+ "var p1 = p.then(v => value = v); \n"; |
+ |
+ CompileRun(source); |
+ // 1) init hook (tempPromise) |
+ // 2) init hook (p) |
+ // 3) init hook (throwaway Promise in Promise.race, p) |
+ // 4) init hook (p1, p) |
+ CHECK_EQ(4, promise_hook_data->promise_hook_count); |
+ CHECK_EQ(2, promise_hook_data->parent_promise_count); |
+ |
+ promise_hook_data->promise_hook_value = "Promise.race"; |
+ CompileRun("resolve('Promise.race'); \n"); |
+ resolve_promise = global->Get(context, v8_str("resolve")).ToLocalChecked(); |
+ CHECK(GetPromise("p1")->Equals(env.local(), resolve_promise).FromJust()); |
+ // 5) resolve hook (tempPromise) |
+ // 6) resolve hook (throwaway Promise in Promise.race) |
+ // 6) before hook (throwaway Promise in Promise.race) |
+ // 7) after hook (throwaway Promise in Promise.race) |
+ // 8) before hook (p) |
+ // 9) after hook (p) |
+ // 10) resolve hook (p1) |
+ // 11) before hook (p1) |
+ // 12) after hook (p1) |
+ CHECK_EQ(12, promise_hook_data->promise_hook_count); |
+ CHECK(CcTest::global() |
+ ->Get(CcTest::isolate()->GetCurrentContext(), v8_str("value")) |
+ .ToLocalChecked() |
+ ->Equals(CcTest::isolate()->GetCurrentContext(), |
+ v8_str(promise_hook_data->promise_hook_value.c_str())) |
+ .FromJust()); |
+ |
+ delete promise_hook_data; |
+} |
void AnalyzeStackOfDynamicScriptWithSourceURL( |
const v8::FunctionCallbackInfo<v8::Value>& args) { |