[JSC] Shrink UnlinkedFunctionExecutable
[WebKit-https.git] / Source / JavaScriptCore / inspector / ScriptDebugServer.cpp
index 2c783af..e0a065c 100644 (file)
 #include "config.h"
 #include "ScriptDebugServer.h"
 
-#if ENABLE(INSPECTOR)
-
 #include "DebuggerCallFrame.h"
 #include "DebuggerScope.h"
+#include "Exception.h"
+#include "JSCInlines.h"
 #include "JSJavaScriptCallFrame.h"
-#include "JSLock.h"
 #include "JavaScriptCallFrame.h"
-#include "LegacyProfiler.h"
-#include "ScriptValue.h"
 #include "SourceProvider.h"
 #include <wtf/NeverDestroyed.h>
-#include <wtf/TemporaryChange.h>
-#include <wtf/text/WTFString.h>
+#include <wtf/SetForScope.h>
 
 using namespace JSC;
 
 namespace Inspector {
 
-ScriptDebugServer::ScriptDebugServer(bool isInWorkerThread)
-    : Debugger(isInWorkerThread)
-    , m_doneProcessingDebuggerEvents(true)
-    , m_callingListeners(false)
+ScriptDebugServer::ScriptDebugServer(VM& vm)
+    : Debugger(vm)
 {
 }
 
@@ -60,62 +54,67 @@ ScriptDebugServer::~ScriptDebugServer()
 {
 }
 
-JSC::BreakpointID ScriptDebugServer::setBreakpoint(JSC::SourceID sourceID, const ScriptBreakpoint& scriptBreakpoint, unsigned* actualLineNumber, unsigned* actualColumnNumber)
+void ScriptDebugServer::setBreakpointActions(BreakpointID id, const ScriptBreakpoint& scriptBreakpoint)
 {
-    if (!sourceID)
-        return JSC::noBreakpointID;
-
-    JSC::Breakpoint breakpoint(sourceID, scriptBreakpoint.lineNumber, scriptBreakpoint.columnNumber, scriptBreakpoint.condition, scriptBreakpoint.autoContinue);
-    JSC::BreakpointID id = Debugger::setBreakpoint(breakpoint, *actualLineNumber, *actualColumnNumber);
-    if (id != JSC::noBreakpointID && !scriptBreakpoint.actions.isEmpty()) {
-#ifndef NDEBUG
-        BreakpointIDToActionsMap::iterator it = m_breakpointIDToActions.find(id);
-        ASSERT(it == m_breakpointIDToActions.end());
-#endif
-        const BreakpointActions& actions = scriptBreakpoint.actions;
-        m_breakpointIDToActions.set(id, actions);
-    }
-    return id;
+    ASSERT(id != noBreakpointID);
+    ASSERT(!m_breakpointIDToActions.contains(id));
+
+    m_breakpointIDToActions.set(id, scriptBreakpoint.actions);
+}
+
+void ScriptDebugServer::removeBreakpointActions(BreakpointID id)
+{
+    ASSERT(id != noBreakpointID);
+
+    m_breakpointIDToActions.remove(id);
 }
 
-void ScriptDebugServer::removeBreakpoint(JSC::BreakpointID id)
+const BreakpointActions& ScriptDebugServer::getActionsForBreakpoint(BreakpointID id)
 {
-    ASSERT(id != JSC::noBreakpointID);
-    BreakpointIDToActionsMap::iterator it = m_breakpointIDToActions.find(id);
-    if (it != m_breakpointIDToActions.end())
-        m_breakpointIDToActions.remove(it);
+    ASSERT(id != noBreakpointID);
+
+    auto entry = m_breakpointIDToActions.find(id);
+    if (entry != m_breakpointIDToActions.end())
+        return entry->value;
+
+    static NeverDestroyed<BreakpointActions> emptyActionVector = BreakpointActions();
+    return emptyActionVector;
+}
 
-    Debugger::removeBreakpoint(id);
+void ScriptDebugServer::clearBreakpointActions()
+{
+    m_breakpointIDToActions.clear();
 }
 
 bool ScriptDebugServer::evaluateBreakpointAction(const ScriptBreakpointAction& breakpointAction)
 {
-    DebuggerCallFrame* debuggerCallFrame = currentDebuggerCallFrame();
+    DebuggerCallFrame& debuggerCallFrame = currentDebuggerCallFrame();
 
     switch (breakpointAction.type) {
     case ScriptBreakpointActionTypeLog: {
-        dispatchBreakpointActionLog(debuggerCallFrame->exec(), breakpointAction.data);
+        dispatchBreakpointActionLog(debuggerCallFrame.globalExec(), breakpointAction.data);
         break;
     }
     case ScriptBreakpointActionTypeEvaluate: {
-        JSValue exception;
-        debuggerCallFrame->evaluate(breakpointAction.data, exception);
+        NakedPtr<Exception> exception;
+        JSObject* scopeExtensionObject = nullptr;
+        debuggerCallFrame.evaluateWithScopeExtension(breakpointAction.data, scopeExtensionObject, exception);
         if (exception)
-            reportException(debuggerCallFrame->exec(), exception);
+            reportException(debuggerCallFrame.globalExec(), exception);
         break;
     }
     case ScriptBreakpointActionTypeSound:
-        dispatchBreakpointActionSound(debuggerCallFrame->exec(), breakpointAction.identifier);
+        dispatchBreakpointActionSound(debuggerCallFrame.globalExec(), breakpointAction.identifier);
         break;
     case ScriptBreakpointActionTypeProbe: {
-        JSValue exception;
-        JSValue result = debuggerCallFrame->evaluate(breakpointAction.data, exception);
+        NakedPtr<Exception> exception;
+        JSObject* scopeExtensionObject = nullptr;
+        JSValue result = debuggerCallFrame.evaluateWithScopeExtension(breakpointAction.data, scopeExtensionObject, exception);
+        JSC::ExecState* exec = debuggerCallFrame.globalExec();
         if (exception)
-            reportException(debuggerCallFrame->exec(), exception);
-        
-        JSC::ExecState* state = debuggerCallFrame->scope()->globalObject()->globalExec();
-        Deprecated::ScriptValue wrappedResult = Deprecated::ScriptValue(state->vm(), exception ? exception : result);
-        dispatchBreakpointActionProbe(state, breakpointAction, wrappedResult);
+            reportException(exec, exception);
+
+        dispatchBreakpointActionProbe(exec, breakpointAction, exception ? exception->value() : result);
         break;
     }
     default:
@@ -125,21 +124,14 @@ bool ScriptDebugServer::evaluateBreakpointAction(const ScriptBreakpointAction& b
     return true;
 }
 
-void ScriptDebugServer::clearBreakpoints()
-{
-    Debugger::clearBreakpoints();
-    m_breakpointIDToActions.clear();
-}
-
 void ScriptDebugServer::dispatchDidPause(ScriptDebugListener* listener)
 {
     ASSERT(isPaused());
-    DebuggerCallFrame* debuggerCallFrame = currentDebuggerCallFrame();
-    JSGlobalObject* globalObject = debuggerCallFrame->scope()->globalObject();
-    JSC::ExecState* state = globalObject->globalExec();
-    RefPtr<JavaScriptCallFrame> javaScriptCallFrame = JavaScriptCallFrame::create(debuggerCallFrame);
-    JSValue jsCallFrame = toJS(state, globalObject, javaScriptCallFrame.get());
-    listener->didPause(state, Deprecated::ScriptValue(state->vm(), jsCallFrame), Deprecated::ScriptValue());
+    DebuggerCallFrame& debuggerCallFrame = currentDebuggerCallFrame();
+    JSGlobalObject* globalObject = debuggerCallFrame.scope()->globalObject();
+    JSC::ExecState& state = *globalObject->globalExec();
+    JSValue jsCallFrame = toJS(&state, globalObject, JavaScriptCallFrame::create(debuggerCallFrame).ptr());
+    listener->didPause(state, jsCallFrame, exceptionOrCaughtValue(&state));
 }
 
 void ScriptDebugServer::dispatchBreakpointActionLog(ExecState* exec, const String& message)
@@ -147,16 +139,13 @@ void ScriptDebugServer::dispatchBreakpointActionLog(ExecState* exec, const Strin
     if (m_callingListeners)
         return;
 
-    ListenerSet& listeners = getListeners();
-    if (listeners.isEmpty())
+    if (m_listeners.isEmpty())
         return;
 
-    TemporaryChange<bool> change(m_callingListeners, true);
+    SetForScope<bool> change(m_callingListeners, true);
 
-    Vector<ScriptDebugListener*> listenersCopy;
-    copyToVector(listeners, listenersCopy);
-    for (auto* listener : listenersCopy)
-        listener->breakpointActionLog(exec, message);
+    for (auto* listener : copyToVector(m_listeners))
+        listener->breakpointActionLog(*exec, message);
 }
 
 void ScriptDebugServer::dispatchBreakpointActionSound(ExecState*, int breakpointActionIdentifier)
@@ -164,33 +153,29 @@ void ScriptDebugServer::dispatchBreakpointActionSound(ExecState*, int breakpoint
     if (m_callingListeners)
         return;
 
-    ListenerSet& listeners = getListeners();
-    if (listeners.isEmpty())
+    if (m_listeners.isEmpty())
         return;
 
-    TemporaryChange<bool> change(m_callingListeners, true);
+    SetForScope<bool> change(m_callingListeners, true);
 
-    Vector<ScriptDebugListener*> listenersCopy;
-    copyToVector(listeners, listenersCopy);
-    for (auto* listener : listenersCopy)
+    for (auto* listener : copyToVector(m_listeners))
         listener->breakpointActionSound(breakpointActionIdentifier);
 }
 
-void ScriptDebugServer::dispatchBreakpointActionProbe(ExecState* exec, const ScriptBreakpointAction& action, const Deprecated::ScriptValue& sample)
+void ScriptDebugServer::dispatchBreakpointActionProbe(ExecState* exec, const ScriptBreakpointAction& action, JSC::JSValue sampleValue)
 {
     if (m_callingListeners)
         return;
 
-    ListenerSet& listeners = getListeners();
-    if (listeners.isEmpty())
+    if (m_listeners.isEmpty())
         return;
 
-    TemporaryChange<bool> change(m_callingListeners, true);
+    SetForScope<bool> change(m_callingListeners, true);
+
+    unsigned sampleId = m_nextProbeSampleId++;
 
-    Vector<ScriptDebugListener*> listenersCopy;
-    copyToVector(listeners, listenersCopy);
-    for (auto* listener : listenersCopy)
-        listener->breakpointActionProbe(exec, action, m_hitCount, sample);
+    for (auto* listener : copyToVector(m_listeners))
+        listener->breakpointActionProbe(*exec, action, m_currentProbeBatchId, sampleId, sampleValue);
 }
 
 void ScriptDebugServer::dispatchDidContinue(ScriptDebugListener* listener)
@@ -202,12 +187,16 @@ void ScriptDebugServer::dispatchDidParseSource(const ListenerSet& listeners, Sou
 {
     JSC::SourceID sourceID = sourceProvider->asID();
 
+    // FIXME: <https://webkit.org/b/162773> Web Inspector: Simplify ScriptDebugListener::Script to use SourceProvider
     ScriptDebugListener::Script script;
+    script.sourceProvider = sourceProvider;
     script.url = sourceProvider->url();
-    script.source = sourceProvider->source();
+    script.source = sourceProvider->source().toString();
     script.startLine = sourceProvider->startPosition().m_line.zeroBasedInt();
     script.startColumn = sourceProvider->startPosition().m_column.zeroBasedInt();
     script.isContentScript = isContentScript;
+    script.sourceURL = sourceProvider->sourceURLDirective();
+    script.sourceMappingURL = sourceProvider->sourceMappingURLDirective();
 
     int sourceLength = script.source.length();
     int lineCount = 1;
@@ -225,22 +214,18 @@ void ScriptDebugServer::dispatchDidParseSource(const ListenerSet& listeners, Sou
     else
         script.endColumn = sourceLength - lastLineStart;
 
-    Vector<ScriptDebugListener*> copy;
-    copyToVector(listeners, copy);
-    for (size_t i = 0; i < copy.size(); ++i)
-        copy[i]->didParseSource(sourceID, script);
+    for (auto* listener : copyToVector(listeners))
+        listener->didParseSource(sourceID, script);
 }
 
 void ScriptDebugServer::dispatchFailedToParseSource(const ListenerSet& listeners, SourceProvider* sourceProvider, int errorLine, const String& errorMessage)
 {
     String url = sourceProvider->url();
-    const String& data = sourceProvider->source();
+    String data = sourceProvider->source().toString();
     int firstLine = sourceProvider->startPosition().m_line.oneBasedInt();
 
-    Vector<ScriptDebugListener*> copy;
-    copyToVector(listeners, copy);
-    for (size_t i = 0; i < copy.size(); ++i)
-        copy[i]->failedToParseSource(url, data, firstLine, errorLine, errorMessage);
+    for (auto* listener : copyToVector(listeners))
+        listener->failedToParseSource(url, data, firstLine, errorLine, errorMessage);
 }
 
 void ScriptDebugServer::sourceParsed(ExecState* exec, SourceProvider* sourceProvider, int errorLine, const String& errorMessage)
@@ -248,17 +233,16 @@ void ScriptDebugServer::sourceParsed(ExecState* exec, SourceProvider* sourceProv
     if (m_callingListeners)
         return;
 
-    ListenerSet& listeners = getListeners();
-    if (listeners.isEmpty())
+    if (m_listeners.isEmpty())
         return;
 
-    TemporaryChange<bool> change(m_callingListeners, true);
+    SetForScope<bool> change(m_callingListeners, true);
 
     bool isError = errorLine != -1;
     if (isError)
-        dispatchFailedToParseSource(listeners, sourceProvider, errorLine, errorMessage);
+        dispatchFailedToParseSource(m_listeners, sourceProvider, errorLine, errorMessage);
     else
-        dispatchDidParseSource(listeners, sourceProvider, isContentScript(exec));
+        dispatchDidParseSource(m_listeners, sourceProvider, isContentScript(exec));
 }
 
 void ScriptDebugServer::dispatchFunctionToListeners(JavaScriptExecutionCallback callback)
@@ -266,19 +250,18 @@ void ScriptDebugServer::dispatchFunctionToListeners(JavaScriptExecutionCallback
     if (m_callingListeners)
         return;
 
-    TemporaryChange<bool> change(m_callingListeners, true);
+    if (m_listeners.isEmpty())
+        return;
+
+    SetForScope<bool> change(m_callingListeners, true);
 
-    ListenerSet& listeners = getListeners();
-    if (!listeners.isEmpty())
-        dispatchFunctionToListeners(listeners, callback);
+    dispatchFunctionToListeners(m_listeners, callback);
 }
 
 void ScriptDebugServer::dispatchFunctionToListeners(const ListenerSet& listeners, JavaScriptExecutionCallback callback)
 {
-    Vector<ScriptDebugListener*> copy;
-    copyToVector(listeners, copy);
-    for (size_t i = 0; i < copy.size(); ++i)
-        (this->*callback)(copy[i]);
+    for (auto* listener : copyToVector(listeners))
+        (this->*callback)(listener);
 }
 
 void ScriptDebugServer::notifyDoneProcessingDebuggerEvents()
@@ -286,49 +269,76 @@ void ScriptDebugServer::notifyDoneProcessingDebuggerEvents()
     m_doneProcessingDebuggerEvents = true;
 }
 
-void ScriptDebugServer::handleBreakpointHit(const JSC::Breakpoint& breakpoint)
+void ScriptDebugServer::handleBreakpointHit(JSC::JSGlobalObject* globalObject, const JSC::Breakpoint& breakpoint)
 {
-    m_hitCount++;
-    BreakpointIDToActionsMap::iterator it = m_breakpointIDToActions.find(breakpoint.id);
-    if (it != m_breakpointIDToActions.end()) {
-        BreakpointActions& actions = it->value;
+    ASSERT(isAttached(globalObject));
+
+    m_currentProbeBatchId++;
+
+    auto entry = m_breakpointIDToActions.find(breakpoint.id);
+    if (entry != m_breakpointIDToActions.end()) {
+        BreakpointActions actions = entry->value;
         for (size_t i = 0; i < actions.size(); ++i) {
             if (!evaluateBreakpointAction(actions[i]))
                 return;
+            if (!isAttached(globalObject))
+                return;
         }
     }
 }
 
-void ScriptDebugServer::handleExceptionInBreakpointCondition(JSC::ExecState* exec, JSC::JSValue exception) const
+void ScriptDebugServer::handleExceptionInBreakpointCondition(JSC::ExecState* exec, JSC::Exception* exception) const
 {
     reportException(exec, exception);
 }
 
-void ScriptDebugServer::handlePause(Debugger::ReasonForPause, JSGlobalObject* vmEntryGlobalObject)
+void ScriptDebugServer::handlePause(JSGlobalObject* vmEntryGlobalObject, Debugger::ReasonForPause)
 {
     dispatchFunctionToListeners(&ScriptDebugServer::dispatchDidPause);
-    LegacyProfiler::profiler()->didPause(currentDebuggerCallFrame());
     didPause(vmEntryGlobalObject);
 
     m_doneProcessingDebuggerEvents = false;
     runEventLoopWhilePaused();
 
     didContinue(vmEntryGlobalObject);
-    LegacyProfiler::profiler()->didContinue(currentDebuggerCallFrame());
     dispatchFunctionToListeners(&ScriptDebugServer::dispatchDidContinue);
 }
 
-const BreakpointActions& ScriptDebugServer::getActionsForBreakpoint(JSC::BreakpointID breakpointID)
+void ScriptDebugServer::addListener(ScriptDebugListener* listener)
 {
-    ASSERT(breakpointID != JSC::noBreakpointID);
+    ASSERT(listener);
 
-    if (m_breakpointIDToActions.contains(breakpointID))
-        return m_breakpointIDToActions.find(breakpointID)->value;
-    
-    static NeverDestroyed<BreakpointActions> emptyActionVector = BreakpointActions();
-    return emptyActionVector;
+    bool wasEmpty = m_listeners.isEmpty();
+    m_listeners.add(listener);
+
+    // First listener. Attach the debugger.
+    if (wasEmpty)
+        attachDebugger();
 }
 
-} // namespace Inspector
+void ScriptDebugServer::removeListener(ScriptDebugListener* listener, bool isBeingDestroyed)
+{
+    ASSERT(listener);
 
-#endif // ENABLE(INSPECTOR)
+    m_listeners.remove(listener);
+
+    // Last listener. Detach the debugger.
+    if (m_listeners.isEmpty())
+        detachDebugger(isBeingDestroyed);
+}
+
+JSC::JSValue ScriptDebugServer::exceptionOrCaughtValue(JSC::ExecState* state)
+{
+    if (reasonForPause() == PausedForException)
+        return currentException();
+
+    for (RefPtr<DebuggerCallFrame> frame = &currentDebuggerCallFrame(); frame; frame = frame->callerFrame()) {
+        DebuggerScope& scope = *frame->scope();
+        if (scope.isCatchScope())
+            return scope.caughtValue(state);
+    }
+
+    return { };
+}
+
+} // namespace Inspector