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-console.h"
      6 
      7 #include "src/base/macros.h"
      8 #include "src/inspector/injected-script.h"
      9 #include "src/inspector/inspected-context.h"
     10 #include "src/inspector/string-util.h"
     11 #include "src/inspector/v8-console-message.h"
     12 #include "src/inspector/v8-debugger-agent-impl.h"
     13 #include "src/inspector/v8-inspector-impl.h"
     14 #include "src/inspector/v8-inspector-session-impl.h"
     15 #include "src/inspector/v8-profiler-agent-impl.h"
     16 #include "src/inspector/v8-runtime-agent-impl.h"
     17 #include "src/inspector/v8-stack-trace-impl.h"
     18 #include "src/inspector/v8-value-copier.h"
     19 
     20 #include "include/v8-inspector.h"
     21 
     22 namespace v8_inspector {
     23 
     24 namespace {
     25 
     26 v8::Local<v8::Private> inspectedContextPrivateKey(v8::Isolate* isolate) {
     27   return v8::Private::ForApi(
     28       isolate, toV8StringInternalized(isolate, "V8Console#InspectedContext"));
     29 }
     30 
     31 class ConsoleHelper {
     32  public:
     33   explicit ConsoleHelper(const v8::FunctionCallbackInfo<v8::Value>& info)
     34       : m_info(info),
     35         m_isolate(info.GetIsolate()),
     36         m_context(info.GetIsolate()->GetCurrentContext()),
     37         m_inspectedContext(nullptr),
     38         m_inspectorClient(nullptr) {}
     39 
     40   v8::Local<v8::Object> ensureConsole() {
     41     if (m_console.IsEmpty()) {
     42       DCHECK(!m_info.Data().IsEmpty());
     43       DCHECK(!m_info.Data()->IsUndefined());
     44       m_console = m_info.Data().As<v8::Object>();
     45     }
     46     return m_console;
     47   }
     48 
     49   InspectedContext* ensureInspectedContext() {
     50     if (m_inspectedContext) return m_inspectedContext;
     51     v8::Local<v8::Object> console = ensureConsole();
     52 
     53     v8::Local<v8::Private> key = inspectedContextPrivateKey(m_isolate);
     54     v8::Local<v8::Value> inspectedContextValue;
     55     if (!console->GetPrivate(m_context, key).ToLocal(&inspectedContextValue))
     56       return nullptr;
     57     DCHECK(inspectedContextValue->IsExternal());
     58     m_inspectedContext = static_cast<InspectedContext*>(
     59         inspectedContextValue.As<v8::External>()->Value());
     60     return m_inspectedContext;
     61   }
     62 
     63   V8InspectorClient* ensureDebuggerClient() {
     64     if (m_inspectorClient) return m_inspectorClient;
     65     InspectedContext* inspectedContext = ensureInspectedContext();
     66     if (!inspectedContext) return nullptr;
     67     m_inspectorClient = inspectedContext->inspector()->client();
     68     return m_inspectorClient;
     69   }
     70 
     71   void reportCall(ConsoleAPIType type) {
     72     if (!m_info.Length()) return;
     73     std::vector<v8::Local<v8::Value>> arguments;
     74     for (int i = 0; i < m_info.Length(); ++i) arguments.push_back(m_info[i]);
     75     reportCall(type, arguments);
     76   }
     77 
     78   void reportCallWithDefaultArgument(ConsoleAPIType type,
     79                                      const String16& message) {
     80     std::vector<v8::Local<v8::Value>> arguments;
     81     for (int i = 0; i < m_info.Length(); ++i) arguments.push_back(m_info[i]);
     82     if (!m_info.Length()) arguments.push_back(toV8String(m_isolate, message));
     83     reportCall(type, arguments);
     84   }
     85 
     86   void reportCallWithArgument(ConsoleAPIType type, const String16& message) {
     87     std::vector<v8::Local<v8::Value>> arguments(1,
     88                                                 toV8String(m_isolate, message));
     89     reportCall(type, arguments);
     90   }
     91 
     92   void reportCall(ConsoleAPIType type,
     93                   const std::vector<v8::Local<v8::Value>>& arguments) {
     94     InspectedContext* inspectedContext = ensureInspectedContext();
     95     if (!inspectedContext) return;
     96     int contextGroupId = inspectedContext->contextGroupId();
     97     V8InspectorImpl* inspector = inspectedContext->inspector();
     98     std::unique_ptr<V8ConsoleMessage> message =
     99         V8ConsoleMessage::createForConsoleAPI(
    100             inspector->client()->currentTimeMS(), type, arguments,
    101             inspector->debugger()->captureStackTrace(false), inspectedContext);
    102     inspector->ensureConsoleMessageStorage(contextGroupId)
    103         ->addMessage(std::move(message));
    104   }
    105 
    106   void reportDeprecatedCall(const char* id, const String16& message) {
    107     if (checkAndSetPrivateFlagOnConsole(id, false)) return;
    108     std::vector<v8::Local<v8::Value>> arguments(1,
    109                                                 toV8String(m_isolate, message));
    110     reportCall(ConsoleAPIType::kWarning, arguments);
    111   }
    112 
    113   bool firstArgToBoolean(bool defaultValue) {
    114     if (m_info.Length() < 1) return defaultValue;
    115     if (m_info[0]->IsBoolean()) return m_info[0].As<v8::Boolean>()->Value();
    116     return m_info[0]->BooleanValue(m_context).FromMaybe(defaultValue);
    117   }
    118 
    119   String16 firstArgToString(const String16& defaultValue) {
    120     if (m_info.Length() < 1) return defaultValue;
    121     v8::Local<v8::String> titleValue;
    122     if (m_info[0]->IsObject()) {
    123       if (!m_info[0].As<v8::Object>()->ObjectProtoToString(m_context).ToLocal(
    124               &titleValue))
    125         return defaultValue;
    126     } else {
    127       if (!m_info[0]->ToString(m_context).ToLocal(&titleValue))
    128         return defaultValue;
    129     }
    130     return toProtocolString(titleValue);
    131   }
    132 
    133   v8::MaybeLocal<v8::Object> firstArgAsObject() {
    134     if (m_info.Length() < 1 || !m_info[0]->IsObject())
    135       return v8::MaybeLocal<v8::Object>();
    136     return m_info[0].As<v8::Object>();
    137   }
    138 
    139   v8::MaybeLocal<v8::Function> firstArgAsFunction() {
    140     if (m_info.Length() < 1 || !m_info[0]->IsFunction())
    141       return v8::MaybeLocal<v8::Function>();
    142     v8::Local<v8::Function> func = m_info[0].As<v8::Function>();
    143     while (func->GetBoundFunction()->IsFunction())
    144       func = func->GetBoundFunction().As<v8::Function>();
    145     return func;
    146   }
    147 
    148   v8::MaybeLocal<v8::Map> privateMap(const char* name) {
    149     v8::Local<v8::Object> console = ensureConsole();
    150     v8::Local<v8::Private> privateKey =
    151         v8::Private::ForApi(m_isolate, toV8StringInternalized(m_isolate, name));
    152     v8::Local<v8::Value> mapValue;
    153     if (!console->GetPrivate(m_context, privateKey).ToLocal(&mapValue))
    154       return v8::MaybeLocal<v8::Map>();
    155     if (mapValue->IsUndefined()) {
    156       v8::Local<v8::Map> map = v8::Map::New(m_isolate);
    157       if (!console->SetPrivate(m_context, privateKey, map).FromMaybe(false))
    158         return v8::MaybeLocal<v8::Map>();
    159       return map;
    160     }
    161     return mapValue->IsMap() ? mapValue.As<v8::Map>()
    162                              : v8::MaybeLocal<v8::Map>();
    163   }
    164 
    165   int32_t getIntFromMap(v8::Local<v8::Map> map, const String16& key,
    166                         int32_t defaultValue) {
    167     v8::Local<v8::String> v8Key = toV8String(m_isolate, key);
    168     if (!map->Has(m_context, v8Key).FromMaybe(false)) return defaultValue;
    169     v8::Local<v8::Value> intValue;
    170     if (!map->Get(m_context, v8Key).ToLocal(&intValue)) return defaultValue;
    171     return static_cast<int32_t>(intValue.As<v8::Integer>()->Value());
    172   }
    173 
    174   void setIntOnMap(v8::Local<v8::Map> map, const String16& key, int32_t value) {
    175     v8::Local<v8::String> v8Key = toV8String(m_isolate, key);
    176     if (!map->Set(m_context, v8Key, v8::Integer::New(m_isolate, value))
    177              .ToLocal(&map))
    178       return;
    179   }
    180 
    181   double getDoubleFromMap(v8::Local<v8::Map> map, const String16& key,
    182                           double defaultValue) {
    183     v8::Local<v8::String> v8Key = toV8String(m_isolate, key);
    184     if (!map->Has(m_context, v8Key).FromMaybe(false)) return defaultValue;
    185     v8::Local<v8::Value> intValue;
    186     if (!map->Get(m_context, v8Key).ToLocal(&intValue)) return defaultValue;
    187     return intValue.As<v8::Number>()->Value();
    188   }
    189 
    190   void setDoubleOnMap(v8::Local<v8::Map> map, const String16& key,
    191                       double value) {
    192     v8::Local<v8::String> v8Key = toV8String(m_isolate, key);
    193     if (!map->Set(m_context, v8Key, v8::Number::New(m_isolate, value))
    194              .ToLocal(&map))
    195       return;
    196   }
    197 
    198   V8ProfilerAgentImpl* profilerAgent() {
    199     if (V8InspectorSessionImpl* session = currentSession()) {
    200       if (session && session->profilerAgent()->enabled())
    201         return session->profilerAgent();
    202     }
    203     return nullptr;
    204   }
    205 
    206   V8DebuggerAgentImpl* debuggerAgent() {
    207     if (V8InspectorSessionImpl* session = currentSession()) {
    208       if (session && session->debuggerAgent()->enabled())
    209         return session->debuggerAgent();
    210     }
    211     return nullptr;
    212   }
    213 
    214   V8InspectorSessionImpl* currentSession() {
    215     InspectedContext* inspectedContext = ensureInspectedContext();
    216     if (!inspectedContext) return nullptr;
    217     return inspectedContext->inspector()->sessionForContextGroup(
    218         inspectedContext->contextGroupId());
    219   }
    220 
    221  private:
    222   const v8::FunctionCallbackInfo<v8::Value>& m_info;
    223   v8::Isolate* m_isolate;
    224   v8::Local<v8::Context> m_context;
    225   v8::Local<v8::Object> m_console;
    226   InspectedContext* m_inspectedContext;
    227   V8InspectorClient* m_inspectorClient;
    228 
    229   bool checkAndSetPrivateFlagOnConsole(const char* name, bool defaultValue) {
    230     v8::Local<v8::Object> console = ensureConsole();
    231     v8::Local<v8::Private> key =
    232         v8::Private::ForApi(m_isolate, toV8StringInternalized(m_isolate, name));
    233     v8::Local<v8::Value> flagValue;
    234     if (!console->GetPrivate(m_context, key).ToLocal(&flagValue))
    235       return defaultValue;
    236     DCHECK(flagValue->IsUndefined() || flagValue->IsBoolean());
    237     if (flagValue->IsBoolean()) {
    238       DCHECK(flagValue.As<v8::Boolean>()->Value());
    239       return true;
    240     }
    241     if (!console->SetPrivate(m_context, key, v8::True(m_isolate))
    242              .FromMaybe(false))
    243       return defaultValue;
    244     return false;
    245   }
    246 
    247   DISALLOW_COPY_AND_ASSIGN(ConsoleHelper);
    248 };
    249 
    250 void returnDataCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    251   info.GetReturnValue().Set(info.Data());
    252 }
    253 
    254 void createBoundFunctionProperty(v8::Local<v8::Context> context,
    255                                  v8::Local<v8::Object> console,
    256                                  const char* name,
    257                                  v8::FunctionCallback callback,
    258                                  const char* description = nullptr) {
    259   v8::Local<v8::String> funcName =
    260       toV8StringInternalized(context->GetIsolate(), name);
    261   v8::Local<v8::Function> func;
    262   if (!v8::Function::New(context, callback, console, 0,
    263                          v8::ConstructorBehavior::kThrow)
    264            .ToLocal(&func))
    265     return;
    266   func->SetName(funcName);
    267   if (description) {
    268     v8::Local<v8::String> returnValue =
    269         toV8String(context->GetIsolate(), description);
    270     v8::Local<v8::Function> toStringFunction;
    271     if (v8::Function::New(context, returnDataCallback, returnValue, 0,
    272                           v8::ConstructorBehavior::kThrow)
    273             .ToLocal(&toStringFunction))
    274       createDataProperty(context, func, toV8StringInternalized(
    275                                             context->GetIsolate(), "toString"),
    276                          toStringFunction);
    277   }
    278   createDataProperty(context, console, funcName, func);
    279 }
    280 
    281 }  // namespace
    282 
    283 void V8Console::debugCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    284   ConsoleHelper(info).reportCall(ConsoleAPIType::kDebug);
    285 }
    286 
    287 void V8Console::errorCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    288   ConsoleHelper(info).reportCall(ConsoleAPIType::kError);
    289 }
    290 
    291 void V8Console::infoCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    292   ConsoleHelper(info).reportCall(ConsoleAPIType::kInfo);
    293 }
    294 
    295 void V8Console::logCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    296   ConsoleHelper(info).reportCall(ConsoleAPIType::kLog);
    297 }
    298 
    299 void V8Console::warnCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    300   ConsoleHelper(info).reportCall(ConsoleAPIType::kWarning);
    301 }
    302 
    303 void V8Console::dirCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    304   ConsoleHelper(info).reportCall(ConsoleAPIType::kDir);
    305 }
    306 
    307 void V8Console::dirxmlCallback(
    308     const v8::FunctionCallbackInfo<v8::Value>& info) {
    309   ConsoleHelper(info).reportCall(ConsoleAPIType::kDirXML);
    310 }
    311 
    312 void V8Console::tableCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    313   ConsoleHelper(info).reportCall(ConsoleAPIType::kTable);
    314 }
    315 
    316 void V8Console::traceCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    317   ConsoleHelper(info).reportCallWithDefaultArgument(ConsoleAPIType::kTrace,
    318                                                     String16("console.trace"));
    319 }
    320 
    321 void V8Console::groupCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    322   ConsoleHelper(info).reportCallWithDefaultArgument(ConsoleAPIType::kStartGroup,
    323                                                     String16("console.group"));
    324 }
    325 
    326 void V8Console::groupCollapsedCallback(
    327     const v8::FunctionCallbackInfo<v8::Value>& info) {
    328   ConsoleHelper(info).reportCallWithDefaultArgument(
    329       ConsoleAPIType::kStartGroupCollapsed, String16("console.groupCollapsed"));
    330 }
    331 
    332 void V8Console::groupEndCallback(
    333     const v8::FunctionCallbackInfo<v8::Value>& info) {
    334   ConsoleHelper(info).reportCallWithDefaultArgument(
    335       ConsoleAPIType::kEndGroup, String16("console.groupEnd"));
    336 }
    337 
    338 void V8Console::clearCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    339   ConsoleHelper helper(info);
    340   InspectedContext* context = helper.ensureInspectedContext();
    341   if (!context) return;
    342   int contextGroupId = context->contextGroupId();
    343   if (V8InspectorClient* client = helper.ensureDebuggerClient())
    344     client->consoleClear(contextGroupId);
    345   helper.reportCallWithDefaultArgument(ConsoleAPIType::kClear,
    346                                        String16("console.clear"));
    347 }
    348 
    349 void V8Console::countCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    350   ConsoleHelper helper(info);
    351 
    352   String16 title = helper.firstArgToString(String16());
    353   String16 identifier;
    354   if (title.isEmpty()) {
    355     std::unique_ptr<V8StackTraceImpl> stackTrace =
    356         V8StackTraceImpl::capture(nullptr, 0, 1);
    357     if (stackTrace && !stackTrace->isEmpty()) {
    358       identifier = toString16(stackTrace->topSourceURL()) + ":" +
    359                    String16::fromInteger(stackTrace->topLineNumber());
    360     }
    361   } else {
    362     identifier = title + "@";
    363   }
    364 
    365   v8::Local<v8::Map> countMap;
    366   if (!helper.privateMap("V8Console#countMap").ToLocal(&countMap)) return;
    367   int32_t count = helper.getIntFromMap(countMap, identifier, 0) + 1;
    368   helper.setIntOnMap(countMap, identifier, count);
    369   String16 countString = String16::fromInteger(count);
    370   helper.reportCallWithArgument(
    371       ConsoleAPIType::kCount,
    372       title.isEmpty() ? countString : (title + ": " + countString));
    373 }
    374 
    375 void V8Console::assertCallback(
    376     const v8::FunctionCallbackInfo<v8::Value>& info) {
    377   ConsoleHelper helper(info);
    378   if (helper.firstArgToBoolean(false)) return;
    379 
    380   std::vector<v8::Local<v8::Value>> arguments;
    381   for (int i = 1; i < info.Length(); ++i) arguments.push_back(info[i]);
    382   if (info.Length() < 2)
    383     arguments.push_back(
    384         toV8String(info.GetIsolate(), String16("console.assert")));
    385   helper.reportCall(ConsoleAPIType::kAssert, arguments);
    386 
    387   if (V8DebuggerAgentImpl* debuggerAgent = helper.debuggerAgent())
    388     debuggerAgent->breakProgramOnException(
    389         protocol::Debugger::Paused::ReasonEnum::Assert, nullptr);
    390 }
    391 
    392 void V8Console::markTimelineCallback(
    393     const v8::FunctionCallbackInfo<v8::Value>& info) {
    394   ConsoleHelper(info).reportDeprecatedCall("V8Console#markTimelineDeprecated",
    395                                            "'console.markTimeline' is "
    396                                            "deprecated. Please use "
    397                                            "'console.timeStamp' instead.");
    398   timeStampCallback(info);
    399 }
    400 
    401 void V8Console::profileCallback(
    402     const v8::FunctionCallbackInfo<v8::Value>& info) {
    403   ConsoleHelper helper(info);
    404   if (V8ProfilerAgentImpl* profilerAgent = helper.profilerAgent())
    405     profilerAgent->consoleProfile(helper.firstArgToString(String16()));
    406 }
    407 
    408 void V8Console::profileEndCallback(
    409     const v8::FunctionCallbackInfo<v8::Value>& info) {
    410   ConsoleHelper helper(info);
    411   if (V8ProfilerAgentImpl* profilerAgent = helper.profilerAgent())
    412     profilerAgent->consoleProfileEnd(helper.firstArgToString(String16()));
    413 }
    414 
    415 static void timeFunction(const v8::FunctionCallbackInfo<v8::Value>& info,
    416                          bool timelinePrefix) {
    417   ConsoleHelper helper(info);
    418   if (V8InspectorClient* client = helper.ensureDebuggerClient()) {
    419     String16 protocolTitle = helper.firstArgToString("default");
    420     if (timelinePrefix) protocolTitle = "Timeline '" + protocolTitle + "'";
    421     client->consoleTime(toStringView(protocolTitle));
    422 
    423     v8::Local<v8::Map> timeMap;
    424     if (!helper.privateMap("V8Console#timeMap").ToLocal(&timeMap)) return;
    425     helper.setDoubleOnMap(timeMap, protocolTitle, client->currentTimeMS());
    426   }
    427 }
    428 
    429 static void timeEndFunction(const v8::FunctionCallbackInfo<v8::Value>& info,
    430                             bool timelinePrefix) {
    431   ConsoleHelper helper(info);
    432   if (V8InspectorClient* client = helper.ensureDebuggerClient()) {
    433     String16 protocolTitle = helper.firstArgToString("default");
    434     if (timelinePrefix) protocolTitle = "Timeline '" + protocolTitle + "'";
    435     client->consoleTimeEnd(toStringView(protocolTitle));
    436 
    437     v8::Local<v8::Map> timeMap;
    438     if (!helper.privateMap("V8Console#timeMap").ToLocal(&timeMap)) return;
    439     double elapsed = client->currentTimeMS() -
    440                      helper.getDoubleFromMap(timeMap, protocolTitle, 0.0);
    441     String16 message =
    442         protocolTitle + ": " + String16::fromDouble(elapsed) + "ms";
    443     helper.reportCallWithArgument(ConsoleAPIType::kTimeEnd, message);
    444   }
    445 }
    446 
    447 void V8Console::timelineCallback(
    448     const v8::FunctionCallbackInfo<v8::Value>& info) {
    449   ConsoleHelper(info).reportDeprecatedCall(
    450       "V8Console#timeline",
    451       "'console.timeline' is deprecated. Please use 'console.time' instead.");
    452   timeFunction(info, true);
    453 }
    454 
    455 void V8Console::timelineEndCallback(
    456     const v8::FunctionCallbackInfo<v8::Value>& info) {
    457   ConsoleHelper(info).reportDeprecatedCall("V8Console#timelineEnd",
    458                                            "'console.timelineEnd' is "
    459                                            "deprecated. Please use "
    460                                            "'console.timeEnd' instead.");
    461   timeEndFunction(info, true);
    462 }
    463 
    464 void V8Console::timeCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    465   timeFunction(info, false);
    466 }
    467 
    468 void V8Console::timeEndCallback(
    469     const v8::FunctionCallbackInfo<v8::Value>& info) {
    470   timeEndFunction(info, false);
    471 }
    472 
    473 void V8Console::timeStampCallback(
    474     const v8::FunctionCallbackInfo<v8::Value>& info) {
    475   ConsoleHelper helper(info);
    476   if (V8InspectorClient* client = helper.ensureDebuggerClient()) {
    477     String16 title = helper.firstArgToString(String16());
    478     client->consoleTimeStamp(toStringView(title));
    479   }
    480 }
    481 
    482 void V8Console::memoryGetterCallback(
    483     const v8::FunctionCallbackInfo<v8::Value>& info) {
    484   if (V8InspectorClient* client = ConsoleHelper(info).ensureDebuggerClient()) {
    485     v8::Local<v8::Value> memoryValue;
    486     if (!client
    487              ->memoryInfo(info.GetIsolate(),
    488                           info.GetIsolate()->GetCurrentContext())
    489              .ToLocal(&memoryValue))
    490       return;
    491     info.GetReturnValue().Set(memoryValue);
    492   }
    493 }
    494 
    495 void V8Console::memorySetterCallback(
    496     const v8::FunctionCallbackInfo<v8::Value>& info) {
    497   // We can't make the attribute readonly as it breaks existing code that relies
    498   // on being able to assign to console.memory in strict mode. Instead, the
    499   // setter just ignores the passed value.  http://crbug.com/468611
    500 }
    501 
    502 void V8Console::keysCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    503   v8::Isolate* isolate = info.GetIsolate();
    504   info.GetReturnValue().Set(v8::Array::New(isolate));
    505 
    506   ConsoleHelper helper(info);
    507   v8::Local<v8::Object> obj;
    508   if (!helper.firstArgAsObject().ToLocal(&obj)) return;
    509   v8::Local<v8::Array> names;
    510   if (!obj->GetOwnPropertyNames(isolate->GetCurrentContext()).ToLocal(&names))
    511     return;
    512   info.GetReturnValue().Set(names);
    513 }
    514 
    515 void V8Console::valuesCallback(
    516     const v8::FunctionCallbackInfo<v8::Value>& info) {
    517   v8::Isolate* isolate = info.GetIsolate();
    518   info.GetReturnValue().Set(v8::Array::New(isolate));
    519 
    520   ConsoleHelper helper(info);
    521   v8::Local<v8::Object> obj;
    522   if (!helper.firstArgAsObject().ToLocal(&obj)) return;
    523   v8::Local<v8::Array> names;
    524   v8::Local<v8::Context> context = isolate->GetCurrentContext();
    525   if (!obj->GetOwnPropertyNames(context).ToLocal(&names)) return;
    526   v8::Local<v8::Array> values = v8::Array::New(isolate, names->Length());
    527   for (uint32_t i = 0; i < names->Length(); ++i) {
    528     v8::Local<v8::Value> key;
    529     if (!names->Get(context, i).ToLocal(&key)) continue;
    530     v8::Local<v8::Value> value;
    531     if (!obj->Get(context, key).ToLocal(&value)) continue;
    532     createDataProperty(context, values, i, value);
    533   }
    534   info.GetReturnValue().Set(values);
    535 }
    536 
    537 static void setFunctionBreakpoint(ConsoleHelper& helper,
    538                                   v8::Local<v8::Function> function,
    539                                   V8DebuggerAgentImpl::BreakpointSource source,
    540                                   const String16& condition, bool enable) {
    541   V8DebuggerAgentImpl* debuggerAgent = helper.debuggerAgent();
    542   if (!debuggerAgent) return;
    543   String16 scriptId = String16::fromInteger(function->ScriptId());
    544   int lineNumber = function->GetScriptLineNumber();
    545   int columnNumber = function->GetScriptColumnNumber();
    546   if (lineNumber == v8::Function::kLineOffsetNotFound ||
    547       columnNumber == v8::Function::kLineOffsetNotFound)
    548     return;
    549   if (enable)
    550     debuggerAgent->setBreakpointAt(scriptId, lineNumber, columnNumber, source,
    551                                    condition);
    552   else
    553     debuggerAgent->removeBreakpointAt(scriptId, lineNumber, columnNumber,
    554                                       source);
    555 }
    556 
    557 void V8Console::debugFunctionCallback(
    558     const v8::FunctionCallbackInfo<v8::Value>& info) {
    559   ConsoleHelper helper(info);
    560   v8::Local<v8::Function> function;
    561   if (!helper.firstArgAsFunction().ToLocal(&function)) return;
    562   setFunctionBreakpoint(helper, function,
    563                         V8DebuggerAgentImpl::DebugCommandBreakpointSource,
    564                         String16(), true);
    565 }
    566 
    567 void V8Console::undebugFunctionCallback(
    568     const v8::FunctionCallbackInfo<v8::Value>& info) {
    569   ConsoleHelper helper(info);
    570   v8::Local<v8::Function> function;
    571   if (!helper.firstArgAsFunction().ToLocal(&function)) return;
    572   setFunctionBreakpoint(helper, function,
    573                         V8DebuggerAgentImpl::DebugCommandBreakpointSource,
    574                         String16(), false);
    575 }
    576 
    577 void V8Console::monitorFunctionCallback(
    578     const v8::FunctionCallbackInfo<v8::Value>& info) {
    579   ConsoleHelper helper(info);
    580   v8::Local<v8::Function> function;
    581   if (!helper.firstArgAsFunction().ToLocal(&function)) return;
    582   v8::Local<v8::Value> name = function->GetName();
    583   if (!name->IsString() || !v8::Local<v8::String>::Cast(name)->Length())
    584     name = function->GetInferredName();
    585   String16 functionName = toProtocolStringWithTypeCheck(name);
    586   String16Builder builder;
    587   builder.append("console.log(\"function ");
    588   if (functionName.isEmpty())
    589     builder.append("(anonymous function)");
    590   else
    591     builder.append(functionName);
    592   builder.append(
    593       " called\" + (arguments.length > 0 ? \" with arguments: \" + "
    594       "Array.prototype.join.call(arguments, \", \") : \"\")) && false");
    595   setFunctionBreakpoint(helper, function,
    596                         V8DebuggerAgentImpl::MonitorCommandBreakpointSource,
    597                         builder.toString(), true);
    598 }
    599 
    600 void V8Console::unmonitorFunctionCallback(
    601     const v8::FunctionCallbackInfo<v8::Value>& info) {
    602   ConsoleHelper helper(info);
    603   v8::Local<v8::Function> function;
    604   if (!helper.firstArgAsFunction().ToLocal(&function)) return;
    605   setFunctionBreakpoint(helper, function,
    606                         V8DebuggerAgentImpl::MonitorCommandBreakpointSource,
    607                         String16(), false);
    608 }
    609 
    610 void V8Console::lastEvaluationResultCallback(
    611     const v8::FunctionCallbackInfo<v8::Value>& info) {
    612   ConsoleHelper helper(info);
    613   InspectedContext* context = helper.ensureInspectedContext();
    614   if (!context) return;
    615   if (InjectedScript* injectedScript = context->getInjectedScript())
    616     info.GetReturnValue().Set(injectedScript->lastEvaluationResult());
    617 }
    618 
    619 static void inspectImpl(const v8::FunctionCallbackInfo<v8::Value>& info,
    620                         bool copyToClipboard) {
    621   if (info.Length() < 1) return;
    622   if (!copyToClipboard) info.GetReturnValue().Set(info[0]);
    623 
    624   ConsoleHelper helper(info);
    625   InspectedContext* context = helper.ensureInspectedContext();
    626   if (!context) return;
    627   InjectedScript* injectedScript = context->getInjectedScript();
    628   if (!injectedScript) return;
    629   std::unique_ptr<protocol::Runtime::RemoteObject> wrappedObject;
    630   protocol::Response response =
    631       injectedScript->wrapObject(info[0], "", false /** forceValueType */,
    632                                  false /** generatePreview */, &wrappedObject);
    633   if (!response.isSuccess()) return;
    634 
    635   std::unique_ptr<protocol::DictionaryValue> hints =
    636       protocol::DictionaryValue::create();
    637   if (copyToClipboard) hints->setBoolean("copyToClipboard", true);
    638   if (V8InspectorSessionImpl* session = helper.currentSession())
    639     session->runtimeAgent()->inspect(std::move(wrappedObject),
    640                                      std::move(hints));
    641 }
    642 
    643 void V8Console::inspectCallback(
    644     const v8::FunctionCallbackInfo<v8::Value>& info) {
    645   inspectImpl(info, false);
    646 }
    647 
    648 void V8Console::copyCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
    649   inspectImpl(info, true);
    650 }
    651 
    652 void V8Console::inspectedObject(const v8::FunctionCallbackInfo<v8::Value>& info,
    653                                 unsigned num) {
    654   DCHECK(num < V8InspectorSessionImpl::kInspectedObjectBufferSize);
    655   ConsoleHelper helper(info);
    656   if (V8InspectorSessionImpl* session = helper.currentSession()) {
    657     V8InspectorSession::Inspectable* object = session->inspectedObject(num);
    658     v8::Isolate* isolate = info.GetIsolate();
    659     if (object)
    660       info.GetReturnValue().Set(object->get(isolate->GetCurrentContext()));
    661     else
    662       info.GetReturnValue().Set(v8::Undefined(isolate));
    663   }
    664 }
    665 
    666 v8::Local<v8::Object> V8Console::createConsole(
    667     InspectedContext* inspectedContext, bool hasMemoryAttribute) {
    668   v8::Local<v8::Context> context = inspectedContext->context();
    669   v8::Context::Scope contextScope(context);
    670   v8::Isolate* isolate = context->GetIsolate();
    671   v8::MicrotasksScope microtasksScope(isolate,
    672                                       v8::MicrotasksScope::kDoNotRunMicrotasks);
    673 
    674   v8::Local<v8::Object> console = v8::Object::New(isolate);
    675   bool success =
    676       console->SetPrototype(context, v8::Object::New(isolate)).FromMaybe(false);
    677   DCHECK(success);
    678   USE(success);
    679 
    680   createBoundFunctionProperty(context, console, "debug",
    681                               V8Console::debugCallback);
    682   createBoundFunctionProperty(context, console, "error",
    683                               V8Console::errorCallback);
    684   createBoundFunctionProperty(context, console, "info",
    685                               V8Console::infoCallback);
    686   createBoundFunctionProperty(context, console, "log", V8Console::logCallback);
    687   createBoundFunctionProperty(context, console, "warn",
    688                               V8Console::warnCallback);
    689   createBoundFunctionProperty(context, console, "dir", V8Console::dirCallback);
    690   createBoundFunctionProperty(context, console, "dirxml",
    691                               V8Console::dirxmlCallback);
    692   createBoundFunctionProperty(context, console, "table",
    693                               V8Console::tableCallback);
    694   createBoundFunctionProperty(context, console, "trace",
    695                               V8Console::traceCallback);
    696   createBoundFunctionProperty(context, console, "group",
    697                               V8Console::groupCallback);
    698   createBoundFunctionProperty(context, console, "groupCollapsed",
    699                               V8Console::groupCollapsedCallback);
    700   createBoundFunctionProperty(context, console, "groupEnd",
    701                               V8Console::groupEndCallback);
    702   createBoundFunctionProperty(context, console, "clear",
    703                               V8Console::clearCallback);
    704   createBoundFunctionProperty(context, console, "count",
    705                               V8Console::countCallback);
    706   createBoundFunctionProperty(context, console, "assert",
    707                               V8Console::assertCallback);
    708   createBoundFunctionProperty(context, console, "markTimeline",
    709                               V8Console::markTimelineCallback);
    710   createBoundFunctionProperty(context, console, "profile",
    711                               V8Console::profileCallback);
    712   createBoundFunctionProperty(context, console, "profileEnd",
    713                               V8Console::profileEndCallback);
    714   createBoundFunctionProperty(context, console, "timeline",
    715                               V8Console::timelineCallback);
    716   createBoundFunctionProperty(context, console, "timelineEnd",
    717                               V8Console::timelineEndCallback);
    718   createBoundFunctionProperty(context, console, "time",
    719                               V8Console::timeCallback);
    720   createBoundFunctionProperty(context, console, "timeEnd",
    721                               V8Console::timeEndCallback);
    722   createBoundFunctionProperty(context, console, "timeStamp",
    723                               V8Console::timeStampCallback);
    724 
    725   const char* jsConsoleAssert =
    726       "(function(){\n"
    727       "  var originAssert = this.assert;\n"
    728       "  originAssert.apply = Function.prototype.apply;\n"
    729       "  this.assert = assertWrapper;\n"
    730       "  assertWrapper.toString = () => originAssert.toString();\n"
    731       "  function assertWrapper(){\n"
    732       "    if (!!arguments[0]) return;\n"
    733       "    originAssert.apply(null, arguments);\n"
    734       "  }\n"
    735       "})";
    736 
    737   v8::Local<v8::String> assertSource = toV8String(isolate, jsConsoleAssert);
    738   V8InspectorImpl* inspector = inspectedContext->inspector();
    739   v8::Local<v8::Value> setupFunction;
    740   if (inspector->compileAndRunInternalScript(context, assertSource)
    741           .ToLocal(&setupFunction) &&
    742       setupFunction->IsFunction()) {
    743     inspector->callInternalFunction(
    744         v8::Local<v8::Function>::Cast(setupFunction), context, console, 0,
    745         nullptr);
    746   }
    747 
    748   if (hasMemoryAttribute)
    749     console->SetAccessorProperty(
    750         toV8StringInternalized(isolate, "memory"),
    751         v8::Function::New(context, V8Console::memoryGetterCallback, console, 0,
    752                           v8::ConstructorBehavior::kThrow)
    753             .ToLocalChecked(),
    754         v8::Function::New(context, V8Console::memorySetterCallback,
    755                           v8::Local<v8::Value>(), 0,
    756                           v8::ConstructorBehavior::kThrow)
    757             .ToLocalChecked(),
    758         static_cast<v8::PropertyAttribute>(v8::None), v8::DEFAULT);
    759 
    760   console->SetPrivate(context, inspectedContextPrivateKey(isolate),
    761                       v8::External::New(isolate, inspectedContext));
    762   return console;
    763 }
    764 
    765 void V8Console::clearInspectedContextIfNeeded(v8::Local<v8::Context> context,
    766                                               v8::Local<v8::Object> console) {
    767   v8::Isolate* isolate = context->GetIsolate();
    768   console->SetPrivate(context, inspectedContextPrivateKey(isolate),
    769                       v8::External::New(isolate, nullptr));
    770 }
    771 
    772 v8::Local<v8::Object> V8Console::createCommandLineAPI(
    773     InspectedContext* inspectedContext) {
    774   v8::Local<v8::Context> context = inspectedContext->context();
    775   v8::Isolate* isolate = context->GetIsolate();
    776   v8::MicrotasksScope microtasksScope(isolate,
    777                                       v8::MicrotasksScope::kDoNotRunMicrotasks);
    778 
    779   v8::Local<v8::Object> commandLineAPI = v8::Object::New(isolate);
    780   bool success =
    781       commandLineAPI->SetPrototype(context, v8::Null(isolate)).FromMaybe(false);
    782   DCHECK(success);
    783   USE(success);
    784 
    785   createBoundFunctionProperty(context, commandLineAPI, "dir",
    786                               V8Console::dirCallback,
    787                               "function dir(value) { [Command Line API] }");
    788   createBoundFunctionProperty(context, commandLineAPI, "dirxml",
    789                               V8Console::dirxmlCallback,
    790                               "function dirxml(value) { [Command Line API] }");
    791   createBoundFunctionProperty(context, commandLineAPI, "profile",
    792                               V8Console::profileCallback,
    793                               "function profile(title) { [Command Line API] }");
    794   createBoundFunctionProperty(
    795       context, commandLineAPI, "profileEnd", V8Console::profileEndCallback,
    796       "function profileEnd(title) { [Command Line API] }");
    797   createBoundFunctionProperty(context, commandLineAPI, "clear",
    798                               V8Console::clearCallback,
    799                               "function clear() { [Command Line API] }");
    800   createBoundFunctionProperty(
    801       context, commandLineAPI, "table", V8Console::tableCallback,
    802       "function table(data, [columns]) { [Command Line API] }");
    803 
    804   createBoundFunctionProperty(context, commandLineAPI, "keys",
    805                               V8Console::keysCallback,
    806                               "function keys(object) { [Command Line API] }");
    807   createBoundFunctionProperty(context, commandLineAPI, "values",
    808                               V8Console::valuesCallback,
    809                               "function values(object) { [Command Line API] }");
    810   createBoundFunctionProperty(
    811       context, commandLineAPI, "debug", V8Console::debugFunctionCallback,
    812       "function debug(function) { [Command Line API] }");
    813   createBoundFunctionProperty(
    814       context, commandLineAPI, "undebug", V8Console::undebugFunctionCallback,
    815       "function undebug(function) { [Command Line API] }");
    816   createBoundFunctionProperty(
    817       context, commandLineAPI, "monitor", V8Console::monitorFunctionCallback,
    818       "function monitor(function) { [Command Line API] }");
    819   createBoundFunctionProperty(
    820       context, commandLineAPI, "unmonitor",
    821       V8Console::unmonitorFunctionCallback,
    822       "function unmonitor(function) { [Command Line API] }");
    823   createBoundFunctionProperty(
    824       context, commandLineAPI, "inspect", V8Console::inspectCallback,
    825       "function inspect(object) { [Command Line API] }");
    826   createBoundFunctionProperty(context, commandLineAPI, "copy",
    827                               V8Console::copyCallback,
    828                               "function copy(value) { [Command Line API] }");
    829   createBoundFunctionProperty(context, commandLineAPI, "$_",
    830                               V8Console::lastEvaluationResultCallback);
    831   createBoundFunctionProperty(context, commandLineAPI, "$0",
    832                               V8Console::inspectedObject0);
    833   createBoundFunctionProperty(context, commandLineAPI, "$1",
    834                               V8Console::inspectedObject1);
    835   createBoundFunctionProperty(context, commandLineAPI, "$2",
    836                               V8Console::inspectedObject2);
    837   createBoundFunctionProperty(context, commandLineAPI, "$3",
    838                               V8Console::inspectedObject3);
    839   createBoundFunctionProperty(context, commandLineAPI, "$4",
    840                               V8Console::inspectedObject4);
    841 
    842   inspectedContext->inspector()->client()->installAdditionalCommandLineAPI(
    843       context, commandLineAPI);
    844 
    845   commandLineAPI->SetPrivate(context, inspectedContextPrivateKey(isolate),
    846                              v8::External::New(isolate, inspectedContext));
    847   return commandLineAPI;
    848 }
    849 
    850 static bool isCommandLineAPIGetter(const String16& name) {
    851   if (name.length() != 2) return false;
    852   // $0 ... $4, $_
    853   return name[0] == '$' &&
    854          ((name[1] >= '0' && name[1] <= '4') || name[1] == '_');
    855 }
    856 
    857 void V8Console::CommandLineAPIScope::accessorGetterCallback(
    858     v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
    859   CommandLineAPIScope* scope = static_cast<CommandLineAPIScope*>(
    860       info.Data().As<v8::External>()->Value());
    861   DCHECK(scope);
    862 
    863   v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
    864   if (scope->m_cleanup) {
    865     bool removed = info.Holder()->Delete(context, name).FromMaybe(false);
    866     DCHECK(removed);
    867     USE(removed);
    868     return;
    869   }
    870   v8::Local<v8::Object> commandLineAPI = scope->m_commandLineAPI;
    871 
    872   v8::Local<v8::Value> value;
    873   if (!commandLineAPI->Get(context, name).ToLocal(&value)) return;
    874   if (isCommandLineAPIGetter(toProtocolStringWithTypeCheck(name))) {
    875     DCHECK(value->IsFunction());
    876     v8::MicrotasksScope microtasks(info.GetIsolate(),
    877                                    v8::MicrotasksScope::kDoNotRunMicrotasks);
    878     if (value.As<v8::Function>()
    879             ->Call(context, commandLineAPI, 0, nullptr)
    880             .ToLocal(&value))
    881       info.GetReturnValue().Set(value);
    882   } else {
    883     info.GetReturnValue().Set(value);
    884   }
    885 }
    886 
    887 void V8Console::CommandLineAPIScope::accessorSetterCallback(
    888     v8::Local<v8::Name> name, v8::Local<v8::Value> value,
    889     const v8::PropertyCallbackInfo<void>& info) {
    890   CommandLineAPIScope* scope = static_cast<CommandLineAPIScope*>(
    891       info.Data().As<v8::External>()->Value());
    892   v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
    893   if (!info.Holder()->Delete(context, name).FromMaybe(false)) return;
    894   if (!info.Holder()->CreateDataProperty(context, name, value).FromMaybe(false))
    895     return;
    896   bool removed =
    897       scope->m_installedMethods->Delete(context, name).FromMaybe(false);
    898   DCHECK(removed);
    899   USE(removed);
    900 }
    901 
    902 V8Console::CommandLineAPIScope::CommandLineAPIScope(
    903     v8::Local<v8::Context> context, v8::Local<v8::Object> commandLineAPI,
    904     v8::Local<v8::Object> global)
    905     : m_context(context),
    906       m_commandLineAPI(commandLineAPI),
    907       m_global(global),
    908       m_installedMethods(v8::Set::New(context->GetIsolate())),
    909       m_cleanup(false) {
    910   v8::Local<v8::Array> names;
    911   if (!m_commandLineAPI->GetOwnPropertyNames(context).ToLocal(&names)) return;
    912   v8::Local<v8::External> externalThis =
    913       v8::External::New(context->GetIsolate(), this);
    914   for (uint32_t i = 0; i < names->Length(); ++i) {
    915     v8::Local<v8::Value> name;
    916     if (!names->Get(context, i).ToLocal(&name) || !name->IsName()) continue;
    917     if (m_global->Has(context, name).FromMaybe(true)) continue;
    918     if (!m_installedMethods->Add(context, name).ToLocal(&m_installedMethods))
    919       continue;
    920     if (!m_global
    921              ->SetAccessor(context, v8::Local<v8::Name>::Cast(name),
    922                            CommandLineAPIScope::accessorGetterCallback,
    923                            CommandLineAPIScope::accessorSetterCallback,
    924                            externalThis, v8::DEFAULT, v8::DontEnum)
    925              .FromMaybe(false)) {
    926       bool removed = m_installedMethods->Delete(context, name).FromMaybe(false);
    927       DCHECK(removed);
    928       USE(removed);
    929       continue;
    930     }
    931   }
    932 }
    933 
    934 V8Console::CommandLineAPIScope::~CommandLineAPIScope() {
    935   m_cleanup = true;
    936   v8::Local<v8::Array> names = m_installedMethods->AsArray();
    937   for (uint32_t i = 0; i < names->Length(); ++i) {
    938     v8::Local<v8::Value> name;
    939     if (!names->Get(m_context, i).ToLocal(&name) || !name->IsName()) continue;
    940     if (name->IsString()) {
    941       v8::Local<v8::Value> descriptor;
    942       bool success = m_global
    943                          ->GetOwnPropertyDescriptor(
    944                              m_context, v8::Local<v8::String>::Cast(name))
    945                          .ToLocal(&descriptor);
    946       DCHECK(success);
    947       USE(success);
    948     }
    949   }
    950 }
    951 
    952 }  // namespace v8_inspector
    953