Home | History | Annotate | Download | only in inspector
      1 // Copyright 2016 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/inspector/v8-debugger.h"
      6 
      7 #include "src/inspector/debugger-script.h"
      8 #include "src/inspector/protocol/Protocol.h"
      9 #include "src/inspector/script-breakpoint.h"
     10 #include "src/inspector/string-util.h"
     11 #include "src/inspector/v8-debugger-agent-impl.h"
     12 #include "src/inspector/v8-inspector-impl.h"
     13 #include "src/inspector/v8-internal-value-type.h"
     14 #include "src/inspector/v8-stack-trace-impl.h"
     15 #include "src/inspector/v8-value-copier.h"
     16 
     17 #include "include/v8-util.h"
     18 
     19 namespace v8_inspector {
     20 
     21 namespace {
     22 static const char v8AsyncTaskEventEnqueue[] = "enqueue";
     23 static const char v8AsyncTaskEventEnqueueRecurring[] = "enqueueRecurring";
     24 static const char v8AsyncTaskEventWillHandle[] = "willHandle";
     25 static const char v8AsyncTaskEventDidHandle[] = "didHandle";
     26 static const char v8AsyncTaskEventCancel[] = "cancel";
     27 
     28 inline v8::Local<v8::Boolean> v8Boolean(bool value, v8::Isolate* isolate) {
     29   return value ? v8::True(isolate) : v8::False(isolate);
     30 }
     31 
     32 }  // namespace
     33 
     34 static bool inLiveEditScope = false;
     35 
     36 v8::MaybeLocal<v8::Value> V8Debugger::callDebuggerMethod(
     37     const char* functionName, int argc, v8::Local<v8::Value> argv[]) {
     38   v8::MicrotasksScope microtasks(m_isolate,
     39                                  v8::MicrotasksScope::kDoNotRunMicrotasks);
     40   DCHECK(m_isolate->InContext());
     41   v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
     42   v8::Local<v8::Object> debuggerScript = m_debuggerScript.Get(m_isolate);
     43   v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
     44       debuggerScript
     45           ->Get(context, toV8StringInternalized(m_isolate, functionName))
     46           .ToLocalChecked());
     47   return function->Call(context, debuggerScript, argc, argv);
     48 }
     49 
     50 V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector)
     51     : m_isolate(isolate),
     52       m_inspector(inspector),
     53       m_lastContextId(0),
     54       m_enableCount(0),
     55       m_breakpointsActivated(true),
     56       m_runningNestedMessageLoop(false),
     57       m_ignoreScriptParsedEventsCounter(0),
     58       m_maxAsyncCallStackDepth(0),
     59       m_pauseOnExceptionsState(v8::DebugInterface::NoBreakOnException) {}
     60 
     61 V8Debugger::~V8Debugger() {}
     62 
     63 void V8Debugger::enable() {
     64   if (m_enableCount++) return;
     65   DCHECK(!enabled());
     66   v8::HandleScope scope(m_isolate);
     67   v8::DebugInterface::SetDebugEventListener(m_isolate,
     68                                             &V8Debugger::v8DebugEventCallback,
     69                                             v8::External::New(m_isolate, this));
     70   m_debuggerContext.Reset(m_isolate,
     71                           v8::DebugInterface::GetDebugContext(m_isolate));
     72   v8::DebugInterface::ChangeBreakOnException(
     73       m_isolate, v8::DebugInterface::NoBreakOnException);
     74   m_pauseOnExceptionsState = v8::DebugInterface::NoBreakOnException;
     75   compileDebuggerScript();
     76 }
     77 
     78 void V8Debugger::disable() {
     79   if (--m_enableCount) return;
     80   DCHECK(enabled());
     81   clearBreakpoints();
     82   m_debuggerScript.Reset();
     83   m_debuggerContext.Reset();
     84   allAsyncTasksCanceled();
     85   v8::DebugInterface::SetDebugEventListener(m_isolate, nullptr);
     86 }
     87 
     88 bool V8Debugger::enabled() const { return !m_debuggerScript.IsEmpty(); }
     89 
     90 // static
     91 int V8Debugger::contextId(v8::Local<v8::Context> context) {
     92   v8::Local<v8::Value> data =
     93       context->GetEmbedderData(static_cast<int>(v8::Context::kDebugIdIndex));
     94   if (data.IsEmpty() || !data->IsString()) return 0;
     95   String16 dataString = toProtocolString(data.As<v8::String>());
     96   if (dataString.isEmpty()) return 0;
     97   size_t commaPos = dataString.find(",");
     98   if (commaPos == String16::kNotFound) return 0;
     99   size_t commaPos2 = dataString.find(",", commaPos + 1);
    100   if (commaPos2 == String16::kNotFound) return 0;
    101   return dataString.substring(commaPos + 1, commaPos2 - commaPos - 1)
    102       .toInteger();
    103 }
    104 
    105 // static
    106 int V8Debugger::getGroupId(v8::Local<v8::Context> context) {
    107   v8::Local<v8::Value> data =
    108       context->GetEmbedderData(static_cast<int>(v8::Context::kDebugIdIndex));
    109   if (data.IsEmpty() || !data->IsString()) return 0;
    110   String16 dataString = toProtocolString(data.As<v8::String>());
    111   if (dataString.isEmpty()) return 0;
    112   size_t commaPos = dataString.find(",");
    113   if (commaPos == String16::kNotFound) return 0;
    114   return dataString.substring(0, commaPos).toInteger();
    115 }
    116 
    117 void V8Debugger::getCompiledScripts(
    118     int contextGroupId,
    119     std::vector<std::unique_ptr<V8DebuggerScript>>& result) {
    120   v8::HandleScope scope(m_isolate);
    121   v8::PersistentValueVector<v8::DebugInterface::Script> scripts(m_isolate);
    122   v8::DebugInterface::GetLoadedScripts(m_isolate, scripts);
    123   String16 contextPrefix = String16::fromInteger(contextGroupId) + ",";
    124   for (size_t i = 0; i < scripts.Size(); ++i) {
    125     v8::Local<v8::DebugInterface::Script> script = scripts.Get(i);
    126     if (!script->WasCompiled()) continue;
    127     v8::ScriptOriginOptions origin = script->OriginOptions();
    128     if (origin.IsEmbedderDebugScript()) continue;
    129     v8::Local<v8::String> v8ContextData;
    130     if (!script->ContextData().ToLocal(&v8ContextData)) continue;
    131     String16 contextData = toProtocolString(v8ContextData);
    132     if (contextData.find(contextPrefix) != 0) continue;
    133     result.push_back(
    134         wrapUnique(new V8DebuggerScript(m_isolate, script, false)));
    135   }
    136 }
    137 
    138 String16 V8Debugger::setBreakpoint(const String16& sourceID,
    139                                    const ScriptBreakpoint& scriptBreakpoint,
    140                                    int* actualLineNumber,
    141                                    int* actualColumnNumber) {
    142   v8::HandleScope scope(m_isolate);
    143   v8::Local<v8::Context> context = debuggerContext();
    144   v8::Context::Scope contextScope(context);
    145 
    146   v8::Local<v8::Object> info = v8::Object::New(m_isolate);
    147   bool success = false;
    148   success = info->Set(context, toV8StringInternalized(m_isolate, "sourceID"),
    149                       toV8String(m_isolate, sourceID))
    150                 .FromMaybe(false);
    151   DCHECK(success);
    152   success = info->Set(context, toV8StringInternalized(m_isolate, "lineNumber"),
    153                       v8::Integer::New(m_isolate, scriptBreakpoint.lineNumber))
    154                 .FromMaybe(false);
    155   DCHECK(success);
    156   success =
    157       info->Set(context, toV8StringInternalized(m_isolate, "columnNumber"),
    158                 v8::Integer::New(m_isolate, scriptBreakpoint.columnNumber))
    159           .FromMaybe(false);
    160   DCHECK(success);
    161   success = info->Set(context, toV8StringInternalized(m_isolate, "condition"),
    162                       toV8String(m_isolate, scriptBreakpoint.condition))
    163                 .FromMaybe(false);
    164   DCHECK(success);
    165 
    166   v8::Local<v8::Function> setBreakpointFunction = v8::Local<v8::Function>::Cast(
    167       m_debuggerScript.Get(m_isolate)
    168           ->Get(context, toV8StringInternalized(m_isolate, "setBreakpoint"))
    169           .ToLocalChecked());
    170   v8::Local<v8::Value> breakpointId =
    171       v8::DebugInterface::Call(debuggerContext(), setBreakpointFunction, info)
    172           .ToLocalChecked();
    173   if (!breakpointId->IsString()) return "";
    174   *actualLineNumber =
    175       info->Get(context, toV8StringInternalized(m_isolate, "lineNumber"))
    176           .ToLocalChecked()
    177           ->Int32Value(context)
    178           .FromJust();
    179   *actualColumnNumber =
    180       info->Get(context, toV8StringInternalized(m_isolate, "columnNumber"))
    181           .ToLocalChecked()
    182           ->Int32Value(context)
    183           .FromJust();
    184   return toProtocolString(breakpointId.As<v8::String>());
    185 }
    186 
    187 void V8Debugger::removeBreakpoint(const String16& breakpointId) {
    188   v8::HandleScope scope(m_isolate);
    189   v8::Local<v8::Context> context = debuggerContext();
    190   v8::Context::Scope contextScope(context);
    191 
    192   v8::Local<v8::Object> info = v8::Object::New(m_isolate);
    193   bool success = false;
    194   success =
    195       info->Set(context, toV8StringInternalized(m_isolate, "breakpointId"),
    196                 toV8String(m_isolate, breakpointId))
    197           .FromMaybe(false);
    198   DCHECK(success);
    199 
    200   v8::Local<v8::Function> removeBreakpointFunction =
    201       v8::Local<v8::Function>::Cast(
    202           m_debuggerScript.Get(m_isolate)
    203               ->Get(context,
    204                     toV8StringInternalized(m_isolate, "removeBreakpoint"))
    205               .ToLocalChecked());
    206   v8::DebugInterface::Call(debuggerContext(), removeBreakpointFunction, info)
    207       .ToLocalChecked();
    208 }
    209 
    210 void V8Debugger::clearBreakpoints() {
    211   v8::HandleScope scope(m_isolate);
    212   v8::Local<v8::Context> context = debuggerContext();
    213   v8::Context::Scope contextScope(context);
    214 
    215   v8::Local<v8::Function> clearBreakpoints = v8::Local<v8::Function>::Cast(
    216       m_debuggerScript.Get(m_isolate)
    217           ->Get(context, toV8StringInternalized(m_isolate, "clearBreakpoints"))
    218           .ToLocalChecked());
    219   v8::DebugInterface::Call(debuggerContext(), clearBreakpoints)
    220       .ToLocalChecked();
    221 }
    222 
    223 void V8Debugger::setBreakpointsActivated(bool activated) {
    224   if (!enabled()) {
    225     UNREACHABLE();
    226     return;
    227   }
    228   v8::HandleScope scope(m_isolate);
    229   v8::Local<v8::Context> context = debuggerContext();
    230   v8::Context::Scope contextScope(context);
    231 
    232   v8::Local<v8::Object> info = v8::Object::New(m_isolate);
    233   bool success = false;
    234   success = info->Set(context, toV8StringInternalized(m_isolate, "enabled"),
    235                       v8::Boolean::New(m_isolate, activated))
    236                 .FromMaybe(false);
    237   DCHECK(success);
    238   v8::Local<v8::Function> setBreakpointsActivated =
    239       v8::Local<v8::Function>::Cast(
    240           m_debuggerScript.Get(m_isolate)
    241               ->Get(context, toV8StringInternalized(m_isolate,
    242                                                     "setBreakpointsActivated"))
    243               .ToLocalChecked());
    244   v8::DebugInterface::Call(debuggerContext(), setBreakpointsActivated, info)
    245       .ToLocalChecked();
    246 
    247   m_breakpointsActivated = activated;
    248 }
    249 
    250 v8::DebugInterface::ExceptionBreakState
    251 V8Debugger::getPauseOnExceptionsState() {
    252   DCHECK(enabled());
    253   return m_pauseOnExceptionsState;
    254 }
    255 
    256 void V8Debugger::setPauseOnExceptionsState(
    257     v8::DebugInterface::ExceptionBreakState pauseOnExceptionsState) {
    258   DCHECK(enabled());
    259   if (m_pauseOnExceptionsState == pauseOnExceptionsState) return;
    260   v8::DebugInterface::ChangeBreakOnException(m_isolate, pauseOnExceptionsState);
    261   m_pauseOnExceptionsState = pauseOnExceptionsState;
    262 }
    263 
    264 void V8Debugger::setPauseOnNextStatement(bool pause) {
    265   if (m_runningNestedMessageLoop) return;
    266   if (pause)
    267     v8::DebugInterface::DebugBreak(m_isolate);
    268   else
    269     v8::DebugInterface::CancelDebugBreak(m_isolate);
    270 }
    271 
    272 bool V8Debugger::canBreakProgram() {
    273   if (!m_breakpointsActivated) return false;
    274   return m_isolate->InContext();
    275 }
    276 
    277 void V8Debugger::breakProgram() {
    278   if (isPaused()) {
    279     DCHECK(!m_runningNestedMessageLoop);
    280     v8::Local<v8::Value> exception;
    281     v8::Local<v8::Array> hitBreakpoints;
    282     handleProgramBreak(m_pausedContext, m_executionState, exception,
    283                        hitBreakpoints);
    284     return;
    285   }
    286 
    287   if (!canBreakProgram()) return;
    288 
    289   v8::HandleScope scope(m_isolate);
    290   v8::Local<v8::Function> breakFunction;
    291   if (!v8::Function::New(m_isolate->GetCurrentContext(),
    292                          &V8Debugger::breakProgramCallback,
    293                          v8::External::New(m_isolate, this), 0,
    294                          v8::ConstructorBehavior::kThrow)
    295            .ToLocal(&breakFunction))
    296     return;
    297   v8::DebugInterface::Call(debuggerContext(), breakFunction).ToLocalChecked();
    298 }
    299 
    300 void V8Debugger::continueProgram() {
    301   if (isPaused()) m_inspector->client()->quitMessageLoopOnPause();
    302   m_pausedContext.Clear();
    303   m_executionState.Clear();
    304 }
    305 
    306 void V8Debugger::stepIntoStatement() {
    307   DCHECK(isPaused());
    308   DCHECK(!m_executionState.IsEmpty());
    309   v8::DebugInterface::PrepareStep(m_isolate, v8::DebugInterface::StepIn);
    310   continueProgram();
    311 }
    312 
    313 void V8Debugger::stepOverStatement() {
    314   DCHECK(isPaused());
    315   DCHECK(!m_executionState.IsEmpty());
    316   v8::DebugInterface::PrepareStep(m_isolate, v8::DebugInterface::StepNext);
    317   continueProgram();
    318 }
    319 
    320 void V8Debugger::stepOutOfFunction() {
    321   DCHECK(isPaused());
    322   DCHECK(!m_executionState.IsEmpty());
    323   v8::DebugInterface::PrepareStep(m_isolate, v8::DebugInterface::StepOut);
    324   continueProgram();
    325 }
    326 
    327 void V8Debugger::clearStepping() {
    328   DCHECK(enabled());
    329   v8::DebugInterface::ClearStepping(m_isolate);
    330 }
    331 
    332 Response V8Debugger::setScriptSource(
    333     const String16& sourceID, v8::Local<v8::String> newSource, bool dryRun,
    334     Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails,
    335     JavaScriptCallFrames* newCallFrames, Maybe<bool>* stackChanged,
    336     bool* compileError) {
    337   class EnableLiveEditScope {
    338    public:
    339     explicit EnableLiveEditScope(v8::Isolate* isolate) : m_isolate(isolate) {
    340       v8::DebugInterface::SetLiveEditEnabled(m_isolate, true);
    341       inLiveEditScope = true;
    342     }
    343     ~EnableLiveEditScope() {
    344       v8::DebugInterface::SetLiveEditEnabled(m_isolate, false);
    345       inLiveEditScope = false;
    346     }
    347 
    348    private:
    349     v8::Isolate* m_isolate;
    350   };
    351 
    352   *compileError = false;
    353   DCHECK(enabled());
    354   v8::HandleScope scope(m_isolate);
    355 
    356   std::unique_ptr<v8::Context::Scope> contextScope;
    357   if (!isPaused())
    358     contextScope = wrapUnique(new v8::Context::Scope(debuggerContext()));
    359 
    360   v8::Local<v8::Value> argv[] = {toV8String(m_isolate, sourceID), newSource,
    361                                  v8Boolean(dryRun, m_isolate)};
    362 
    363   v8::Local<v8::Value> v8result;
    364   {
    365     EnableLiveEditScope enableLiveEditScope(m_isolate);
    366     v8::TryCatch tryCatch(m_isolate);
    367     tryCatch.SetVerbose(false);
    368     v8::MaybeLocal<v8::Value> maybeResult =
    369         callDebuggerMethod("liveEditScriptSource", 3, argv);
    370     if (tryCatch.HasCaught()) {
    371       v8::Local<v8::Message> message = tryCatch.Message();
    372       if (!message.IsEmpty())
    373         return Response::Error(toProtocolStringWithTypeCheck(message->Get()));
    374       else
    375         return Response::InternalError();
    376     }
    377     v8result = maybeResult.ToLocalChecked();
    378   }
    379   DCHECK(!v8result.IsEmpty());
    380   v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
    381   v8::Local<v8::Object> resultTuple =
    382       v8result->ToObject(context).ToLocalChecked();
    383   int code = static_cast<int>(resultTuple->Get(context, 0)
    384                                   .ToLocalChecked()
    385                                   ->ToInteger(context)
    386                                   .ToLocalChecked()
    387                                   ->Value());
    388   switch (code) {
    389     case 0: {
    390       *stackChanged = resultTuple->Get(context, 1)
    391                           .ToLocalChecked()
    392                           ->BooleanValue(context)
    393                           .FromJust();
    394       // Call stack may have changed after if the edited function was on the
    395       // stack.
    396       if (!dryRun && isPaused()) {
    397         JavaScriptCallFrames frames = currentCallFrames();
    398         newCallFrames->swap(frames);
    399       }
    400       return Response::OK();
    401     }
    402     // Compile error.
    403     case 1: {
    404       *exceptionDetails =
    405           protocol::Runtime::ExceptionDetails::create()
    406               .setExceptionId(m_inspector->nextExceptionId())
    407               .setText(toProtocolStringWithTypeCheck(
    408                   resultTuple->Get(context, 2).ToLocalChecked()))
    409               .setLineNumber(static_cast<int>(resultTuple->Get(context, 3)
    410                                                   .ToLocalChecked()
    411                                                   ->ToInteger(context)
    412                                                   .ToLocalChecked()
    413                                                   ->Value()) -
    414                              1)
    415               .setColumnNumber(static_cast<int>(resultTuple->Get(context, 4)
    416                                                     .ToLocalChecked()
    417                                                     ->ToInteger(context)
    418                                                     .ToLocalChecked()
    419                                                     ->Value()) -
    420                                1)
    421               .build();
    422       *compileError = true;
    423       return Response::OK();
    424     }
    425   }
    426   return Response::InternalError();
    427 }
    428 
    429 JavaScriptCallFrames V8Debugger::currentCallFrames(int limit) {
    430   if (!m_isolate->InContext()) return JavaScriptCallFrames();
    431   v8::Local<v8::Value> currentCallFramesV8;
    432   if (m_executionState.IsEmpty()) {
    433     v8::Local<v8::Function> currentCallFramesFunction =
    434         v8::Local<v8::Function>::Cast(
    435             m_debuggerScript.Get(m_isolate)
    436                 ->Get(debuggerContext(),
    437                       toV8StringInternalized(m_isolate, "currentCallFrames"))
    438                 .ToLocalChecked());
    439     currentCallFramesV8 =
    440         v8::DebugInterface::Call(debuggerContext(), currentCallFramesFunction,
    441                                  v8::Integer::New(m_isolate, limit))
    442             .ToLocalChecked();
    443   } else {
    444     v8::Local<v8::Value> argv[] = {m_executionState,
    445                                    v8::Integer::New(m_isolate, limit)};
    446     currentCallFramesV8 =
    447         callDebuggerMethod("currentCallFrames", arraysize(argv), argv)
    448             .ToLocalChecked();
    449   }
    450   DCHECK(!currentCallFramesV8.IsEmpty());
    451   if (!currentCallFramesV8->IsArray()) return JavaScriptCallFrames();
    452   v8::Local<v8::Array> callFramesArray = currentCallFramesV8.As<v8::Array>();
    453   JavaScriptCallFrames callFrames;
    454   for (uint32_t i = 0; i < callFramesArray->Length(); ++i) {
    455     v8::Local<v8::Value> callFrameValue;
    456     if (!callFramesArray->Get(debuggerContext(), i).ToLocal(&callFrameValue))
    457       return JavaScriptCallFrames();
    458     if (!callFrameValue->IsObject()) return JavaScriptCallFrames();
    459     v8::Local<v8::Object> callFrameObject = callFrameValue.As<v8::Object>();
    460     callFrames.push_back(JavaScriptCallFrame::create(
    461         debuggerContext(), v8::Local<v8::Object>::Cast(callFrameObject)));
    462   }
    463   return callFrames;
    464 }
    465 
    466 static V8Debugger* toV8Debugger(v8::Local<v8::Value> data) {
    467   void* p = v8::Local<v8::External>::Cast(data)->Value();
    468   return static_cast<V8Debugger*>(p);
    469 }
    470 
    471 void V8Debugger::breakProgramCallback(
    472     const v8::FunctionCallbackInfo<v8::Value>& info) {
    473   DCHECK_EQ(info.Length(), 2);
    474   V8Debugger* thisPtr = toV8Debugger(info.Data());
    475   if (!thisPtr->enabled()) return;
    476   v8::Local<v8::Context> pausedContext =
    477       thisPtr->m_isolate->GetCurrentContext();
    478   v8::Local<v8::Value> exception;
    479   v8::Local<v8::Array> hitBreakpoints;
    480   thisPtr->handleProgramBreak(pausedContext,
    481                               v8::Local<v8::Object>::Cast(info[0]), exception,
    482                               hitBreakpoints);
    483 }
    484 
    485 void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext,
    486                                     v8::Local<v8::Object> executionState,
    487                                     v8::Local<v8::Value> exception,
    488                                     v8::Local<v8::Array> hitBreakpointNumbers,
    489                                     bool isPromiseRejection, bool isUncaught) {
    490   // Don't allow nested breaks.
    491   if (m_runningNestedMessageLoop) return;
    492 
    493   V8DebuggerAgentImpl* agent =
    494       m_inspector->enabledDebuggerAgentForGroup(getGroupId(pausedContext));
    495   if (!agent) return;
    496 
    497   std::vector<String16> breakpointIds;
    498   if (!hitBreakpointNumbers.IsEmpty()) {
    499     breakpointIds.reserve(hitBreakpointNumbers->Length());
    500     for (uint32_t i = 0; i < hitBreakpointNumbers->Length(); i++) {
    501       v8::Local<v8::Value> hitBreakpointNumber =
    502           hitBreakpointNumbers->Get(debuggerContext(), i).ToLocalChecked();
    503       DCHECK(hitBreakpointNumber->IsInt32());
    504       breakpointIds.push_back(String16::fromInteger(
    505           hitBreakpointNumber->Int32Value(debuggerContext()).FromJust()));
    506     }
    507   }
    508 
    509   m_pausedContext = pausedContext;
    510   m_executionState = executionState;
    511   V8DebuggerAgentImpl::SkipPauseRequest result = agent->didPause(
    512       pausedContext, exception, breakpointIds, isPromiseRejection, isUncaught);
    513   if (result == V8DebuggerAgentImpl::RequestNoSkip) {
    514     m_runningNestedMessageLoop = true;
    515     int groupId = getGroupId(pausedContext);
    516     DCHECK(groupId);
    517     m_inspector->client()->runMessageLoopOnPause(groupId);
    518     // The agent may have been removed in the nested loop.
    519     agent =
    520         m_inspector->enabledDebuggerAgentForGroup(getGroupId(pausedContext));
    521     if (agent) agent->didContinue();
    522     m_runningNestedMessageLoop = false;
    523   }
    524   m_pausedContext.Clear();
    525   m_executionState.Clear();
    526 
    527   if (result == V8DebuggerAgentImpl::RequestStepFrame) {
    528     v8::DebugInterface::PrepareStep(m_isolate, v8::DebugInterface::StepFrame);
    529   } else if (result == V8DebuggerAgentImpl::RequestStepInto) {
    530     v8::DebugInterface::PrepareStep(m_isolate, v8::DebugInterface::StepIn);
    531   } else if (result == V8DebuggerAgentImpl::RequestStepOut) {
    532     v8::DebugInterface::PrepareStep(m_isolate, v8::DebugInterface::StepOut);
    533   }
    534 }
    535 
    536 void V8Debugger::v8DebugEventCallback(
    537     const v8::DebugInterface::EventDetails& eventDetails) {
    538   V8Debugger* thisPtr = toV8Debugger(eventDetails.GetCallbackData());
    539   thisPtr->handleV8DebugEvent(eventDetails);
    540 }
    541 
    542 v8::Local<v8::Value> V8Debugger::callInternalGetterFunction(
    543     v8::Local<v8::Object> object, const char* functionName) {
    544   v8::MicrotasksScope microtasks(m_isolate,
    545                                  v8::MicrotasksScope::kDoNotRunMicrotasks);
    546   v8::Local<v8::Value> getterValue =
    547       object
    548           ->Get(m_isolate->GetCurrentContext(),
    549                 toV8StringInternalized(m_isolate, functionName))
    550           .ToLocalChecked();
    551   DCHECK(!getterValue.IsEmpty() && getterValue->IsFunction());
    552   return v8::Local<v8::Function>::Cast(getterValue)
    553       ->Call(m_isolate->GetCurrentContext(), object, 0, nullptr)
    554       .ToLocalChecked();
    555 }
    556 
    557 void V8Debugger::handleV8DebugEvent(
    558     const v8::DebugInterface::EventDetails& eventDetails) {
    559   if (!enabled()) return;
    560   v8::DebugEvent event = eventDetails.GetEvent();
    561   if (event != v8::AsyncTaskEvent && event != v8::Break &&
    562       event != v8::Exception && event != v8::AfterCompile &&
    563       event != v8::BeforeCompile && event != v8::CompileError)
    564     return;
    565 
    566   v8::Local<v8::Context> eventContext = eventDetails.GetEventContext();
    567   DCHECK(!eventContext.IsEmpty());
    568 
    569   if (event == v8::AsyncTaskEvent) {
    570     v8::HandleScope scope(m_isolate);
    571     handleV8AsyncTaskEvent(eventContext, eventDetails.GetExecutionState(),
    572                            eventDetails.GetEventData());
    573     return;
    574   }
    575 
    576   V8DebuggerAgentImpl* agent =
    577       m_inspector->enabledDebuggerAgentForGroup(getGroupId(eventContext));
    578   if (agent) {
    579     v8::HandleScope scope(m_isolate);
    580     if (m_ignoreScriptParsedEventsCounter == 0 &&
    581         (event == v8::AfterCompile || event == v8::CompileError)) {
    582       v8::Local<v8::Context> context = debuggerContext();
    583       v8::Context::Scope contextScope(context);
    584       v8::Local<v8::Value> argv[] = {eventDetails.GetEventData()};
    585       v8::Local<v8::Value> value =
    586           callDebuggerMethod("getAfterCompileScript", 1, argv).ToLocalChecked();
    587       if (value->IsNull()) return;
    588       DCHECK(value->IsObject());
    589       v8::Local<v8::Object> scriptObject = v8::Local<v8::Object>::Cast(value);
    590       v8::Local<v8::DebugInterface::Script> script;
    591       if (!v8::DebugInterface::Script::Wrap(m_isolate, scriptObject)
    592                .ToLocal(&script))
    593         return;
    594       agent->didParseSource(
    595           wrapUnique(new V8DebuggerScript(m_isolate, script, inLiveEditScope)),
    596           event == v8::AfterCompile);
    597     } else if (event == v8::Exception) {
    598       v8::Local<v8::Context> context = debuggerContext();
    599       v8::Local<v8::Object> eventData = eventDetails.GetEventData();
    600       v8::Local<v8::Value> exception =
    601           callInternalGetterFunction(eventData, "exception");
    602       v8::Local<v8::Value> promise =
    603           callInternalGetterFunction(eventData, "promise");
    604       bool isPromiseRejection = !promise.IsEmpty() && promise->IsObject();
    605       v8::Local<v8::Value> uncaught =
    606           callInternalGetterFunction(eventData, "uncaught");
    607       bool isUncaught = uncaught->BooleanValue(context).FromJust();
    608       handleProgramBreak(eventContext, eventDetails.GetExecutionState(),
    609                          exception, v8::Local<v8::Array>(), isPromiseRejection,
    610                          isUncaught);
    611     } else if (event == v8::Break) {
    612       v8::Local<v8::Value> argv[] = {eventDetails.GetEventData()};
    613       v8::Local<v8::Value> hitBreakpoints =
    614           callDebuggerMethod("getBreakpointNumbers", 1, argv).ToLocalChecked();
    615       DCHECK(hitBreakpoints->IsArray());
    616       handleProgramBreak(eventContext, eventDetails.GetExecutionState(),
    617                          v8::Local<v8::Value>(),
    618                          hitBreakpoints.As<v8::Array>());
    619     }
    620   }
    621 }
    622 
    623 void V8Debugger::handleV8AsyncTaskEvent(v8::Local<v8::Context> context,
    624                                         v8::Local<v8::Object> executionState,
    625                                         v8::Local<v8::Object> eventData) {
    626   if (!m_maxAsyncCallStackDepth) return;
    627 
    628   String16 type = toProtocolStringWithTypeCheck(
    629       callInternalGetterFunction(eventData, "type"));
    630   String16 name = toProtocolStringWithTypeCheck(
    631       callInternalGetterFunction(eventData, "name"));
    632   int id = static_cast<int>(callInternalGetterFunction(eventData, "id")
    633                                 ->ToInteger(context)
    634                                 .ToLocalChecked()
    635                                 ->Value());
    636   // Async task events from Promises are given misaligned pointers to prevent
    637   // from overlapping with other Blink task identifiers. There is a single
    638   // namespace of such ids, managed by src/js/promise.js.
    639   void* ptr = reinterpret_cast<void*>(id * 2 + 1);
    640   if (type == v8AsyncTaskEventEnqueue)
    641     asyncTaskScheduled(name, ptr, false);
    642   else if (type == v8AsyncTaskEventEnqueueRecurring)
    643     asyncTaskScheduled(name, ptr, true);
    644   else if (type == v8AsyncTaskEventWillHandle)
    645     asyncTaskStarted(ptr);
    646   else if (type == v8AsyncTaskEventDidHandle)
    647     asyncTaskFinished(ptr);
    648   else if (type == v8AsyncTaskEventCancel)
    649     asyncTaskCanceled(ptr);
    650   else
    651     UNREACHABLE();
    652 }
    653 
    654 V8StackTraceImpl* V8Debugger::currentAsyncCallChain() {
    655   if (!m_currentStacks.size()) return nullptr;
    656   return m_currentStacks.back().get();
    657 }
    658 
    659 void V8Debugger::compileDebuggerScript() {
    660   if (!m_debuggerScript.IsEmpty()) {
    661     UNREACHABLE();
    662     return;
    663   }
    664 
    665   v8::HandleScope scope(m_isolate);
    666   v8::Context::Scope contextScope(debuggerContext());
    667 
    668   v8::Local<v8::String> scriptValue =
    669       v8::String::NewFromUtf8(m_isolate, DebuggerScript_js,
    670                               v8::NewStringType::kInternalized,
    671                               sizeof(DebuggerScript_js))
    672           .ToLocalChecked();
    673   v8::Local<v8::Value> value;
    674   if (!m_inspector->compileAndRunInternalScript(debuggerContext(), scriptValue)
    675            .ToLocal(&value)) {
    676     UNREACHABLE();
    677     return;
    678   }
    679   DCHECK(value->IsObject());
    680   m_debuggerScript.Reset(m_isolate, value.As<v8::Object>());
    681 }
    682 
    683 v8::Local<v8::Context> V8Debugger::debuggerContext() const {
    684   DCHECK(!m_debuggerContext.IsEmpty());
    685   return m_debuggerContext.Get(m_isolate);
    686 }
    687 
    688 v8::MaybeLocal<v8::Value> V8Debugger::functionScopes(
    689     v8::Local<v8::Context> context, v8::Local<v8::Function> function) {
    690   if (!enabled()) {
    691     UNREACHABLE();
    692     return v8::Local<v8::Value>::New(m_isolate, v8::Undefined(m_isolate));
    693   }
    694   v8::Local<v8::Value> argv[] = {function};
    695   v8::Local<v8::Value> scopesValue;
    696   if (!callDebuggerMethod("getFunctionScopes", 1, argv).ToLocal(&scopesValue))
    697     return v8::MaybeLocal<v8::Value>();
    698   v8::Local<v8::Value> copied;
    699   if (!copyValueFromDebuggerContext(m_isolate, debuggerContext(), context,
    700                                     scopesValue)
    701            .ToLocal(&copied) ||
    702       !copied->IsArray())
    703     return v8::MaybeLocal<v8::Value>();
    704   if (!markAsInternal(context, v8::Local<v8::Array>::Cast(copied),
    705                       V8InternalValueType::kScopeList))
    706     return v8::MaybeLocal<v8::Value>();
    707   if (!markArrayEntriesAsInternal(context, v8::Local<v8::Array>::Cast(copied),
    708                                   V8InternalValueType::kScope))
    709     return v8::MaybeLocal<v8::Value>();
    710   return copied;
    711 }
    712 
    713 v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(
    714     v8::Local<v8::Context> context, v8::Local<v8::Value> value) {
    715   v8::Local<v8::Array> properties;
    716   if (!v8::DebugInterface::GetInternalProperties(m_isolate, value)
    717            .ToLocal(&properties))
    718     return v8::MaybeLocal<v8::Array>();
    719   if (value->IsFunction()) {
    720     v8::Local<v8::Function> function = value.As<v8::Function>();
    721     v8::Local<v8::Value> location = functionLocation(context, function);
    722     if (location->IsObject()) {
    723       createDataProperty(
    724           context, properties, properties->Length(),
    725           toV8StringInternalized(m_isolate, "[[FunctionLocation]]"));
    726       createDataProperty(context, properties, properties->Length(), location);
    727     }
    728     if (function->IsGeneratorFunction()) {
    729       createDataProperty(context, properties, properties->Length(),
    730                          toV8StringInternalized(m_isolate, "[[IsGenerator]]"));
    731       createDataProperty(context, properties, properties->Length(),
    732                          v8::True(m_isolate));
    733     }
    734   }
    735   if (!enabled()) return properties;
    736   if (value->IsMap() || value->IsWeakMap() || value->IsSet() ||
    737       value->IsWeakSet() || value->IsSetIterator() || value->IsMapIterator()) {
    738     v8::Local<v8::Value> entries =
    739         collectionEntries(context, v8::Local<v8::Object>::Cast(value));
    740     if (entries->IsArray()) {
    741       createDataProperty(context, properties, properties->Length(),
    742                          toV8StringInternalized(m_isolate, "[[Entries]]"));
    743       createDataProperty(context, properties, properties->Length(), entries);
    744     }
    745   }
    746   if (value->IsGeneratorObject()) {
    747     v8::Local<v8::Value> location =
    748         generatorObjectLocation(context, v8::Local<v8::Object>::Cast(value));
    749     if (location->IsObject()) {
    750       createDataProperty(
    751           context, properties, properties->Length(),
    752           toV8StringInternalized(m_isolate, "[[GeneratorLocation]]"));
    753       createDataProperty(context, properties, properties->Length(), location);
    754     }
    755   }
    756   if (value->IsFunction()) {
    757     v8::Local<v8::Function> function = value.As<v8::Function>();
    758     v8::Local<v8::Value> boundFunction = function->GetBoundFunction();
    759     v8::Local<v8::Value> scopes;
    760     if (boundFunction->IsUndefined() &&
    761         functionScopes(context, function).ToLocal(&scopes)) {
    762       createDataProperty(context, properties, properties->Length(),
    763                          toV8StringInternalized(m_isolate, "[[Scopes]]"));
    764       createDataProperty(context, properties, properties->Length(), scopes);
    765     }
    766   }
    767   return properties;
    768 }
    769 
    770 v8::Local<v8::Value> V8Debugger::collectionEntries(
    771     v8::Local<v8::Context> context, v8::Local<v8::Object> object) {
    772   if (!enabled()) {
    773     UNREACHABLE();
    774     return v8::Undefined(m_isolate);
    775   }
    776   v8::Local<v8::Value> argv[] = {object};
    777   v8::Local<v8::Value> entriesValue =
    778       callDebuggerMethod("getCollectionEntries", 1, argv).ToLocalChecked();
    779   if (!entriesValue->IsArray()) return v8::Undefined(m_isolate);
    780 
    781   v8::Local<v8::Array> entries = entriesValue.As<v8::Array>();
    782   v8::Local<v8::Array> copiedArray =
    783       v8::Array::New(m_isolate, entries->Length());
    784   if (!copiedArray->SetPrototype(context, v8::Null(m_isolate)).FromMaybe(false))
    785     return v8::Undefined(m_isolate);
    786   for (uint32_t i = 0; i < entries->Length(); ++i) {
    787     v8::Local<v8::Value> item;
    788     if (!entries->Get(debuggerContext(), i).ToLocal(&item))
    789       return v8::Undefined(m_isolate);
    790     v8::Local<v8::Value> copied;
    791     if (!copyValueFromDebuggerContext(m_isolate, debuggerContext(), context,
    792                                       item)
    793              .ToLocal(&copied))
    794       return v8::Undefined(m_isolate);
    795     if (!createDataProperty(context, copiedArray, i, copied).FromMaybe(false))
    796       return v8::Undefined(m_isolate);
    797   }
    798   if (!markArrayEntriesAsInternal(context,
    799                                   v8::Local<v8::Array>::Cast(copiedArray),
    800                                   V8InternalValueType::kEntry))
    801     return v8::Undefined(m_isolate);
    802   return copiedArray;
    803 }
    804 
    805 v8::Local<v8::Value> V8Debugger::generatorObjectLocation(
    806     v8::Local<v8::Context> context, v8::Local<v8::Object> object) {
    807   if (!enabled()) {
    808     UNREACHABLE();
    809     return v8::Null(m_isolate);
    810   }
    811   v8::Local<v8::Value> argv[] = {object};
    812   v8::Local<v8::Value> location =
    813       callDebuggerMethod("getGeneratorObjectLocation", 1, argv)
    814           .ToLocalChecked();
    815   v8::Local<v8::Value> copied;
    816   if (!copyValueFromDebuggerContext(m_isolate, debuggerContext(), context,
    817                                     location)
    818            .ToLocal(&copied) ||
    819       !copied->IsObject())
    820     return v8::Null(m_isolate);
    821   if (!markAsInternal(context, v8::Local<v8::Object>::Cast(copied),
    822                       V8InternalValueType::kLocation))
    823     return v8::Null(m_isolate);
    824   return copied;
    825 }
    826 
    827 v8::Local<v8::Value> V8Debugger::functionLocation(
    828     v8::Local<v8::Context> context, v8::Local<v8::Function> function) {
    829   int scriptId = function->ScriptId();
    830   if (scriptId == v8::UnboundScript::kNoScriptId) return v8::Null(m_isolate);
    831   int lineNumber = function->GetScriptLineNumber();
    832   int columnNumber = function->GetScriptColumnNumber();
    833   if (lineNumber == v8::Function::kLineOffsetNotFound ||
    834       columnNumber == v8::Function::kLineOffsetNotFound)
    835     return v8::Null(m_isolate);
    836   v8::Local<v8::Object> location = v8::Object::New(m_isolate);
    837   if (!location->SetPrototype(context, v8::Null(m_isolate)).FromMaybe(false))
    838     return v8::Null(m_isolate);
    839   if (!createDataProperty(
    840            context, location, toV8StringInternalized(m_isolate, "scriptId"),
    841            toV8String(m_isolate, String16::fromInteger(scriptId)))
    842            .FromMaybe(false))
    843     return v8::Null(m_isolate);
    844   if (!createDataProperty(context, location,
    845                           toV8StringInternalized(m_isolate, "lineNumber"),
    846                           v8::Integer::New(m_isolate, lineNumber))
    847            .FromMaybe(false))
    848     return v8::Null(m_isolate);
    849   if (!createDataProperty(context, location,
    850                           toV8StringInternalized(m_isolate, "columnNumber"),
    851                           v8::Integer::New(m_isolate, columnNumber))
    852            .FromMaybe(false))
    853     return v8::Null(m_isolate);
    854   if (!markAsInternal(context, location, V8InternalValueType::kLocation))
    855     return v8::Null(m_isolate);
    856   return location;
    857 }
    858 
    859 bool V8Debugger::isPaused() { return !m_pausedContext.IsEmpty(); }
    860 
    861 std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace(
    862     v8::Local<v8::StackTrace> stackTrace) {
    863   int contextGroupId =
    864       m_isolate->InContext() ? getGroupId(m_isolate->GetCurrentContext()) : 0;
    865   return V8StackTraceImpl::create(this, contextGroupId, stackTrace,
    866                                   V8StackTraceImpl::maxCallStackSizeToCapture);
    867 }
    868 
    869 int V8Debugger::markContext(const V8ContextInfo& info) {
    870   DCHECK(info.context->GetIsolate() == m_isolate);
    871   int contextId = ++m_lastContextId;
    872   String16 debugData = String16::fromInteger(info.contextGroupId) + "," +
    873                        String16::fromInteger(contextId) + "," +
    874                        toString16(info.auxData);
    875   v8::Context::Scope contextScope(info.context);
    876   info.context->SetEmbedderData(static_cast<int>(v8::Context::kDebugIdIndex),
    877                                 toV8String(m_isolate, debugData));
    878   return contextId;
    879 }
    880 
    881 void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) {
    882   if (depth <= 0)
    883     m_maxAsyncCallStackDepthMap.erase(agent);
    884   else
    885     m_maxAsyncCallStackDepthMap[agent] = depth;
    886 
    887   int maxAsyncCallStackDepth = 0;
    888   for (const auto& pair : m_maxAsyncCallStackDepthMap) {
    889     if (pair.second > maxAsyncCallStackDepth)
    890       maxAsyncCallStackDepth = pair.second;
    891   }
    892 
    893   if (m_maxAsyncCallStackDepth == maxAsyncCallStackDepth) return;
    894   m_maxAsyncCallStackDepth = maxAsyncCallStackDepth;
    895   if (!maxAsyncCallStackDepth) allAsyncTasksCanceled();
    896 }
    897 
    898 void V8Debugger::asyncTaskScheduled(const StringView& taskName, void* task,
    899                                     bool recurring) {
    900   if (!m_maxAsyncCallStackDepth) return;
    901   asyncTaskScheduled(toString16(taskName), task, recurring);
    902 }
    903 
    904 void V8Debugger::asyncTaskScheduled(const String16& taskName, void* task,
    905                                     bool recurring) {
    906   if (!m_maxAsyncCallStackDepth) return;
    907   v8::HandleScope scope(m_isolate);
    908   int contextGroupId =
    909       m_isolate->InContext() ? getGroupId(m_isolate->GetCurrentContext()) : 0;
    910   std::unique_ptr<V8StackTraceImpl> chain = V8StackTraceImpl::capture(
    911       this, contextGroupId, V8StackTraceImpl::maxCallStackSizeToCapture,
    912       taskName);
    913   if (chain) {
    914     m_asyncTaskStacks[task] = std::move(chain);
    915     if (recurring) m_recurringTasks.insert(task);
    916   }
    917 }
    918 
    919 void V8Debugger::asyncTaskCanceled(void* task) {
    920   if (!m_maxAsyncCallStackDepth) return;
    921   m_asyncTaskStacks.erase(task);
    922   m_recurringTasks.erase(task);
    923 }
    924 
    925 void V8Debugger::asyncTaskStarted(void* task) {
    926   if (!m_maxAsyncCallStackDepth) return;
    927   m_currentTasks.push_back(task);
    928   AsyncTaskToStackTrace::iterator stackIt = m_asyncTaskStacks.find(task);
    929   // Needs to support following order of events:
    930   // - asyncTaskScheduled
    931   //   <-- attached here -->
    932   // - asyncTaskStarted
    933   // - asyncTaskCanceled <-- canceled before finished
    934   //   <-- async stack requested here -->
    935   // - asyncTaskFinished
    936   std::unique_ptr<V8StackTraceImpl> stack;
    937   if (stackIt != m_asyncTaskStacks.end() && stackIt->second)
    938     stack = stackIt->second->cloneImpl();
    939   m_currentStacks.push_back(std::move(stack));
    940 }
    941 
    942 void V8Debugger::asyncTaskFinished(void* task) {
    943   if (!m_maxAsyncCallStackDepth) return;
    944   // We could start instrumenting half way and the stack is empty.
    945   if (!m_currentStacks.size()) return;
    946 
    947   DCHECK(m_currentTasks.back() == task);
    948   m_currentTasks.pop_back();
    949 
    950   m_currentStacks.pop_back();
    951   if (m_recurringTasks.find(task) == m_recurringTasks.end())
    952     m_asyncTaskStacks.erase(task);
    953 }
    954 
    955 void V8Debugger::allAsyncTasksCanceled() {
    956   m_asyncTaskStacks.clear();
    957   m_recurringTasks.clear();
    958   m_currentStacks.clear();
    959   m_currentTasks.clear();
    960 }
    961 
    962 void V8Debugger::muteScriptParsedEvents() {
    963   ++m_ignoreScriptParsedEventsCounter;
    964 }
    965 
    966 void V8Debugger::unmuteScriptParsedEvents() {
    967   --m_ignoreScriptParsedEventsCounter;
    968   DCHECK_GE(m_ignoreScriptParsedEventsCounter, 0);
    969 }
    970 
    971 std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace(
    972     bool fullStack) {
    973   if (!m_isolate->InContext()) return nullptr;
    974 
    975   v8::HandleScope handles(m_isolate);
    976   int contextGroupId = getGroupId(m_isolate->GetCurrentContext());
    977   if (!contextGroupId) return nullptr;
    978 
    979   size_t stackSize =
    980       fullStack ? V8StackTraceImpl::maxCallStackSizeToCapture : 1;
    981   if (m_inspector->enabledRuntimeAgentForGroup(contextGroupId))
    982     stackSize = V8StackTraceImpl::maxCallStackSizeToCapture;
    983 
    984   return V8StackTraceImpl::capture(this, contextGroupId, stackSize);
    985 }
    986 
    987 }  // namespace v8_inspector
    988