LCOV - code coverage report
Current view: top level - src - async-hooks-wrapper.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 133 136 97.8 %
Date: 2019-04-17 Functions: 15 17 88.2 %

          Line data    Source code
       1             : // Copyright 2018 the V8 project authors. All rights reserved.
       2             : // Use of this source code is governed by a BSD-style license that can be
       3             : // found in the LICENSE file.
       4             : 
       5             : #include "src/async-hooks-wrapper.h"
       6             : #include "src/d8.h"
       7             : #include "src/isolate-inl.h"
       8             : 
       9             : namespace v8 {
      10             : 
      11          81 : void AsyncHooksWrap::Enable() { enabled_ = true; }
      12             : 
      13           9 : void AsyncHooksWrap::Disable() { enabled_ = false; }
      14             : 
      15             : v8::Local<v8::Function> AsyncHooksWrap::init_function() const {
      16         799 :   return init_function_.Get(isolate_);
      17             : }
      18          54 : void AsyncHooksWrap::set_init_function(v8::Local<v8::Function> value) {
      19          54 :   init_function_.Reset(isolate_, value);
      20          54 : }
      21             : v8::Local<v8::Function> AsyncHooksWrap::before_function() const {
      22         247 :   return before_function_.Get(isolate_);
      23             : }
      24           9 : void AsyncHooksWrap::set_before_function(v8::Local<v8::Function> value) {
      25           9 :   before_function_.Reset(isolate_, value);
      26           9 : }
      27             : v8::Local<v8::Function> AsyncHooksWrap::after_function() const {
      28         304 :   return after_function_.Get(isolate_);
      29             : }
      30          27 : void AsyncHooksWrap::set_after_function(v8::Local<v8::Function> value) {
      31          27 :   after_function_.Reset(isolate_, value);
      32          27 : }
      33             : v8::Local<v8::Function> AsyncHooksWrap::promiseResolve_function() const {
      34         523 :   return promiseResolve_function_.Get(isolate_);
      35             : }
      36           9 : void AsyncHooksWrap::set_promiseResolve_function(
      37             :     v8::Local<v8::Function> value) {
      38           9 :   promiseResolve_function_.Reset(isolate_, value);
      39           9 : }
      40             : 
      41         108 : static AsyncHooksWrap* UnwrapHook(
      42             :     const v8::FunctionCallbackInfo<v8::Value>& args) {
      43             :   Isolate* isolate = args.GetIsolate();
      44         216 :   HandleScope scope(isolate);
      45             :   Local<Object> hook = args.This();
      46             : 
      47             :   AsyncHooks* hooks = PerIsolateData::Get(isolate)->GetAsyncHooks();
      48             : 
      49         108 :   if (!hooks->async_hook_ctor.Get(isolate)->HasInstance(hook)) {
      50             :     isolate->ThrowException(
      51          18 :         String::NewFromUtf8(
      52             :             isolate, "Invalid 'this' passed instead of AsyncHooks instance",
      53             :             NewStringType::kNormal)
      54          18 :             .ToLocalChecked());
      55             :     return nullptr;
      56             :   }
      57             : 
      58             :   Local<External> wrap = Local<External>::Cast(hook->GetInternalField(0));
      59          90 :   void* ptr = wrap->Value();
      60             :   return static_cast<AsyncHooksWrap*>(ptr);
      61             : }
      62             : 
      63          90 : static void EnableHook(const v8::FunctionCallbackInfo<v8::Value>& args) {
      64          90 :   AsyncHooksWrap* wrap = UnwrapHook(args);
      65          90 :   if (wrap) {
      66             :     wrap->Enable();
      67             :   }
      68          90 : }
      69             : 
      70          18 : static void DisableHook(const v8::FunctionCallbackInfo<v8::Value>& args) {
      71          18 :   AsyncHooksWrap* wrap = UnwrapHook(args);
      72          18 :   if (wrap) {
      73             :     wrap->Disable();
      74             :   }
      75          18 : }
      76             : 
      77          45 : async_id_t AsyncHooks::GetExecutionAsyncId() const {
      78          45 :   return asyncContexts.top().execution_async_id;
      79             : }
      80             : 
      81          45 : async_id_t AsyncHooks::GetTriggerAsyncId() const {
      82          45 :   return asyncContexts.top().trigger_async_id;
      83             : }
      84             : 
      85         117 : Local<Object> AsyncHooks::CreateHook(
      86             :     const v8::FunctionCallbackInfo<v8::Value>& args) {
      87             :   Isolate* isolate = args.GetIsolate();
      88         117 :   EscapableHandleScope handle_scope(isolate);
      89             : 
      90         117 :   Local<Context> currentContext = isolate->GetCurrentContext();
      91             : 
      92         225 :   if (args.Length() != 1 || !args[0]->IsObject()) {
      93             :     isolate->ThrowException(
      94          27 :         String::NewFromUtf8(isolate, "Invalid arguments passed to createHook",
      95             :                             NewStringType::kNormal)
      96          27 :             .ToLocalChecked());
      97          27 :     return Local<Object>();
      98             :   }
      99             : 
     100         180 :   AsyncHooksWrap* wrap = new AsyncHooksWrap(isolate);
     101             : 
     102             :   Local<Object> fn_obj = args[0].As<Object>();
     103             : 
     104             : #define SET_HOOK_FN(name)                                                   \
     105             :   Local<Value> name##_v =                                                   \
     106             :       fn_obj                                                                \
     107             :           ->Get(currentContext,                                             \
     108             :                 String::NewFromUtf8(isolate, #name, NewStringType::kNormal) \
     109             :                     .ToLocalChecked())                                      \
     110             :           .ToLocalChecked();                                                \
     111             :   if (name##_v->IsFunction()) {                                             \
     112             :     wrap->set_##name##_function(name##_v.As<Function>());                   \
     113             :   }
     114             : 
     115         270 :   SET_HOOK_FN(init);
     116         270 :   SET_HOOK_FN(before);
     117         270 :   SET_HOOK_FN(after);
     118         270 :   SET_HOOK_FN(promiseResolve);
     119             : #undef SET_HOOK_FN
     120             : 
     121          90 :   async_wraps_.push_back(wrap);
     122             : 
     123             :   Local<Object> obj = async_hooks_templ.Get(isolate)
     124          90 :                           ->NewInstance(currentContext)
     125             :                           .ToLocalChecked();
     126         180 :   obj->SetInternalField(0, External::New(isolate, wrap));
     127             : 
     128             :   return handle_scope.Escape(obj);
     129             : }
     130             : 
     131         792 : void AsyncHooks::ShellPromiseHook(PromiseHookType type, Local<Promise> promise,
     132             :                                   Local<Value> parent) {
     133             :   AsyncHooks* hooks =
     134         792 :       PerIsolateData::Get(promise->GetIsolate())->GetAsyncHooks();
     135             : 
     136        1584 :   HandleScope handle_scope(hooks->isolate_);
     137             : 
     138         792 :   Local<Context> currentContext = hooks->isolate_->GetCurrentContext();
     139             : 
     140         792 :   if (type == PromiseHookType::kInit) {
     141         270 :     ++hooks->current_async_id;
     142             :     Local<Integer> async_id =
     143         270 :         Integer::New(hooks->isolate_, hooks->current_async_id);
     144             : 
     145         810 :     CHECK(!promise
     146             :                ->HasPrivate(currentContext,
     147             :                             hooks->async_id_smb.Get(hooks->isolate_))
     148             :                .ToChecked());
     149             :     promise->SetPrivate(currentContext,
     150         540 :                         hooks->async_id_smb.Get(hooks->isolate_), async_id);
     151             : 
     152         270 :     if (parent->IsPromise()) {
     153             :       Local<Promise> parent_promise = parent.As<Promise>();
     154             :       Local<Value> parent_async_id =
     155             :           parent_promise
     156         288 :               ->GetPrivate(hooks->isolate_->GetCurrentContext(),
     157         288 :                            hooks->async_id_smb.Get(hooks->isolate_))
     158         144 :               .ToLocalChecked();
     159             :       promise->SetPrivate(currentContext,
     160             :                           hooks->trigger_id_smb.Get(hooks->isolate_),
     161         288 :                           parent_async_id);
     162             :     } else {
     163         126 :       CHECK(parent->IsUndefined());
     164         126 :       Local<Integer> trigger_id = Integer::New(hooks->isolate_, 0);
     165             :       promise->SetPrivate(currentContext,
     166             :                           hooks->trigger_id_smb.Get(hooks->isolate_),
     167         252 :                           trigger_id);
     168             :     }
     169         522 :   } else if (type == PromiseHookType::kBefore) {
     170             :     AsyncContext ctx;
     171             :     ctx.execution_async_id =
     172             :         promise
     173         252 :             ->GetPrivate(hooks->isolate_->GetCurrentContext(),
     174         252 :                          hooks->async_id_smb.Get(hooks->isolate_))
     175             :             .ToLocalChecked()
     176             :             .As<Integer>()
     177         126 :             ->Value();
     178             :     ctx.trigger_async_id =
     179             :         promise
     180         252 :             ->GetPrivate(hooks->isolate_->GetCurrentContext(),
     181         252 :                          hooks->trigger_id_smb.Get(hooks->isolate_))
     182             :             .ToLocalChecked()
     183             :             .As<Integer>()
     184         126 :             ->Value();
     185             :     hooks->asyncContexts.push(ctx);
     186         396 :   } else if (type == PromiseHookType::kAfter) {
     187             :     hooks->asyncContexts.pop();
     188             :   }
     189             : 
     190        2274 :   for (AsyncHooksWrap* wrap : hooks->async_wraps_) {
     191        1482 :     PromiseHookDispatch(type, promise, parent, wrap, hooks);
     192             :   }
     193         792 : }
     194             : 
     195          50 : void AsyncHooks::Initialize() {
     196         100 :   HandleScope handle_scope(isolate_);
     197             : 
     198         100 :   async_hook_ctor.Reset(isolate_, FunctionTemplate::New(isolate_));
     199          50 :   async_hook_ctor.Get(isolate_)->SetClassName(
     200          50 :       String::NewFromUtf8(isolate_, "AsyncHook", NewStringType::kNormal)
     201          50 :           .ToLocalChecked());
     202             : 
     203          50 :   async_hooks_templ.Reset(isolate_,
     204         150 :                           async_hook_ctor.Get(isolate_)->InstanceTemplate());
     205         100 :   async_hooks_templ.Get(isolate_)->SetInternalFieldCount(1);
     206         150 :   async_hooks_templ.Get(isolate_)->Set(
     207          50 :       String::NewFromUtf8(isolate_, "enable", v8::NewStringType::kNormal)
     208             :           .ToLocalChecked(),
     209          50 :       FunctionTemplate::New(isolate_, EnableHook));
     210         150 :   async_hooks_templ.Get(isolate_)->Set(
     211          50 :       String::NewFromUtf8(isolate_, "disable", v8::NewStringType::kNormal)
     212             :           .ToLocalChecked(),
     213          50 :       FunctionTemplate::New(isolate_, DisableHook));
     214             : 
     215         100 :   async_id_smb.Reset(isolate_, Private::New(isolate_));
     216         100 :   trigger_id_smb.Reset(isolate_, Private::New(isolate_));
     217             : 
     218          50 :   isolate_->SetPromiseHook(ShellPromiseHook);
     219          50 : }
     220             : 
     221          50 : void AsyncHooks::Deinitialize() {
     222          50 :   isolate_->SetPromiseHook(nullptr);
     223         140 :   for (AsyncHooksWrap* wrap : async_wraps_) {
     224         180 :     delete wrap;
     225             :   }
     226          50 : }
     227             : 
     228        1482 : void AsyncHooks::PromiseHookDispatch(PromiseHookType type,
     229             :                                      Local<Promise> promise,
     230             :                                      Local<Value> parent, AsyncHooksWrap* wrap,
     231             :                                      AsyncHooks* hooks) {
     232        1482 :   if (!wrap->IsEnabled()) {
     233         132 :     return;
     234             :   }
     235             : 
     236        2700 :   HandleScope handle_scope(hooks->isolate_);
     237             : 
     238        2700 :   TryCatch try_catch(hooks->isolate_);
     239        1350 :   try_catch.SetVerbose(true);
     240             : 
     241        1350 :   i::Isolate* isolate = reinterpret_cast<i::Isolate*>(hooks->isolate_);
     242        1350 :   if (isolate->has_scheduled_exception()) {
     243           0 :     isolate->ScheduleThrow(isolate->scheduled_exception());
     244             : 
     245             :     DCHECK(try_catch.HasCaught());
     246           0 :     Shell::ReportException(hooks->isolate_, &try_catch);
     247           0 :     return;
     248             :   }
     249             : 
     250             :   Local<Value> rcv = Undefined(hooks->isolate_);
     251        1350 :   Local<Context> context = hooks->isolate_->GetCurrentContext();
     252             :   Local<Value> async_id =
     253        2700 :       promise->GetPrivate(context, hooks->async_id_smb.Get(hooks->isolate_))
     254             :           .ToLocalChecked();
     255        1350 :   Local<Value> args[1] = {async_id};
     256             : 
     257             :   // This is unused. It's here to silence the warning about
     258             :   // not using the MaybeLocal return value from Call.
     259             :   MaybeLocal<Value> result;
     260             : 
     261             :   // Sacrifice the brevity for readability and debugfulness
     262        1350 :   if (type == PromiseHookType::kInit) {
     263         466 :     if (!wrap->init_function().IsEmpty()) {
     264             :       Local<Value> initArgs[4] = {
     265             :           async_id,
     266         333 :           String::NewFromUtf8(hooks->isolate_, "PROMISE",
     267         333 :                               NewStringType::kNormal)
     268             :               .ToLocalChecked(),
     269             :           promise
     270         666 :               ->GetPrivate(context, hooks->trigger_id_smb.Get(hooks->isolate_))
     271             :               .ToLocalChecked(),
     272         666 :           promise};
     273         333 :       result = wrap->init_function()->Call(context, rcv, 4, initArgs);
     274             :     }
     275         884 :   } else if (type == PromiseHookType::kBefore) {
     276         209 :     if (!wrap->before_function().IsEmpty()) {
     277          38 :       result = wrap->before_function()->Call(context, rcv, 1, args);
     278             :     }
     279         675 :   } else if (type == PromiseHookType::kAfter) {
     280         209 :     if (!wrap->after_function().IsEmpty()) {
     281          95 :       result = wrap->after_function()->Call(context, rcv, 1, args);
     282             :     }
     283         466 :   } else if (type == PromiseHookType::kResolve) {
     284         466 :     if (!wrap->promiseResolve_function().IsEmpty()) {
     285          57 :       result = wrap->promiseResolve_function()->Call(context, rcv, 1, args);
     286             :     }
     287             :   }
     288             : 
     289        1350 :   if (try_catch.HasCaught()) {
     290          57 :     Shell::ReportException(hooks->isolate_, &try_catch);
     291             :   }
     292             : }
     293             : 
     294       60196 : }  // namespace v8

Generated by: LCOV version 1.10