OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "core/inspector/PromiseTracker.h" | |
6 | |
7 #include "bindings/core/v8/ScriptCallStackFactory.h" | |
8 #include "core/inspector/ScriptAsyncCallStack.h" | |
9 #include "wtf/CurrentTime.h" | |
10 #include "wtf/PassOwnPtr.h" | |
11 #include "wtf/WeakPtr.h" | |
12 | |
13 using blink::TypeBuilder::Array; | |
14 using blink::TypeBuilder::Console::CallFrame; | |
15 using blink::TypeBuilder::Debugger::PromiseDetails; | |
16 | |
17 namespace blink { | |
18 | |
19 class PromiseTracker::PromiseWeakCallbackData final { | |
20 WTF_MAKE_NONCOPYABLE(PromiseWeakCallbackData); | |
21 public: | |
22 PromiseWeakCallbackData(PromiseTracker* tracker, int id) | |
23 : m_tracker(tracker->m_weakPtrFactory.createWeakPtr()) | |
24 , m_id(id) | |
25 { | |
26 } | |
27 | |
28 ~PromiseWeakCallbackData() | |
29 { | |
30 if (!m_tracker) | |
31 return; | |
32 RefPtr<PromiseDetails> promiseDetails = PromiseDetails::create().setId(m
_id); | |
33 m_tracker->m_listener->didUpdatePromise(InspectorFrontend::Debugger::Eve
ntType::Gc, promiseDetails.release()); | |
34 } | |
35 | |
36 WeakPtr<PromiseTracker> m_tracker; | |
37 int m_id; | |
38 }; | |
39 | |
40 PromiseTracker::IdToPromiseMapTraits::WeakCallbackDataType* PromiseTracker::IdTo
PromiseMapTraits::WeakCallbackParameter(MapType* map, int key, v8::Local<v8::Obj
ect>& value) | |
41 { | |
42 // This method is called when promise is added into the map, hence the map m
ust be alive at this point. The tracker in turn must be alive too. | |
43 PromiseTracker* tracker = reinterpret_cast<PromiseTracker*>(reinterpret_cast
<intptr_t>(map) - offsetof(PromiseTracker, m_idToPromise)); | |
44 return new PromiseWeakCallbackData(tracker, key); | |
45 } | |
46 | |
47 void PromiseTracker::IdToPromiseMapTraits::DisposeCallbackData(WeakCallbackDataT
ype* callbackData) | |
48 { | |
49 delete callbackData; | |
50 } | |
51 | |
52 void PromiseTracker::IdToPromiseMapTraits::DisposeWeak(const v8::WeakCallbackInf
o<WeakCallbackDataType>& data) | |
53 { | |
54 delete data.GetParameter(); | |
55 } | |
56 | |
57 PromiseTracker::IdToPromiseMapTraits::MapType* PromiseTracker::IdToPromiseMapTra
its::MapFromWeakCallbackInfo(const v8::WeakCallbackInfo<WeakCallbackDataType>& i
nfo) | |
58 { | |
59 return &info.GetParameter()->m_tracker->m_idToPromise; | |
60 } | |
61 | |
62 int PromiseTracker::IdToPromiseMapTraits::KeyFromWeakCallbackInfo(const v8::Weak
CallbackInfo<WeakCallbackDataType>& info) | |
63 { | |
64 return info.GetParameter()->m_id; | |
65 } | |
66 | |
67 PromiseTracker::PromiseTracker(Listener* listener, v8::Isolate* isolate) | |
68 : m_circularSequentialId(0) | |
69 , m_isEnabled(false) | |
70 , m_captureStacks(false) | |
71 , m_listener(listener) | |
72 , m_isolate(isolate) | |
73 , m_weakPtrFactory(this) | |
74 , m_idToPromise(isolate) | |
75 { | |
76 clear(); | |
77 } | |
78 | |
79 PromiseTracker::~PromiseTracker() | |
80 { | |
81 } | |
82 | |
83 void PromiseTracker::setEnabled(bool enabled, bool captureStacks) | |
84 { | |
85 m_isEnabled = enabled; | |
86 m_captureStacks = captureStacks; | |
87 if (!enabled) | |
88 clear(); | |
89 } | |
90 | |
91 void PromiseTracker::clear() | |
92 { | |
93 v8::HandleScope scope(m_isolate); | |
94 m_promiseToId.Reset(m_isolate, v8::NativeWeakMap::New(m_isolate)); | |
95 m_idToPromise.Clear(); | |
96 } | |
97 | |
98 int PromiseTracker::circularSequentialId() | |
99 { | |
100 ++m_circularSequentialId; | |
101 if (m_circularSequentialId <= 0) | |
102 m_circularSequentialId = 1; | |
103 return m_circularSequentialId; | |
104 } | |
105 | |
106 int PromiseTracker::promiseId(v8::Local<v8::Object> promise, bool* isNewPromise) | |
107 { | |
108 v8::HandleScope scope(m_isolate); | |
109 v8::Local<v8::NativeWeakMap> map = v8::Local<v8::NativeWeakMap>::New(m_isola
te, m_promiseToId); | |
110 v8::Local<v8::Value> value = map->Get(promise); | |
111 if (value->IsInt32()) { | |
112 *isNewPromise = false; | |
113 return value.As<v8::Int32>()->Value(); | |
114 } | |
115 *isNewPromise = true; | |
116 int id = circularSequentialId(); | |
117 map->Set(promise, v8::Int32::New(m_isolate, id)); | |
118 m_idToPromise.Set(id, promise); | |
119 return id; | |
120 } | |
121 | |
122 void PromiseTracker::didReceiveV8PromiseEvent(v8::Local<v8::Context> context, v8
::Local<v8::Object> promise, v8::Local<v8::Value> parentPromise, int status) | |
123 { | |
124 ASSERT(isEnabled()); | |
125 ASSERT(!context.IsEmpty()); | |
126 | |
127 bool isNewPromise = false; | |
128 int id = promiseId(promise, &isNewPromise); | |
129 | |
130 InspectorFrontend::Debugger::EventType::Enum eventType = isNewPromise ? Insp
ectorFrontend::Debugger::EventType::New : InspectorFrontend::Debugger::EventType
::Update; | |
131 | |
132 PromiseDetails::Status::Enum promiseStatus; | |
133 switch (status) { | |
134 case 0: | |
135 promiseStatus = PromiseDetails::Status::Pending; | |
136 break; | |
137 case 1: | |
138 promiseStatus = PromiseDetails::Status::Resolved; | |
139 break; | |
140 default: | |
141 promiseStatus = PromiseDetails::Status::Rejected; | |
142 }; | |
143 RefPtr<PromiseDetails> promiseDetails = PromiseDetails::create().setId(id); | |
144 promiseDetails->setStatus(promiseStatus); | |
145 | |
146 if (!parentPromise.IsEmpty() && parentPromise->IsObject()) { | |
147 v8::Local<v8::Object> handle = parentPromise->ToObject(context->GetIsola
te()); | |
148 bool parentIsNewPromise = false; | |
149 int parentPromiseId = promiseId(handle, &parentIsNewPromise); | |
150 promiseDetails->setParentId(parentPromiseId); | |
151 } else { | |
152 if (!status) { | |
153 if (isNewPromise) { | |
154 promiseDetails->setCreationTime(currentTimeMS()); | |
155 RefPtr<ScriptCallStack> stack = currentScriptCallStack(m_capture
Stacks ? ScriptCallStack::maxCallStackSizeToCapture : 1); | |
156 if (stack) { | |
157 if (stack->size()) { | |
158 promiseDetails->setCallFrame(stack->at(0).buildInspector
Object()); | |
159 if (m_captureStacks) | |
160 promiseDetails->setCreationStack(stack->buildInspect
orArray()); | |
161 } | |
162 RefPtr<ScriptAsyncCallStack> asyncCallStack = stack->asyncCa
llStack(); | |
163 if (m_captureStacks && asyncCallStack) | |
164 promiseDetails->setAsyncCreationStack(asyncCallStack->bu
ildInspectorObject()); | |
165 } | |
166 } | |
167 } else { | |
168 promiseDetails->setSettlementTime(currentTimeMS()); | |
169 if (m_captureStacks) { | |
170 RefPtr<ScriptCallStack> stack = currentScriptCallStack(ScriptCal
lStack::maxCallStackSizeToCapture); | |
171 if (stack) { | |
172 if (stack->size()) | |
173 promiseDetails->setSettlementStack(stack->buildInspector
Array()); | |
174 if (RefPtr<ScriptAsyncCallStack> asyncCallStack = stack->asy
ncCallStack()) | |
175 promiseDetails->setAsyncSettlementStack(asyncCallStack->
buildInspectorObject()); | |
176 } | |
177 } | |
178 } | |
179 } | |
180 | |
181 m_listener->didUpdatePromise(eventType, promiseDetails.release()); | |
182 } | |
183 | |
184 v8::Local<v8::Object> PromiseTracker::promiseById(int promiseId) | |
185 { | |
186 ASSERT(isEnabled()); | |
187 return m_idToPromise.Get(promiseId); | |
188 } | |
189 | |
190 } // namespace blink | |
OLD | NEW |