mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
This doesn't fully separate v8_inspector from core, but it does lay the path. The next steps to removing v8_inspector is to remove all the # FIXME: Remove lines in the deps section of v8_inspector/BUILD.gn. gn check out/Debug v8_inspector will tell us if we've successfully removed all the dependencies. It's unclear if we want to remove the wtf dependency, but definitely all of the engine/core dependencies should be removed and presumably replaced with abstract interfaces which can be provided to v8_inspector by its host. Given the size of this patch (and that it's largely mechanical) I plan to TBR it. Most of this was done with tools/git/move_source_file.py TBR=yurys@chromium.org Review URL: https://codereview.chromium.org/772563003
402 lines
15 KiB
C++
402 lines
15 KiB
C++
/*
|
|
* Copyright (C) 2013 Google Inc. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following disclaimer
|
|
* in the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* * Neither the name of Google Inc. nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "sky/engine/config.h"
|
|
#include "sky/engine/v8_inspector/AsyncCallStackTracker.h"
|
|
|
|
#include "sky/engine/bindings/core/v8/V8Binding.h"
|
|
#include "sky/engine/bindings/core/v8/V8RecursionScope.h"
|
|
#include "sky/engine/core/dom/ExecutionContext.h"
|
|
// #include "core/dom/ExecutionContextTask.h"
|
|
#include "sky/engine/core/events/Event.h"
|
|
#include "sky/engine/core/events/EventTarget.h"
|
|
#include "sky/engine/wtf/text/StringBuilder.h"
|
|
#include "sky/engine/wtf/text/StringHash.h"
|
|
#include "v8/include/v8.h"
|
|
|
|
namespace {
|
|
|
|
static const char setTimeoutName[] = "setTimeout";
|
|
static const char setIntervalName[] = "setInterval";
|
|
static const char requestAnimationFrameName[] = "requestAnimationFrame";
|
|
static const char enqueueMutationRecordName[] = "Mutation";
|
|
|
|
}
|
|
|
|
namespace blink {
|
|
|
|
void AsyncCallStackTracker::ExecutionContextData::contextDestroyed()
|
|
{
|
|
ASSERT(executionContext());
|
|
OwnPtr<ExecutionContextData> self = m_tracker->m_executionContextDataMap.take(executionContext());
|
|
ASSERT_UNUSED(self, self == this);
|
|
ContextLifecycleObserver::contextDestroyed();
|
|
}
|
|
|
|
int AsyncCallStackTracker::ExecutionContextData::circularSequentialID()
|
|
{
|
|
++m_circularSequentialID;
|
|
if (m_circularSequentialID <= 0)
|
|
m_circularSequentialID = 1;
|
|
return m_circularSequentialID;
|
|
}
|
|
|
|
AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description, const ScriptValue& callFrames)
|
|
: m_description(description)
|
|
, m_callFrames(callFrames)
|
|
{
|
|
}
|
|
|
|
AsyncCallStackTracker::AsyncCallStack::~AsyncCallStack()
|
|
{
|
|
}
|
|
|
|
AsyncCallStackTracker::AsyncCallStackTracker()
|
|
: m_maxAsyncCallStackDepth(0)
|
|
, m_nestedAsyncCallCount(0)
|
|
{
|
|
}
|
|
|
|
void AsyncCallStackTracker::setAsyncCallStackDepth(int depth)
|
|
{
|
|
if (depth <= 0) {
|
|
m_maxAsyncCallStackDepth = 0;
|
|
clear();
|
|
} else {
|
|
m_maxAsyncCallStackDepth = depth;
|
|
}
|
|
}
|
|
|
|
const AsyncCallStackTracker::AsyncCallChain* AsyncCallStackTracker::currentAsyncCallChain() const
|
|
{
|
|
if (m_currentAsyncCallChain)
|
|
ensureMaxAsyncCallChainDepth(m_currentAsyncCallChain.get(), m_maxAsyncCallStackDepth);
|
|
return m_currentAsyncCallChain.get();
|
|
}
|
|
|
|
void AsyncCallStackTracker::didInstallTimer(ExecutionContext* context, int timerId, bool singleShot, const ScriptValue& callFrames)
|
|
{
|
|
ASSERT(context);
|
|
ASSERT(isEnabled());
|
|
if (!validateCallFrames(callFrames))
|
|
return;
|
|
ASSERT(timerId > 0);
|
|
ExecutionContextData* data = createContextDataIfNeeded(context);
|
|
data->m_timerCallChains.set(timerId, createAsyncCallChain(singleShot ? setTimeoutName : setIntervalName, callFrames));
|
|
if (!singleShot)
|
|
data->m_intervalTimerIds.add(timerId);
|
|
}
|
|
|
|
void AsyncCallStackTracker::didRemoveTimer(ExecutionContext* context, int timerId)
|
|
{
|
|
ASSERT(context);
|
|
ASSERT(isEnabled());
|
|
if (timerId <= 0)
|
|
return;
|
|
ExecutionContextData* data = m_executionContextDataMap.get(context);
|
|
if (!data)
|
|
return;
|
|
data->m_intervalTimerIds.remove(timerId);
|
|
data->m_timerCallChains.remove(timerId);
|
|
}
|
|
|
|
void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId)
|
|
{
|
|
ASSERT(context);
|
|
ASSERT(isEnabled());
|
|
ASSERT(timerId > 0);
|
|
ASSERT(!m_currentAsyncCallChain);
|
|
if (ExecutionContextData* data = m_executionContextDataMap.get(context)) {
|
|
if (data->m_intervalTimerIds.contains(timerId))
|
|
setCurrentAsyncCallChain(context, data->m_timerCallChains.get(timerId));
|
|
else
|
|
setCurrentAsyncCallChain(context, data->m_timerCallChains.take(timerId));
|
|
} else {
|
|
setCurrentAsyncCallChain(context, nullptr);
|
|
}
|
|
}
|
|
|
|
void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context, int callbackId, const ScriptValue& callFrames)
|
|
{
|
|
ASSERT(context);
|
|
ASSERT(isEnabled());
|
|
if (!validateCallFrames(callFrames))
|
|
return;
|
|
ASSERT(callbackId > 0);
|
|
ExecutionContextData* data = createContextDataIfNeeded(context);
|
|
data->m_animationFrameCallChains.set(callbackId, createAsyncCallChain(requestAnimationFrameName, callFrames));
|
|
}
|
|
|
|
void AsyncCallStackTracker::didCancelAnimationFrame(ExecutionContext* context, int callbackId)
|
|
{
|
|
ASSERT(context);
|
|
ASSERT(isEnabled());
|
|
if (callbackId <= 0)
|
|
return;
|
|
if (ExecutionContextData* data = m_executionContextDataMap.get(context))
|
|
data->m_animationFrameCallChains.remove(callbackId);
|
|
}
|
|
|
|
void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, int callbackId)
|
|
{
|
|
ASSERT(context);
|
|
ASSERT(isEnabled());
|
|
ASSERT(callbackId > 0);
|
|
ASSERT(!m_currentAsyncCallChain);
|
|
if (ExecutionContextData* data = m_executionContextDataMap.get(context))
|
|
setCurrentAsyncCallChain(context, data->m_animationFrameCallChains.take(callbackId));
|
|
else
|
|
setCurrentAsyncCallChain(context, nullptr);
|
|
}
|
|
|
|
void AsyncCallStackTracker::didEnqueueEvent(EventTarget* eventTarget, Event* event, const ScriptValue& callFrames)
|
|
{
|
|
ASSERT(eventTarget->executionContext());
|
|
ASSERT(isEnabled());
|
|
if (!validateCallFrames(callFrames))
|
|
return;
|
|
ExecutionContextData* data = createContextDataIfNeeded(eventTarget->executionContext());
|
|
data->m_eventCallChains.set(event, createAsyncCallChain(event->type(), callFrames));
|
|
}
|
|
|
|
void AsyncCallStackTracker::didRemoveEvent(EventTarget* eventTarget, Event* event)
|
|
{
|
|
ASSERT(eventTarget->executionContext());
|
|
ASSERT(isEnabled());
|
|
if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->executionContext()))
|
|
data->m_eventCallChains.remove(event);
|
|
}
|
|
|
|
void AsyncCallStackTracker::willHandleEvent(EventTarget* eventTarget, Event* event, EventListener* listener, bool useCapture)
|
|
{
|
|
ASSERT(eventTarget->executionContext());
|
|
ASSERT(isEnabled());
|
|
ExecutionContext* context = eventTarget->executionContext();
|
|
if (ExecutionContextData* data = m_executionContextDataMap.get(context))
|
|
setCurrentAsyncCallChain(context, data->m_eventCallChains.get(event));
|
|
else
|
|
setCurrentAsyncCallChain(context, nullptr);
|
|
}
|
|
|
|
void AsyncCallStackTracker::didEnqueueMutationRecord(ExecutionContext* context, MutationObserver* observer, const ScriptValue& callFrames)
|
|
{
|
|
ASSERT(context);
|
|
ASSERT(isEnabled());
|
|
if (!validateCallFrames(callFrames))
|
|
return;
|
|
ExecutionContextData* data = createContextDataIfNeeded(context);
|
|
data->m_mutationObserverCallChains.set(observer, createAsyncCallChain(enqueueMutationRecordName, callFrames));
|
|
}
|
|
|
|
bool AsyncCallStackTracker::hasEnqueuedMutationRecord(ExecutionContext* context, MutationObserver* observer)
|
|
{
|
|
ASSERT(context);
|
|
ASSERT(isEnabled());
|
|
if (ExecutionContextData* data = m_executionContextDataMap.get(context))
|
|
return data->m_mutationObserverCallChains.contains(observer);
|
|
return false;
|
|
}
|
|
|
|
void AsyncCallStackTracker::didClearAllMutationRecords(ExecutionContext* context, MutationObserver* observer)
|
|
{
|
|
ASSERT(context);
|
|
ASSERT(isEnabled());
|
|
if (ExecutionContextData* data = m_executionContextDataMap.get(context))
|
|
data->m_mutationObserverCallChains.remove(observer);
|
|
}
|
|
|
|
void AsyncCallStackTracker::willDeliverMutationRecords(ExecutionContext* context, MutationObserver* observer)
|
|
{
|
|
ASSERT(context);
|
|
ASSERT(isEnabled());
|
|
if (ExecutionContextData* data = m_executionContextDataMap.get(context))
|
|
setCurrentAsyncCallChain(context, data->m_mutationObserverCallChains.take(observer));
|
|
else
|
|
setCurrentAsyncCallChain(context, nullptr);
|
|
}
|
|
|
|
// void AsyncCallStackTracker::didPostExecutionContextTask(ExecutionContext* context, ExecutionContextTask* task, const ScriptValue& callFrames)
|
|
// {
|
|
// ASSERT(context);
|
|
// ASSERT(isEnabled());
|
|
// if (!validateCallFrames(callFrames))
|
|
// return;
|
|
// ExecutionContextData* data = createContextDataIfNeeded(context);
|
|
// data->m_executionContextTaskCallChains.set(task, createAsyncCallChain(task->taskNameForInstrumentation(), callFrames));
|
|
// }
|
|
|
|
// void AsyncCallStackTracker::didKillAllExecutionContextTasks(ExecutionContext* context)
|
|
// {
|
|
// ASSERT(context);
|
|
// ASSERT(isEnabled());
|
|
// if (ExecutionContextData* data = m_executionContextDataMap.get(context))
|
|
// data->m_executionContextTaskCallChains.clear();
|
|
// }
|
|
|
|
// void AsyncCallStackTracker::willPerformExecutionContextTask(ExecutionContext* context, ExecutionContextTask* task)
|
|
// {
|
|
// ASSERT(context);
|
|
// ASSERT(isEnabled());
|
|
// if (ExecutionContextData* data = m_executionContextDataMap.get(context))
|
|
// setCurrentAsyncCallChain(context, data->m_executionContextTaskCallChains.take(task));
|
|
// else
|
|
// setCurrentAsyncCallChain(context, nullptr);
|
|
// }
|
|
|
|
static String makeV8AsyncTaskUniqueId(const String& eventName, int id)
|
|
{
|
|
StringBuilder builder;
|
|
builder.append(eventName);
|
|
builder.appendNumber(id);
|
|
return builder.toString();
|
|
}
|
|
|
|
void AsyncCallStackTracker::didEnqueueV8AsyncTask(ExecutionContext* context, const String& eventName, int id, const ScriptValue& callFrames)
|
|
{
|
|
ASSERT(context);
|
|
ASSERT(isEnabled());
|
|
if (!validateCallFrames(callFrames))
|
|
return;
|
|
ExecutionContextData* data = createContextDataIfNeeded(context);
|
|
data->m_v8AsyncTaskCallChains.set(makeV8AsyncTaskUniqueId(eventName, id), createAsyncCallChain(eventName, callFrames));
|
|
}
|
|
|
|
void AsyncCallStackTracker::willHandleV8AsyncTask(ExecutionContext* context, const String& eventName, int id)
|
|
{
|
|
ASSERT(context);
|
|
ASSERT(isEnabled());
|
|
if (ExecutionContextData* data = m_executionContextDataMap.get(context))
|
|
setCurrentAsyncCallChain(context, data->m_v8AsyncTaskCallChains.take(makeV8AsyncTaskUniqueId(eventName, id)));
|
|
else
|
|
setCurrentAsyncCallChain(context, nullptr);
|
|
}
|
|
|
|
int AsyncCallStackTracker::traceAsyncOperationStarting(ExecutionContext* context, const String& operationName, const ScriptValue& callFrames)
|
|
{
|
|
ASSERT(context);
|
|
ASSERT(isEnabled());
|
|
if (!validateCallFrames(callFrames))
|
|
return 0;
|
|
ExecutionContextData* data = createContextDataIfNeeded(context);
|
|
int id = data->circularSequentialID();
|
|
while (data->m_asyncOperationCallChains.contains(id))
|
|
id = data->circularSequentialID();
|
|
data->m_asyncOperationCallChains.set(id, createAsyncCallChain(operationName, callFrames));
|
|
return id;
|
|
}
|
|
|
|
void AsyncCallStackTracker::traceAsyncOperationCompleted(ExecutionContext* context, int operationId)
|
|
{
|
|
ASSERT(context);
|
|
ASSERT(isEnabled());
|
|
if (operationId <= 0)
|
|
return;
|
|
if (ExecutionContextData* data = m_executionContextDataMap.get(context))
|
|
data->m_asyncOperationCallChains.remove(operationId);
|
|
}
|
|
|
|
void AsyncCallStackTracker::traceAsyncCallbackStarting(ExecutionContext* context, int operationId)
|
|
{
|
|
ASSERT(context);
|
|
ASSERT(isEnabled());
|
|
if (ExecutionContextData* data = m_executionContextDataMap.get(context))
|
|
setCurrentAsyncCallChain(context, operationId > 0 ? data->m_asyncOperationCallChains.get(operationId) : nullptr);
|
|
else
|
|
setCurrentAsyncCallChain(context, nullptr);
|
|
}
|
|
|
|
void AsyncCallStackTracker::didFireAsyncCall()
|
|
{
|
|
clearCurrentAsyncCallChain();
|
|
}
|
|
|
|
PassRefPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createAsyncCallChain(const String& description, const ScriptValue& callFrames)
|
|
{
|
|
if (callFrames.isEmpty()) {
|
|
ASSERT(m_currentAsyncCallChain);
|
|
return m_currentAsyncCallChain; // Propogate async call stack chain.
|
|
}
|
|
RefPtr<AsyncCallChain> chain = adoptRef(m_currentAsyncCallChain ? new AsyncCallStackTracker::AsyncCallChain(*m_currentAsyncCallChain) : new AsyncCallStackTracker::AsyncCallChain());
|
|
ensureMaxAsyncCallChainDepth(chain.get(), m_maxAsyncCallStackDepth - 1);
|
|
chain->m_callStacks.prepend(adoptRef(new AsyncCallStackTracker::AsyncCallStack(description, callFrames)));
|
|
return chain.release();
|
|
}
|
|
|
|
void AsyncCallStackTracker::setCurrentAsyncCallChain(ExecutionContext* context, PassRefPtr<AsyncCallChain> chain)
|
|
{
|
|
if (chain && !V8RecursionScope::recursionLevel(toIsolate(context))) {
|
|
// Current AsyncCallChain corresponds to the bottommost JS call frame.
|
|
m_currentAsyncCallChain = chain;
|
|
m_nestedAsyncCallCount = 1;
|
|
} else {
|
|
if (m_currentAsyncCallChain)
|
|
++m_nestedAsyncCallCount;
|
|
}
|
|
}
|
|
|
|
void AsyncCallStackTracker::clearCurrentAsyncCallChain()
|
|
{
|
|
if (!m_nestedAsyncCallCount)
|
|
return;
|
|
--m_nestedAsyncCallCount;
|
|
if (!m_nestedAsyncCallCount)
|
|
m_currentAsyncCallChain.clear();
|
|
}
|
|
|
|
void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain, unsigned maxDepth)
|
|
{
|
|
while (chain->m_callStacks.size() > maxDepth)
|
|
chain->m_callStacks.removeLast();
|
|
}
|
|
|
|
bool AsyncCallStackTracker::validateCallFrames(const ScriptValue& callFrames)
|
|
{
|
|
return !callFrames.isEmpty() || m_currentAsyncCallChain;
|
|
}
|
|
|
|
AsyncCallStackTracker::ExecutionContextData* AsyncCallStackTracker::createContextDataIfNeeded(ExecutionContext* context)
|
|
{
|
|
ExecutionContextData* data = m_executionContextDataMap.get(context);
|
|
if (!data) {
|
|
data = m_executionContextDataMap.set(context, adoptPtr(new AsyncCallStackTracker::ExecutionContextData(this, context)))
|
|
.storedValue->value.get();
|
|
}
|
|
return data;
|
|
}
|
|
|
|
void AsyncCallStackTracker::clear()
|
|
{
|
|
m_currentAsyncCallChain.clear();
|
|
m_nestedAsyncCallCount = 0;
|
|
m_executionContextDataMap.clear();
|
|
}
|
|
|
|
} // namespace blink
|