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