1 // Copyright 2014 The Chromium 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.
6 #include "core/inspector/PromiseTracker.h"
8 #include "bindings/core/v8/ScriptCallStackFactory.h"
9 #include "bindings/core/v8/ScriptState.h"
10 #include "bindings/core/v8/ScriptValue.h"
11 #include "core/inspector/ScriptAsyncCallStack.h"
12 #include "wtf/CurrentTime.h"
13 #include "wtf/PassOwnPtr.h"
14 #include "wtf/WeakPtr.h"
16 using blink::TypeBuilder::Array
;
17 using blink::TypeBuilder::Console::CallFrame
;
18 using blink::TypeBuilder::Debugger::PromiseDetails
;
22 class PromiseTracker::PromiseWeakCallbackData final
{
23 WTF_MAKE_NONCOPYABLE(PromiseWeakCallbackData
);
25 PromiseWeakCallbackData(PromiseTracker
* tracker
, int id
)
29 : m_tracker(tracker
->m_weakPtrFactory
.createWeakPtr())
35 ~PromiseWeakCallbackData()
39 RefPtr
<PromiseDetails
> promiseDetails
= PromiseDetails::create().setId(m_id
);
40 m_tracker
->m_listener
->didUpdatePromise(InspectorFrontend::Debugger::EventType::Gc
, promiseDetails
.release());
43 WeakPtrWillBeWeakPersistent
<PromiseTracker
> m_tracker
;
47 PromiseTracker::IdToPromiseMapTraits::WeakCallbackDataType
* PromiseTracker::IdToPromiseMapTraits::WeakCallbackParameter(MapType
* map
, int key
, v8::Local
<v8::Object
>& value
)
49 // This method is called when promise is added into the map, hence the map must be alive at this point. The tracker in turn must be alive too.
50 PromiseTracker
* tracker
= reinterpret_cast<PromiseTracker
*>(reinterpret_cast<intptr_t>(map
) - offsetof(PromiseTracker
, m_idToPromise
));
51 return new PromiseWeakCallbackData(tracker
, key
);
54 void PromiseTracker::IdToPromiseMapTraits::DisposeCallbackData(WeakCallbackDataType
* callbackData
)
59 void PromiseTracker::IdToPromiseMapTraits::DisposeWeak(const v8::WeakCallbackInfo
<WeakCallbackDataType
>& data
)
61 delete data
.GetParameter();
64 PromiseTracker::IdToPromiseMapTraits::MapType
* PromiseTracker::IdToPromiseMapTraits::MapFromWeakCallbackInfo(const v8::WeakCallbackInfo
<WeakCallbackDataType
>& info
)
66 return &info
.GetParameter()->m_tracker
->m_idToPromise
;
69 int PromiseTracker::IdToPromiseMapTraits::KeyFromWeakCallbackInfo(const v8::WeakCallbackInfo
<WeakCallbackDataType
>& info
)
71 return info
.GetParameter()->m_id
;
74 PromiseTracker::PromiseTracker(Listener
* listener
, v8::Isolate
* isolate
)
75 : m_circularSequentialId(0)
77 , m_captureStacks(false)
78 , m_listener(listener
)
81 , m_weakPtrFactory(this)
83 , m_idToPromise(isolate
)
88 PromiseTracker::~PromiseTracker()
92 DEFINE_TRACE(PromiseTracker
)
95 visitor
->trace(m_listener
);
99 void PromiseTracker::setEnabled(bool enabled
, bool captureStacks
)
101 m_isEnabled
= enabled
;
102 m_captureStacks
= captureStacks
;
107 void PromiseTracker::clear()
109 v8::HandleScope
scope(m_isolate
);
110 m_promiseToId
.Reset(m_isolate
, v8::NativeWeakMap::New(m_isolate
));
111 m_idToPromise
.Clear();
114 int PromiseTracker::circularSequentialId()
116 ++m_circularSequentialId
;
117 if (m_circularSequentialId
<= 0)
118 m_circularSequentialId
= 1;
119 return m_circularSequentialId
;
122 int PromiseTracker::promiseId(v8::Local
<v8::Object
> promise
, bool* isNewPromise
)
124 v8::HandleScope
scope(m_isolate
);
125 v8::Local
<v8::NativeWeakMap
> map
= v8::Local
<v8::NativeWeakMap
>::New(m_isolate
, m_promiseToId
);
126 v8::Local
<v8::Value
> value
= map
->Get(promise
);
127 if (value
->IsInt32()) {
128 *isNewPromise
= false;
129 return value
.As
<v8::Int32
>()->Value();
131 *isNewPromise
= true;
132 int id
= circularSequentialId();
133 map
->Set(promise
, v8::Int32::New(m_isolate
, id
));
134 m_idToPromise
.Set(id
, promise
);
138 void PromiseTracker::didReceiveV8PromiseEvent(ScriptState
* scriptState
, v8::Local
<v8::Object
> promise
, v8::Local
<v8::Value
> parentPromise
, int status
)
141 ASSERT(scriptState
->contextIsValid());
143 bool isNewPromise
= false;
144 int id
= promiseId(promise
, &isNewPromise
);
146 ScriptState::Scope
scope(scriptState
);
147 InspectorFrontend::Debugger::EventType::Enum eventType
= isNewPromise
? InspectorFrontend::Debugger::EventType::New
: InspectorFrontend::Debugger::EventType::Update
;
149 PromiseDetails::Status::Enum promiseStatus
;
152 promiseStatus
= PromiseDetails::Status::Pending
;
155 promiseStatus
= PromiseDetails::Status::Resolved
;
158 promiseStatus
= PromiseDetails::Status::Rejected
;
160 RefPtr
<PromiseDetails
> promiseDetails
= PromiseDetails::create().setId(id
);
161 promiseDetails
->setStatus(promiseStatus
);
163 if (!parentPromise
.IsEmpty() && parentPromise
->IsObject()) {
164 v8::Local
<v8::Object
> handle
= parentPromise
->ToObject(scriptState
->isolate());
165 bool parentIsNewPromise
= false;
166 int parentPromiseId
= promiseId(handle
, &parentIsNewPromise
);
167 promiseDetails
->setParentId(parentPromiseId
);
171 promiseDetails
->setCreationTime(currentTimeMS());
172 RefPtrWillBeRawPtr
<ScriptCallStack
> stack
= currentScriptCallStack(m_captureStacks
? ScriptCallStack::maxCallStackSizeToCapture
: 1);
175 promiseDetails
->setCallFrame(stack
->at(0).buildInspectorObject());
177 promiseDetails
->setCreationStack(stack
->buildInspectorArray());
179 RefPtrWillBeRawPtr
<ScriptAsyncCallStack
> asyncCallStack
= stack
->asyncCallStack();
180 if (m_captureStacks
&& asyncCallStack
)
181 promiseDetails
->setAsyncCreationStack(asyncCallStack
->buildInspectorObject());
185 promiseDetails
->setSettlementTime(currentTimeMS());
186 if (m_captureStacks
) {
187 RefPtrWillBeRawPtr
<ScriptCallStack
> stack
= currentScriptCallStack(ScriptCallStack::maxCallStackSizeToCapture
);
190 promiseDetails
->setSettlementStack(stack
->buildInspectorArray());
191 if (RefPtrWillBeRawPtr
<ScriptAsyncCallStack
> asyncCallStack
= stack
->asyncCallStack())
192 promiseDetails
->setAsyncSettlementStack(asyncCallStack
->buildInspectorObject());
198 m_listener
->didUpdatePromise(eventType
, promiseDetails
.release());
201 ScriptValue
PromiseTracker::promiseById(int promiseId
)
204 v8::HandleScope
scope(m_isolate
);
205 v8::Local
<v8::Object
> value
= m_idToPromise
.Get(promiseId
);
207 return ScriptValue();
208 return ScriptValue(ScriptState::from(value
->CreationContext()) , value
);