1 // Copyright 2018 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/value-mirror.h"
6 
7 #include <algorithm>
8 #include <cmath>
9 
10 #include "src/base/optional.h"
11 #include "src/debug/debug-interface.h"
12 #include "src/inspector/v8-debugger.h"
13 #include "src/inspector/v8-inspector-impl.h"
14 #include "src/inspector/v8-value-utils.h"
15 
16 namespace v8_inspector {
17 
18 using protocol::Response;
19 using protocol::Runtime::EntryPreview;
20 using protocol::Runtime::ObjectPreview;
21 using protocol::Runtime::PropertyPreview;
22 using protocol::Runtime::RemoteObject;
23 
24 namespace {
clientFor(v8::Local<v8::Context> context)25 V8InspectorClient* clientFor(v8::Local<v8::Context> context) {
26   return static_cast<V8InspectorImpl*>(
27              v8::debug::GetInspector(context->GetIsolate()))
28       ->client();
29 }
30 
v8InternalValueTypeFrom(v8::Local<v8::Context> context,v8::Local<v8::Value> value)31 V8InternalValueType v8InternalValueTypeFrom(v8::Local<v8::Context> context,
32                                             v8::Local<v8::Value> value) {
33   if (!value->IsObject()) return V8InternalValueType::kNone;
34   V8InspectorImpl* inspector = static_cast<V8InspectorImpl*>(
35       v8::debug::GetInspector(context->GetIsolate()));
36   int contextId = InspectedContext::contextId(context);
37   InspectedContext* inspectedContext = inspector->getContext(contextId);
38   if (!inspectedContext) return V8InternalValueType::kNone;
39   return inspectedContext->getInternalType(value.As<v8::Object>());
40 }
41 
toProtocolValue(v8::Local<v8::Context> context,v8::Local<v8::Value> value,int maxDepth,std::unique_ptr<protocol::Value> * result)42 Response toProtocolValue(v8::Local<v8::Context> context,
43                          v8::Local<v8::Value> value, int maxDepth,
44                          std::unique_ptr<protocol::Value>* result) {
45   if (!maxDepth)
46     return Response::ServerError("Object reference chain is too long");
47   maxDepth--;
48 
49   if (value->IsNull() || value->IsUndefined()) {
50     *result = protocol::Value::null();
51     return Response::Success();
52   }
53   if (value->IsBoolean()) {
54     *result =
55         protocol::FundamentalValue::create(value.As<v8::Boolean>()->Value());
56     return Response::Success();
57   }
58   if (value->IsNumber()) {
59     double doubleValue = value.As<v8::Number>()->Value();
60     if (doubleValue >= std::numeric_limits<int>::min() &&
61         doubleValue <= std::numeric_limits<int>::max() &&
62         bit_cast<int64_t>(doubleValue) != bit_cast<int64_t>(-0.0)) {
63       int intValue = static_cast<int>(doubleValue);
64       if (intValue == doubleValue) {
65         *result = protocol::FundamentalValue::create(intValue);
66         return Response::Success();
67       }
68     }
69     *result = protocol::FundamentalValue::create(doubleValue);
70     return Response::Success();
71   }
72   if (value->IsString()) {
73     *result = protocol::StringValue::create(
74         toProtocolString(context->GetIsolate(), value.As<v8::String>()));
75     return Response::Success();
76   }
77   if (value->IsArray()) {
78     v8::Local<v8::Array> array = value.As<v8::Array>();
79     std::unique_ptr<protocol::ListValue> inspectorArray =
80         protocol::ListValue::create();
81     uint32_t length = array->Length();
82     for (uint32_t i = 0; i < length; i++) {
83       v8::Local<v8::Value> value;
84       if (!array->Get(context, i).ToLocal(&value))
85         return Response::InternalError();
86       std::unique_ptr<protocol::Value> element;
87       Response response = toProtocolValue(context, value, maxDepth, &element);
88       if (!response.IsSuccess()) return response;
89       inspectorArray->pushValue(std::move(element));
90     }
91     *result = std::move(inspectorArray);
92     return Response::Success();
93   }
94   if (value->IsObject()) {
95     std::unique_ptr<protocol::DictionaryValue> jsonObject =
96         protocol::DictionaryValue::create();
97     v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(value);
98     v8::Local<v8::Array> propertyNames;
99     if (!object->GetPropertyNames(context).ToLocal(&propertyNames))
100       return Response::InternalError();
101     uint32_t length = propertyNames->Length();
102     for (uint32_t i = 0; i < length; i++) {
103       v8::Local<v8::Value> name;
104       if (!propertyNames->Get(context, i).ToLocal(&name))
105         return Response::InternalError();
106       // FIXME(yurys): v8::Object should support GetOwnPropertyNames
107       if (name->IsString()) {
108         v8::Maybe<bool> hasRealNamedProperty = object->HasRealNamedProperty(
109             context, v8::Local<v8::String>::Cast(name));
110         if (hasRealNamedProperty.IsNothing() ||
111             !hasRealNamedProperty.FromJust())
112           continue;
113       }
114       v8::Local<v8::String> propertyName;
115       if (!name->ToString(context).ToLocal(&propertyName)) continue;
116       v8::Local<v8::Value> property;
117       if (!object->Get(context, name).ToLocal(&property))
118         return Response::InternalError();
119       if (property->IsUndefined()) continue;
120       std::unique_ptr<protocol::Value> propertyValue;
121       Response response =
122           toProtocolValue(context, property, maxDepth, &propertyValue);
123       if (!response.IsSuccess()) return response;
124       jsonObject->setValue(
125           toProtocolString(context->GetIsolate(), propertyName),
126           std::move(propertyValue));
127     }
128     *result = std::move(jsonObject);
129     return Response::Success();
130   }
131   return Response::ServerError("Object couldn't be returned by value");
132 }
133 
toProtocolValue(v8::Local<v8::Context> context,v8::Local<v8::Value> value,std::unique_ptr<protocol::Value> * result)134 Response toProtocolValue(v8::Local<v8::Context> context,
135                          v8::Local<v8::Value> value,
136                          std::unique_ptr<protocol::Value>* result) {
137   if (value->IsUndefined()) return Response::Success();
138   return toProtocolValue(context, value, 1000, result);
139 }
140 
141 enum AbbreviateMode { kMiddle, kEnd };
142 
abbreviateString(const String16 & value,AbbreviateMode mode)143 String16 abbreviateString(const String16& value, AbbreviateMode mode) {
144   const size_t maxLength = 100;
145   if (value.length() <= maxLength) return value;
146   UChar ellipsis = static_cast<UChar>(0x2026);
147   if (mode == kMiddle) {
148     return String16::concat(
149         value.substring(0, maxLength / 2), String16(&ellipsis, 1),
150         value.substring(value.length() - maxLength / 2 + 1));
151   }
152   return String16::concat(value.substring(0, maxLength - 1), ellipsis);
153 }
154 
descriptionForSymbol(v8::Local<v8::Context> context,v8::Local<v8::Symbol> symbol)155 String16 descriptionForSymbol(v8::Local<v8::Context> context,
156                               v8::Local<v8::Symbol> symbol) {
157   return String16::concat("Symbol(",
158                           toProtocolStringWithTypeCheck(context->GetIsolate(),
159                                                         symbol->Description()),
160                           ")");
161 }
162 
descriptionForBigInt(v8::Local<v8::Context> context,v8::Local<v8::BigInt> value)163 String16 descriptionForBigInt(v8::Local<v8::Context> context,
164                               v8::Local<v8::BigInt> value) {
165   v8::Isolate* isolate = context->GetIsolate();
166   v8::TryCatch tryCatch(isolate);
167   v8::Local<v8::String> description;
168   if (!value->ToString(context).ToLocal(&description)) return String16();
169   return toProtocolString(isolate, description) + "n";
170 }
171 
descriptionForPrimitiveType(v8::Local<v8::Context> context,v8::Local<v8::Value> value)172 String16 descriptionForPrimitiveType(v8::Local<v8::Context> context,
173                                      v8::Local<v8::Value> value) {
174   if (value->IsUndefined()) return RemoteObject::TypeEnum::Undefined;
175   if (value->IsNull()) return RemoteObject::SubtypeEnum::Null;
176   if (value->IsBoolean()) {
177     return value.As<v8::Boolean>()->Value() ? "true" : "false";
178   }
179   if (value->IsString()) {
180     return toProtocolString(context->GetIsolate(), value.As<v8::String>());
181   }
182   UNREACHABLE();
183   return String16();
184 }
185 
descriptionForRegExp(v8::Isolate * isolate,v8::Local<v8::RegExp> value)186 String16 descriptionForRegExp(v8::Isolate* isolate,
187                               v8::Local<v8::RegExp> value) {
188   String16Builder description;
189   description.append('/');
190   description.append(toProtocolString(isolate, value->GetSource()));
191   description.append('/');
192   v8::RegExp::Flags flags = value->GetFlags();
193   if (flags & v8::RegExp::Flags::kGlobal) description.append('g');
194   if (flags & v8::RegExp::Flags::kIgnoreCase) description.append('i');
195   if (flags & v8::RegExp::Flags::kMultiline) description.append('m');
196   if (flags & v8::RegExp::Flags::kDotAll) description.append('s');
197   if (flags & v8::RegExp::Flags::kUnicode) description.append('u');
198   if (flags & v8::RegExp::Flags::kSticky) description.append('y');
199   return description.toString();
200 }
201 
202 enum class ErrorType { kNative, kClient };
203 
204 // Build a description from an exception using the following rules:
205 //   * Usually return the stack trace found in the {stack} property.
206 //   * If the stack trace does not start with the class name of the passed
207 //     exception, try to build a description from the class name, the
208 //     {message} property and the rest of the stack trace.
209 //     (The stack trace is only used if {message} was also found in
210 //     said stack trace).
descriptionForError(v8::Local<v8::Context> context,v8::Local<v8::Object> object,ErrorType type)211 String16 descriptionForError(v8::Local<v8::Context> context,
212                              v8::Local<v8::Object> object, ErrorType type) {
213   v8::Isolate* isolate = context->GetIsolate();
214   v8::TryCatch tryCatch(isolate);
215   String16 className = toProtocolString(isolate, object->GetConstructorName());
216 
217   v8::base::Optional<String16> stack;
218   {
219     v8::Local<v8::Value> stackValue;
220     if (object->Get(context, toV8String(isolate, "stack"))
221             .ToLocal(&stackValue) &&
222         stackValue->IsString()) {
223       stack = toProtocolString(isolate, stackValue.As<v8::String>());
224     }
225   }
226 
227   if (type == ErrorType::kNative && stack) return *stack;
228 
229   if (stack && stack->substring(0, className.length()) == className) {
230     return *stack;
231   }
232 
233   v8::base::Optional<String16> message;
234   {
235     v8::Local<v8::Value> messageValue;
236     if (object->Get(context, toV8String(isolate, "message"))
237             .ToLocal(&messageValue) &&
238         messageValue->IsString()) {
239       String16 msg = toProtocolStringWithTypeCheck(isolate, messageValue);
240       if (!msg.isEmpty()) message = msg;
241     }
242   }
243 
244   if (!message) return stack ? *stack : className;
245 
246   String16 description = className + ": " + *message;
247   if (!stack) return description;
248 
249   DCHECK(stack && message);
250   size_t index = stack->find(*message);
251   String16 stackWithoutMessage =
252       index != String16::kNotFound ? stack->substring(index + message->length())
253                                    : String16();
254   return description + stackWithoutMessage;
255 }
256 
descriptionForObject(v8::Isolate * isolate,v8::Local<v8::Object> object)257 String16 descriptionForObject(v8::Isolate* isolate,
258                               v8::Local<v8::Object> object) {
259   return toProtocolString(isolate, object->GetConstructorName());
260 }
261 
descriptionForDate(v8::Local<v8::Context> context,v8::Local<v8::Date> date)262 String16 descriptionForDate(v8::Local<v8::Context> context,
263                             v8::Local<v8::Date> date) {
264   v8::Isolate* isolate = context->GetIsolate();
265   v8::TryCatch tryCatch(isolate);
266   v8::Local<v8::String> description;
267   if (!date->ToString(context).ToLocal(&description)) {
268     return descriptionForObject(isolate, date);
269   }
270   return toProtocolString(isolate, description);
271 }
272 
descriptionForScopeList(v8::Local<v8::Array> list)273 String16 descriptionForScopeList(v8::Local<v8::Array> list) {
274   return String16::concat(
275       "Scopes[", String16::fromInteger(static_cast<size_t>(list->Length())),
276       ']');
277 }
278 
descriptionForScope(v8::Local<v8::Context> context,v8::Local<v8::Object> object)279 String16 descriptionForScope(v8::Local<v8::Context> context,
280                              v8::Local<v8::Object> object) {
281   v8::Isolate* isolate = context->GetIsolate();
282   v8::Local<v8::Value> value;
283   if (!object->GetRealNamedProperty(context, toV8String(isolate, "description"))
284            .ToLocal(&value)) {
285     return String16();
286   }
287   return toProtocolStringWithTypeCheck(isolate, value);
288 }
289 
descriptionForCollection(v8::Isolate * isolate,v8::Local<v8::Object> object,size_t length)290 String16 descriptionForCollection(v8::Isolate* isolate,
291                                   v8::Local<v8::Object> object, size_t length) {
292   String16 className = toProtocolString(isolate, object->GetConstructorName());
293   return String16::concat(className, '(', String16::fromInteger(length), ')');
294 }
295 
descriptionForEntry(v8::Local<v8::Context> context,v8::Local<v8::Object> object)296 String16 descriptionForEntry(v8::Local<v8::Context> context,
297                              v8::Local<v8::Object> object) {
298   v8::Isolate* isolate = context->GetIsolate();
299   String16 key;
300   v8::Local<v8::Value> tmp;
301   if (object->GetRealNamedProperty(context, toV8String(isolate, "key"))
302           .ToLocal(&tmp)) {
303     auto wrapper = ValueMirror::create(context, tmp);
304     if (wrapper) {
305       std::unique_ptr<ObjectPreview> preview;
306       int limit = 5;
307       wrapper->buildEntryPreview(context, &limit, &limit, &preview);
308       if (preview) {
309         key = preview->getDescription(String16());
310         if (preview->getType() == RemoteObject::TypeEnum::String) {
311           key = String16::concat('\"', key, '\"');
312         }
313       }
314     }
315   }
316 
317   String16 value;
318   if (object->GetRealNamedProperty(context, toV8String(isolate, "value"))
319           .ToLocal(&tmp)) {
320     auto wrapper = ValueMirror::create(context, tmp);
321     if (wrapper) {
322       std::unique_ptr<ObjectPreview> preview;
323       int limit = 5;
324       wrapper->buildEntryPreview(context, &limit, &limit, &preview);
325       if (preview) {
326         value = preview->getDescription(String16());
327         if (preview->getType() == RemoteObject::TypeEnum::String) {
328           value = String16::concat('\"', value, '\"');
329         }
330       }
331     }
332   }
333 
334   return key.length() ? ("{" + key + " => " + value + "}") : value;
335 }
336 
descriptionForFunction(v8::Local<v8::Context> context,v8::Local<v8::Function> value)337 String16 descriptionForFunction(v8::Local<v8::Context> context,
338                                 v8::Local<v8::Function> value) {
339   v8::Isolate* isolate = context->GetIsolate();
340   v8::TryCatch tryCatch(isolate);
341   v8::Local<v8::String> description;
342   if (!value->ToString(context).ToLocal(&description)) {
343     return descriptionForObject(isolate, value);
344   }
345   return toProtocolString(isolate, description);
346 }
347 
348 class PrimitiveValueMirror final : public ValueMirror {
349  public:
PrimitiveValueMirror(v8::Local<v8::Value> value,const String16 & type)350   PrimitiveValueMirror(v8::Local<v8::Value> value, const String16& type)
351       : m_value(value), m_type(type) {}
352 
v8Value() const353   v8::Local<v8::Value> v8Value() const override { return m_value; }
buildRemoteObject(v8::Local<v8::Context> context,WrapMode mode,std::unique_ptr<RemoteObject> * result) const354   Response buildRemoteObject(
355       v8::Local<v8::Context> context, WrapMode mode,
356       std::unique_ptr<RemoteObject>* result) const override {
357     std::unique_ptr<protocol::Value> protocolValue;
358     toProtocolValue(context, m_value, &protocolValue);
359     *result = RemoteObject::create()
360                   .setType(m_type)
361                   .setValue(std::move(protocolValue))
362                   .build();
363     if (m_value->IsNull())
364       (*result)->setSubtype(RemoteObject::SubtypeEnum::Null);
365     return Response::Success();
366   }
367 
buildEntryPreview(v8::Local<v8::Context> context,int * nameLimit,int * indexLimit,std::unique_ptr<ObjectPreview> * preview) const368   void buildEntryPreview(
369       v8::Local<v8::Context> context, int* nameLimit, int* indexLimit,
370       std::unique_ptr<ObjectPreview>* preview) const override {
371     *preview =
372         ObjectPreview::create()
373             .setType(m_type)
374             .setDescription(descriptionForPrimitiveType(context, m_value))
375             .setOverflow(false)
376             .setProperties(std::make_unique<protocol::Array<PropertyPreview>>())
377             .build();
378     if (m_value->IsNull())
379       (*preview)->setSubtype(RemoteObject::SubtypeEnum::Null);
380   }
381 
buildPropertyPreview(v8::Local<v8::Context> context,const String16 & name,std::unique_ptr<PropertyPreview> * preview) const382   void buildPropertyPreview(
383       v8::Local<v8::Context> context, const String16& name,
384       std::unique_ptr<PropertyPreview>* preview) const override {
385     *preview = PropertyPreview::create()
386                    .setName(name)
387                    .setValue(abbreviateString(
388                        descriptionForPrimitiveType(context, m_value), kMiddle))
389                    .setType(m_type)
390                    .build();
391     if (m_value->IsNull())
392       (*preview)->setSubtype(RemoteObject::SubtypeEnum::Null);
393   }
394 
395  private:
396   v8::Local<v8::Value> m_value;
397   String16 m_type;
398   String16 m_subtype;
399 };
400 
401 class NumberMirror final : public ValueMirror {
402  public:
NumberMirror(v8::Local<v8::Number> value)403   explicit NumberMirror(v8::Local<v8::Number> value) : m_value(value) {}
v8Value() const404   v8::Local<v8::Value> v8Value() const override { return m_value; }
405 
buildRemoteObject(v8::Local<v8::Context> context,WrapMode mode,std::unique_ptr<RemoteObject> * result) const406   Response buildRemoteObject(
407       v8::Local<v8::Context> context, WrapMode mode,
408       std::unique_ptr<RemoteObject>* result) const override {
409     bool unserializable = false;
410     String16 descriptionValue = description(&unserializable);
411     *result = RemoteObject::create()
412                   .setType(RemoteObject::TypeEnum::Number)
413                   .setDescription(descriptionValue)
414                   .build();
415     if (unserializable) {
416       (*result)->setUnserializableValue(descriptionValue);
417     } else {
418       (*result)->setValue(protocol::FundamentalValue::create(m_value->Value()));
419     }
420     return Response::Success();
421   }
buildPropertyPreview(v8::Local<v8::Context> context,const String16 & name,std::unique_ptr<PropertyPreview> * result) const422   void buildPropertyPreview(
423       v8::Local<v8::Context> context, const String16& name,
424       std::unique_ptr<PropertyPreview>* result) const override {
425     bool unserializable = false;
426     *result = PropertyPreview::create()
427                   .setName(name)
428                   .setType(RemoteObject::TypeEnum::Number)
429                   .setValue(description(&unserializable))
430                   .build();
431   }
buildEntryPreview(v8::Local<v8::Context> context,int * nameLimit,int * indexLimit,std::unique_ptr<ObjectPreview> * preview) const432   void buildEntryPreview(
433       v8::Local<v8::Context> context, int* nameLimit, int* indexLimit,
434       std::unique_ptr<ObjectPreview>* preview) const override {
435     bool unserializable = false;
436     *preview =
437         ObjectPreview::create()
438             .setType(RemoteObject::TypeEnum::Number)
439             .setDescription(description(&unserializable))
440             .setOverflow(false)
441             .setProperties(std::make_unique<protocol::Array<PropertyPreview>>())
442             .build();
443   }
444 
445  private:
description(bool * unserializable) const446   String16 description(bool* unserializable) const {
447     *unserializable = true;
448     double rawValue = m_value->Value();
449     if (std::isnan(rawValue)) return "NaN";
450     if (rawValue == 0.0 && std::signbit(rawValue)) return "-0";
451     if (std::isinf(rawValue)) {
452       return std::signbit(rawValue) ? "-Infinity" : "Infinity";
453     }
454     *unserializable = false;
455     return String16::fromDouble(rawValue);
456   }
457 
458   v8::Local<v8::Number> m_value;
459 };
460 
461 class BigIntMirror final : public ValueMirror {
462  public:
BigIntMirror(v8::Local<v8::BigInt> value)463   explicit BigIntMirror(v8::Local<v8::BigInt> value) : m_value(value) {}
464 
buildRemoteObject(v8::Local<v8::Context> context,WrapMode mode,std::unique_ptr<RemoteObject> * result) const465   Response buildRemoteObject(
466       v8::Local<v8::Context> context, WrapMode mode,
467       std::unique_ptr<RemoteObject>* result) const override {
468     String16 description = descriptionForBigInt(context, m_value);
469     *result = RemoteObject::create()
470                   .setType(RemoteObject::TypeEnum::Bigint)
471                   .setUnserializableValue(description)
472                   .setDescription(description)
473                   .build();
474     return Response::Success();
475   }
476 
buildPropertyPreview(v8::Local<v8::Context> context,const String16 & name,std::unique_ptr<protocol::Runtime::PropertyPreview> * preview) const477   void buildPropertyPreview(v8::Local<v8::Context> context,
478                             const String16& name,
479                             std::unique_ptr<protocol::Runtime::PropertyPreview>*
480                                 preview) const override {
481     *preview = PropertyPreview::create()
482                    .setName(name)
483                    .setType(RemoteObject::TypeEnum::Bigint)
484                    .setValue(abbreviateString(
485                        descriptionForBigInt(context, m_value), kMiddle))
486                    .build();
487   }
488 
buildEntryPreview(v8::Local<v8::Context> context,int * nameLimit,int * indexLimit,std::unique_ptr<protocol::Runtime::ObjectPreview> * preview) const489   void buildEntryPreview(v8::Local<v8::Context> context, int* nameLimit,
490                          int* indexLimit,
491                          std::unique_ptr<protocol::Runtime::ObjectPreview>*
492                              preview) const override {
493     *preview =
494         ObjectPreview::create()
495             .setType(RemoteObject::TypeEnum::Bigint)
496             .setDescription(descriptionForBigInt(context, m_value))
497             .setOverflow(false)
498             .setProperties(std::make_unique<protocol::Array<PropertyPreview>>())
499             .build();
500   }
501 
v8Value() const502   v8::Local<v8::Value> v8Value() const override { return m_value; }
503 
504  private:
505   v8::Local<v8::BigInt> m_value;
506 };
507 
508 class SymbolMirror final : public ValueMirror {
509  public:
SymbolMirror(v8::Local<v8::Value> value)510   explicit SymbolMirror(v8::Local<v8::Value> value)
511       : m_symbol(value.As<v8::Symbol>()) {}
512 
buildRemoteObject(v8::Local<v8::Context> context,WrapMode mode,std::unique_ptr<RemoteObject> * result) const513   Response buildRemoteObject(
514       v8::Local<v8::Context> context, WrapMode mode,
515       std::unique_ptr<RemoteObject>* result) const override {
516     if (mode == WrapMode::kForceValue) {
517       return Response::ServerError("Object couldn't be returned by value");
518     }
519     *result = RemoteObject::create()
520                   .setType(RemoteObject::TypeEnum::Symbol)
521                   .setDescription(descriptionForSymbol(context, m_symbol))
522                   .build();
523     return Response::Success();
524   }
525 
buildPropertyPreview(v8::Local<v8::Context> context,const String16 & name,std::unique_ptr<protocol::Runtime::PropertyPreview> * preview) const526   void buildPropertyPreview(v8::Local<v8::Context> context,
527                             const String16& name,
528                             std::unique_ptr<protocol::Runtime::PropertyPreview>*
529                                 preview) const override {
530     *preview = PropertyPreview::create()
531                    .setName(name)
532                    .setType(RemoteObject::TypeEnum::Symbol)
533                    .setValue(abbreviateString(
534                        descriptionForSymbol(context, m_symbol), kEnd))
535                    .build();
536   }
537 
v8Value() const538   v8::Local<v8::Value> v8Value() const override { return m_symbol; }
539 
540  private:
541   v8::Local<v8::Symbol> m_symbol;
542 };
543 
544 class LocationMirror final : public ValueMirror {
545  public:
create(v8::Local<v8::Function> function)546   static std::unique_ptr<LocationMirror> create(
547       v8::Local<v8::Function> function) {
548     return create(function, function->ScriptId(),
549                   function->GetScriptLineNumber(),
550                   function->GetScriptColumnNumber());
551   }
createForGenerator(v8::Local<v8::Value> value)552   static std::unique_ptr<LocationMirror> createForGenerator(
553       v8::Local<v8::Value> value) {
554     v8::Local<v8::debug::GeneratorObject> generatorObject =
555         v8::debug::GeneratorObject::Cast(value);
556     if (!generatorObject->IsSuspended()) {
557       return create(generatorObject->Function());
558     }
559     v8::Local<v8::debug::Script> script;
560     if (!generatorObject->Script().ToLocal(&script)) return nullptr;
561     v8::debug::Location suspendedLocation =
562         generatorObject->SuspendedLocation();
563     return create(value, script->Id(), suspendedLocation.GetLineNumber(),
564                   suspendedLocation.GetColumnNumber());
565   }
566 
buildRemoteObject(v8::Local<v8::Context> context,WrapMode mode,std::unique_ptr<RemoteObject> * result) const567   Response buildRemoteObject(
568       v8::Local<v8::Context> context, WrapMode mode,
569       std::unique_ptr<RemoteObject>* result) const override {
570     auto location = protocol::DictionaryValue::create();
571     location->setString("scriptId", String16::fromInteger(m_scriptId));
572     location->setInteger("lineNumber", m_lineNumber);
573     location->setInteger("columnNumber", m_columnNumber);
574     *result = RemoteObject::create()
575                   .setType(RemoteObject::TypeEnum::Object)
576                   .setSubtype("internal#location")
577                   .setDescription("Object")
578                   .setValue(std::move(location))
579                   .build();
580     return Response::Success();
581   }
v8Value() const582   v8::Local<v8::Value> v8Value() const override { return m_value; }
583 
584  private:
create(v8::Local<v8::Value> value,int scriptId,int lineNumber,int columnNumber)585   static std::unique_ptr<LocationMirror> create(v8::Local<v8::Value> value,
586                                                 int scriptId, int lineNumber,
587                                                 int columnNumber) {
588     if (scriptId == v8::UnboundScript::kNoScriptId) return nullptr;
589     if (lineNumber == v8::Function::kLineOffsetNotFound ||
590         columnNumber == v8::Function::kLineOffsetNotFound) {
591       return nullptr;
592     }
593     return std::unique_ptr<LocationMirror>(
594         new LocationMirror(value, scriptId, lineNumber, columnNumber));
595   }
596 
LocationMirror(v8::Local<v8::Value> value,int scriptId,int lineNumber,int columnNumber)597   LocationMirror(v8::Local<v8::Value> value, int scriptId, int lineNumber,
598                  int columnNumber)
599       : m_value(value),
600         m_scriptId(scriptId),
601         m_lineNumber(lineNumber),
602         m_columnNumber(columnNumber) {}
603 
604   v8::Local<v8::Value> m_value;
605   int m_scriptId;
606   int m_lineNumber;
607   int m_columnNumber;
608 };
609 
610 class FunctionMirror final : public ValueMirror {
611  public:
FunctionMirror(v8::Local<v8::Value> value)612   explicit FunctionMirror(v8::Local<v8::Value> value)
613       : m_value(value.As<v8::Function>()) {}
614 
v8Value() const615   v8::Local<v8::Value> v8Value() const override { return m_value; }
616 
buildRemoteObject(v8::Local<v8::Context> context,WrapMode mode,std::unique_ptr<RemoteObject> * result) const617   Response buildRemoteObject(
618       v8::Local<v8::Context> context, WrapMode mode,
619       std::unique_ptr<RemoteObject>* result) const override {
620     // TODO(alph): drop this functionality.
621     if (mode == WrapMode::kForceValue) {
622       std::unique_ptr<protocol::Value> protocolValue;
623       Response response = toProtocolValue(context, m_value, &protocolValue);
624       if (!response.IsSuccess()) return response;
625       *result = RemoteObject::create()
626                     .setType(RemoteObject::TypeEnum::Function)
627                     .setValue(std::move(protocolValue))
628                     .build();
629     } else {
630       *result = RemoteObject::create()
631                     .setType(RemoteObject::TypeEnum::Function)
632                     .setClassName(toProtocolStringWithTypeCheck(
633                         context->GetIsolate(), m_value->GetConstructorName()))
634                     .setDescription(descriptionForFunction(context, m_value))
635                     .build();
636     }
637     return Response::Success();
638   }
639 
buildPropertyPreview(v8::Local<v8::Context> context,const String16 & name,std::unique_ptr<PropertyPreview> * result) const640   void buildPropertyPreview(
641       v8::Local<v8::Context> context, const String16& name,
642       std::unique_ptr<PropertyPreview>* result) const override {
643     *result = PropertyPreview::create()
644                   .setName(name)
645                   .setType(RemoteObject::TypeEnum::Function)
646                   .setValue(String16())
647                   .build();
648   }
buildEntryPreview(v8::Local<v8::Context> context,int * nameLimit,int * indexLimit,std::unique_ptr<ObjectPreview> * preview) const649   void buildEntryPreview(
650       v8::Local<v8::Context> context, int* nameLimit, int* indexLimit,
651       std::unique_ptr<ObjectPreview>* preview) const override {
652     *preview =
653         ObjectPreview::create()
654             .setType(RemoteObject::TypeEnum::Function)
655             .setDescription(descriptionForFunction(context, m_value))
656             .setOverflow(false)
657             .setProperties(std::make_unique<protocol::Array<PropertyPreview>>())
658             .build();
659   }
660 
661  private:
662   v8::Local<v8::Function> m_value;
663 };
664 
isArrayLike(v8::Local<v8::Context> context,v8::Local<v8::Value> value,size_t * length)665 bool isArrayLike(v8::Local<v8::Context> context, v8::Local<v8::Value> value,
666                  size_t* length) {
667   if (!value->IsObject()) return false;
668   v8::Isolate* isolate = context->GetIsolate();
669   v8::TryCatch tryCatch(isolate);
670   v8::MicrotasksScope microtasksScope(isolate,
671                                       v8::MicrotasksScope::kDoNotRunMicrotasks);
672   v8::Local<v8::Object> object = value.As<v8::Object>();
673   v8::Local<v8::Value> spliceValue;
674   if (!object->IsArgumentsObject() &&
675       (!object->GetRealNamedProperty(context, toV8String(isolate, "splice"))
676             .ToLocal(&spliceValue) ||
677        !spliceValue->IsFunction())) {
678     return false;
679   }
680   v8::Local<v8::Value> lengthValue;
681   v8::Maybe<bool> result =
682       object->HasOwnProperty(context, toV8String(isolate, "length"));
683   if (result.IsNothing()) return false;
684   if (!result.FromJust() ||
685       !object->Get(context, toV8String(isolate, "length"))
686            .ToLocal(&lengthValue) ||
687       !lengthValue->IsUint32()) {
688     return false;
689   }
690   *length = v8::Local<v8::Uint32>::Cast(lengthValue)->Value();
691   return true;
692 }
693 
694 struct EntryMirror {
695   std::unique_ptr<ValueMirror> key;
696   std::unique_ptr<ValueMirror> value;
697 
getEntriesv8_inspector::__anonab7d21130111::EntryMirror698   static bool getEntries(v8::Local<v8::Context> context,
699                          v8::Local<v8::Object> object, size_t limit,
700                          bool* overflow, std::vector<EntryMirror>* mirrors) {
701     bool isKeyValue = false;
702     v8::Local<v8::Array> entries;
703     if (!object->PreviewEntries(&isKeyValue).ToLocal(&entries)) return false;
704     for (uint32_t i = 0; i < entries->Length(); i += isKeyValue ? 2 : 1) {
705       v8::Local<v8::Value> tmp;
706 
707       std::unique_ptr<ValueMirror> keyMirror;
708       if (isKeyValue && entries->Get(context, i).ToLocal(&tmp)) {
709         keyMirror = ValueMirror::create(context, tmp);
710       }
711       std::unique_ptr<ValueMirror> valueMirror;
712       if (entries->Get(context, isKeyValue ? i + 1 : i).ToLocal(&tmp)) {
713         valueMirror = ValueMirror::create(context, tmp);
714       } else {
715         continue;
716       }
717       if (mirrors->size() == limit) {
718         *overflow = true;
719         return true;
720       }
721       mirrors->emplace_back(
722           EntryMirror{std::move(keyMirror), std::move(valueMirror)});
723     }
724     return mirrors->size() > 0;
725   }
726 };
727 
728 class PreviewPropertyAccumulator : public ValueMirror::PropertyAccumulator {
729  public:
PreviewPropertyAccumulator(const std::vector<String16> & blacklist,int skipIndex,int * nameLimit,int * indexLimit,bool * overflow,std::vector<PropertyMirror> * mirrors)730   PreviewPropertyAccumulator(const std::vector<String16>& blacklist,
731                              int skipIndex, int* nameLimit, int* indexLimit,
732                              bool* overflow,
733                              std::vector<PropertyMirror>* mirrors)
734       : m_blacklist(blacklist),
735         m_skipIndex(skipIndex),
736         m_nameLimit(nameLimit),
737         m_indexLimit(indexLimit),
738         m_overflow(overflow),
739         m_mirrors(mirrors) {}
740 
Add(PropertyMirror mirror)741   bool Add(PropertyMirror mirror) override {
742     if (mirror.exception) return true;
743     if ((!mirror.getter || !mirror.getter->v8Value()->IsFunction()) &&
744         !mirror.value) {
745       return true;
746     }
747     if (!mirror.isOwn) return true;
748     if (std::find(m_blacklist.begin(), m_blacklist.end(), mirror.name) !=
749         m_blacklist.end()) {
750       return true;
751     }
752     if (mirror.isIndex && m_skipIndex > 0) {
753       --m_skipIndex;
754       if (m_skipIndex > 0) return true;
755     }
756     int* limit = mirror.isIndex ? m_indexLimit : m_nameLimit;
757     if (!*limit) {
758       *m_overflow = true;
759       return false;
760     }
761     --*limit;
762     m_mirrors->push_back(std::move(mirror));
763     return true;
764   }
765 
766  private:
767   std::vector<String16> m_blacklist;
768   int m_skipIndex;
769   int* m_nameLimit;
770   int* m_indexLimit;
771   bool* m_overflow;
772   std::vector<PropertyMirror>* m_mirrors;
773 };
774 
getPropertiesForPreview(v8::Local<v8::Context> context,v8::Local<v8::Object> object,int * nameLimit,int * indexLimit,bool * overflow,std::vector<PropertyMirror> * properties)775 bool getPropertiesForPreview(v8::Local<v8::Context> context,
776                              v8::Local<v8::Object> object, int* nameLimit,
777                              int* indexLimit, bool* overflow,
778                              std::vector<PropertyMirror>* properties) {
779   std::vector<String16> blacklist;
780   size_t length = 0;
781   if (object->IsArray() || isArrayLike(context, object, &length) ||
782       object->IsStringObject()) {
783     blacklist.push_back("length");
784   } else {
785     auto clientSubtype = clientFor(context)->valueSubtype(object);
786     if (clientSubtype && toString16(clientSubtype->string()) == "array") {
787       blacklist.push_back("length");
788     }
789   }
790   if (object->IsArrayBuffer() || object->IsSharedArrayBuffer()) {
791     blacklist.push_back("[[Int8Array]]");
792     blacklist.push_back("[[Uint8Array]]");
793     blacklist.push_back("[[Int16Array]]");
794     blacklist.push_back("[[Int32Array]]");
795   }
796   int skipIndex = object->IsStringObject()
797                       ? object.As<v8::StringObject>()->ValueOf()->Length() + 1
798                       : -1;
799   PreviewPropertyAccumulator accumulator(blacklist, skipIndex, nameLimit,
800                                          indexLimit, overflow, properties);
801   return ValueMirror::getProperties(context, object, false, false,
802                                     &accumulator);
803 }
804 
getInternalPropertiesForPreview(v8::Local<v8::Context> context,v8::Local<v8::Object> object,int * nameLimit,bool * overflow,std::vector<InternalPropertyMirror> * properties)805 void getInternalPropertiesForPreview(
806     v8::Local<v8::Context> context, v8::Local<v8::Object> object,
807     int* nameLimit, bool* overflow,
808     std::vector<InternalPropertyMirror>* properties) {
809   std::vector<InternalPropertyMirror> mirrors;
810   ValueMirror::getInternalProperties(context, object, &mirrors);
811   std::vector<String16> whitelist;
812   if (object->IsBooleanObject() || object->IsNumberObject() ||
813       object->IsStringObject() || object->IsSymbolObject() ||
814       object->IsBigIntObject()) {
815     whitelist.emplace_back("[[PrimitiveValue]]");
816   } else if (object->IsPromise()) {
817     whitelist.emplace_back("[[PromiseStatus]]");
818     whitelist.emplace_back("[[PromiseValue]]");
819   } else if (object->IsGeneratorObject()) {
820     whitelist.emplace_back("[[GeneratorStatus]]");
821   }
822   for (auto& mirror : mirrors) {
823     if (std::find(whitelist.begin(), whitelist.end(), mirror.name) ==
824         whitelist.end()) {
825       continue;
826     }
827     if (!*nameLimit) {
828       *overflow = true;
829       return;
830     }
831     --*nameLimit;
832     properties->push_back(std::move(mirror));
833   }
834 }
835 
getPrivatePropertiesForPreview(v8::Local<v8::Context> context,v8::Local<v8::Object> object,int * nameLimit,bool * overflow,protocol::Array<PropertyPreview> * privateProperties)836 void getPrivatePropertiesForPreview(
837     v8::Local<v8::Context> context, v8::Local<v8::Object> object,
838     int* nameLimit, bool* overflow,
839     protocol::Array<PropertyPreview>* privateProperties) {
840   std::vector<PrivatePropertyMirror> mirrors =
841       ValueMirror::getPrivateProperties(context, object);
842   std::vector<String16> whitelist;
843   for (auto& mirror : mirrors) {
844     std::unique_ptr<PropertyPreview> propertyPreview;
845     if (mirror.value) {
846       mirror.value->buildPropertyPreview(context, mirror.name,
847                                          &propertyPreview);
848     } else {
849       propertyPreview = PropertyPreview::create()
850                             .setName(mirror.name)
851                             .setType(PropertyPreview::TypeEnum::Accessor)
852                             .build();
853     }
854     if (!propertyPreview) continue;
855     if (!*nameLimit) {
856       *overflow = true;
857       return;
858     }
859     --*nameLimit;
860     privateProperties->emplace_back(std::move(propertyPreview));
861   }
862 }
863 
864 class ObjectMirror final : public ValueMirror {
865  public:
ObjectMirror(v8::Local<v8::Value> value,const String16 & description)866   ObjectMirror(v8::Local<v8::Value> value, const String16& description)
867       : m_value(value.As<v8::Object>()),
868         m_description(description),
869         m_hasSubtype(false) {}
ObjectMirror(v8::Local<v8::Value> value,const String16 & subtype,const String16 & description)870   ObjectMirror(v8::Local<v8::Value> value, const String16& subtype,
871                const String16& description)
872       : m_value(value.As<v8::Object>()),
873         m_description(description),
874         m_hasSubtype(true),
875         m_subtype(subtype) {}
876 
v8Value() const877   v8::Local<v8::Value> v8Value() const override { return m_value; }
878 
buildRemoteObject(v8::Local<v8::Context> context,WrapMode mode,std::unique_ptr<RemoteObject> * result) const879   Response buildRemoteObject(
880       v8::Local<v8::Context> context, WrapMode mode,
881       std::unique_ptr<RemoteObject>* result) const override {
882     if (mode == WrapMode::kForceValue) {
883       std::unique_ptr<protocol::Value> protocolValue;
884       Response response = toProtocolValue(context, m_value, &protocolValue);
885       if (!response.IsSuccess()) return response;
886       *result = RemoteObject::create()
887                     .setType(RemoteObject::TypeEnum::Object)
888                     .setValue(std::move(protocolValue))
889                     .build();
890     } else {
891       v8::Isolate* isolate = context->GetIsolate();
892       *result = RemoteObject::create()
893                     .setType(RemoteObject::TypeEnum::Object)
894                     .setClassName(toProtocolString(
895                         isolate, m_value->GetConstructorName()))
896                     .setDescription(m_description)
897                     .build();
898       if (m_hasSubtype) (*result)->setSubtype(m_subtype);
899       if (mode == WrapMode::kWithPreview) {
900         std::unique_ptr<ObjectPreview> previewValue;
901         int nameLimit = 5;
902         int indexLimit = 100;
903         buildObjectPreview(context, false, &nameLimit, &indexLimit,
904                            &previewValue);
905         (*result)->setPreview(std::move(previewValue));
906       }
907     }
908     return Response::Success();
909   }
910 
buildObjectPreview(v8::Local<v8::Context> context,bool generatePreviewForTable,int * nameLimit,int * indexLimit,std::unique_ptr<ObjectPreview> * result) const911   void buildObjectPreview(
912       v8::Local<v8::Context> context, bool generatePreviewForTable,
913       int* nameLimit, int* indexLimit,
914       std::unique_ptr<ObjectPreview>* result) const override {
915     buildObjectPreviewInternal(context, false /* forEntry */,
916                                generatePreviewForTable, nameLimit, indexLimit,
917                                result);
918   }
919 
buildEntryPreview(v8::Local<v8::Context> context,int * nameLimit,int * indexLimit,std::unique_ptr<ObjectPreview> * result) const920   void buildEntryPreview(
921       v8::Local<v8::Context> context, int* nameLimit, int* indexLimit,
922       std::unique_ptr<ObjectPreview>* result) const override {
923     buildObjectPreviewInternal(context, true /* forEntry */,
924                                false /* generatePreviewForTable */, nameLimit,
925                                indexLimit, result);
926   }
927 
buildPropertyPreview(v8::Local<v8::Context> context,const String16 & name,std::unique_ptr<PropertyPreview> * result) const928   void buildPropertyPreview(
929       v8::Local<v8::Context> context, const String16& name,
930       std::unique_ptr<PropertyPreview>* result) const override {
931     *result = PropertyPreview::create()
932                   .setName(name)
933                   .setType(RemoteObject::TypeEnum::Object)
934                   .setValue(abbreviateString(
935                       m_description,
936                       m_subtype == RemoteObject::SubtypeEnum::Regexp ? kMiddle
937                                                                      : kEnd))
938                   .build();
939     if (m_hasSubtype) (*result)->setSubtype(m_subtype);
940   }
941 
942  private:
buildObjectPreviewInternal(v8::Local<v8::Context> context,bool forEntry,bool generatePreviewForTable,int * nameLimit,int * indexLimit,std::unique_ptr<ObjectPreview> * result) const943   void buildObjectPreviewInternal(
944       v8::Local<v8::Context> context, bool forEntry,
945       bool generatePreviewForTable, int* nameLimit, int* indexLimit,
946       std::unique_ptr<ObjectPreview>* result) const {
947     auto properties = std::make_unique<protocol::Array<PropertyPreview>>();
948     std::unique_ptr<protocol::Array<EntryPreview>> entriesPreview;
949     bool overflow = false;
950 
951     v8::Local<v8::Value> value = m_value;
952     while (value->IsProxy()) value = value.As<v8::Proxy>()->GetTarget();
953 
954     if (value->IsObject() && !value->IsProxy()) {
955       v8::Local<v8::Object> objectForPreview = value.As<v8::Object>();
956       std::vector<InternalPropertyMirror> internalProperties;
957       getInternalPropertiesForPreview(context, objectForPreview, nameLimit,
958                                       &overflow, &internalProperties);
959       for (size_t i = 0; i < internalProperties.size(); ++i) {
960         std::unique_ptr<PropertyPreview> propertyPreview;
961         internalProperties[i].value->buildPropertyPreview(
962             context, internalProperties[i].name, &propertyPreview);
963         if (propertyPreview) {
964           properties->emplace_back(std::move(propertyPreview));
965         }
966       }
967 
968       getPrivatePropertiesForPreview(context, objectForPreview, nameLimit,
969                                      &overflow, properties.get());
970 
971       std::vector<PropertyMirror> mirrors;
972       if (getPropertiesForPreview(context, objectForPreview, nameLimit,
973                                   indexLimit, &overflow, &mirrors)) {
974         for (size_t i = 0; i < mirrors.size(); ++i) {
975           std::unique_ptr<PropertyPreview> preview;
976           std::unique_ptr<ObjectPreview> valuePreview;
977           if (mirrors[i].value) {
978             mirrors[i].value->buildPropertyPreview(context, mirrors[i].name,
979                                                    &preview);
980             if (generatePreviewForTable) {
981               int tableLimit = 1000;
982               mirrors[i].value->buildObjectPreview(context, false, &tableLimit,
983                                                    &tableLimit, &valuePreview);
984             }
985           } else {
986             preview = PropertyPreview::create()
987                           .setName(mirrors[i].name)
988                           .setType(PropertyPreview::TypeEnum::Accessor)
989                           .build();
990           }
991           if (valuePreview) {
992             preview->setValuePreview(std::move(valuePreview));
993           }
994           properties->emplace_back(std::move(preview));
995         }
996       }
997 
998       std::vector<EntryMirror> entries;
999       if (EntryMirror::getEntries(context, objectForPreview, 5, &overflow,
1000                                   &entries)) {
1001         if (forEntry) {
1002           overflow = true;
1003         } else {
1004           entriesPreview = std::make_unique<protocol::Array<EntryPreview>>();
1005           for (const auto& entry : entries) {
1006             std::unique_ptr<ObjectPreview> valuePreview;
1007             entry.value->buildEntryPreview(context, nameLimit, indexLimit,
1008                                            &valuePreview);
1009             if (!valuePreview) continue;
1010             std::unique_ptr<ObjectPreview> keyPreview;
1011             if (entry.key) {
1012               entry.key->buildEntryPreview(context, nameLimit, indexLimit,
1013                                            &keyPreview);
1014               if (!keyPreview) continue;
1015             }
1016             std::unique_ptr<EntryPreview> entryPreview =
1017                 EntryPreview::create()
1018                     .setValue(std::move(valuePreview))
1019                     .build();
1020             if (keyPreview) entryPreview->setKey(std::move(keyPreview));
1021             entriesPreview->emplace_back(std::move(entryPreview));
1022           }
1023         }
1024       }
1025     }
1026     *result = ObjectPreview::create()
1027                   .setType(RemoteObject::TypeEnum::Object)
1028                   .setDescription(m_description)
1029                   .setOverflow(overflow)
1030                   .setProperties(std::move(properties))
1031                   .build();
1032     if (m_hasSubtype) (*result)->setSubtype(m_subtype);
1033     if (entriesPreview) (*result)->setEntries(std::move(entriesPreview));
1034   }
1035 
1036   v8::Local<v8::Object> m_value;
1037   String16 m_description;
1038   bool m_hasSubtype;
1039   String16 m_subtype;
1040 };
1041 
nativeGetterCallback(const v8::FunctionCallbackInfo<v8::Value> & info)1042 void nativeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
1043   v8::Local<v8::Object> data = info.Data().As<v8::Object>();
1044   v8::Isolate* isolate = info.GetIsolate();
1045   v8::Local<v8::Context> context = isolate->GetCurrentContext();
1046   v8::Local<v8::Value> name;
1047   if (!data->GetRealNamedProperty(context, toV8String(isolate, "name"))
1048            .ToLocal(&name)) {
1049     return;
1050   }
1051   v8::Local<v8::Value> object;
1052   if (!data->GetRealNamedProperty(context, toV8String(isolate, "object"))
1053            .ToLocal(&object) ||
1054       !object->IsObject()) {
1055     return;
1056   }
1057   v8::Local<v8::Value> value;
1058   if (!object.As<v8::Object>()->Get(context, name).ToLocal(&value)) return;
1059   info.GetReturnValue().Set(value);
1060 }
1061 
createNativeGetter(v8::Local<v8::Context> context,v8::Local<v8::Value> object,v8::Local<v8::Name> name)1062 std::unique_ptr<ValueMirror> createNativeGetter(v8::Local<v8::Context> context,
1063                                                 v8::Local<v8::Value> object,
1064                                                 v8::Local<v8::Name> name) {
1065   v8::Isolate* isolate = context->GetIsolate();
1066   v8::TryCatch tryCatch(isolate);
1067 
1068   v8::Local<v8::Object> data = v8::Object::New(isolate);
1069   if (data->Set(context, toV8String(isolate, "name"), name).IsNothing()) {
1070     return nullptr;
1071   }
1072   if (data->Set(context, toV8String(isolate, "object"), object).IsNothing()) {
1073     return nullptr;
1074   }
1075 
1076   v8::Local<v8::Function> function;
1077   if (!v8::Function::New(context, nativeGetterCallback, data, 0,
1078                          v8::ConstructorBehavior::kThrow)
1079            .ToLocal(&function)) {
1080     return nullptr;
1081   }
1082   return ValueMirror::create(context, function);
1083 }
1084 
nativeSetterCallback(const v8::FunctionCallbackInfo<v8::Value> & info)1085 void nativeSetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
1086   if (info.Length() < 1) return;
1087   v8::Local<v8::Object> data = info.Data().As<v8::Object>();
1088   v8::Isolate* isolate = info.GetIsolate();
1089   v8::Local<v8::Context> context = isolate->GetCurrentContext();
1090   v8::Local<v8::Value> name;
1091   if (!data->GetRealNamedProperty(context, toV8String(isolate, "name"))
1092            .ToLocal(&name)) {
1093     return;
1094   }
1095   v8::Local<v8::Value> object;
1096   if (!data->GetRealNamedProperty(context, toV8String(isolate, "object"))
1097            .ToLocal(&object) ||
1098       !object->IsObject()) {
1099     return;
1100   }
1101   v8::Local<v8::Value> value;
1102   if (!object.As<v8::Object>()->Set(context, name, info[0]).IsNothing()) return;
1103 }
1104 
createNativeSetter(v8::Local<v8::Context> context,v8::Local<v8::Value> object,v8::Local<v8::Name> name)1105 std::unique_ptr<ValueMirror> createNativeSetter(v8::Local<v8::Context> context,
1106                                                 v8::Local<v8::Value> object,
1107                                                 v8::Local<v8::Name> name) {
1108   v8::Isolate* isolate = context->GetIsolate();
1109   v8::TryCatch tryCatch(isolate);
1110 
1111   v8::Local<v8::Object> data = v8::Object::New(isolate);
1112   if (data->Set(context, toV8String(isolate, "name"), name).IsNothing()) {
1113     return nullptr;
1114   }
1115   if (data->Set(context, toV8String(isolate, "object"), object).IsNothing()) {
1116     return nullptr;
1117   }
1118 
1119   v8::Local<v8::Function> function;
1120   if (!v8::Function::New(context, nativeSetterCallback, data, 1,
1121                          v8::ConstructorBehavior::kThrow)
1122            .ToLocal(&function)) {
1123     return nullptr;
1124   }
1125   return ValueMirror::create(context, function);
1126 }
1127 
doesAttributeHaveObservableSideEffectOnGet(v8::Local<v8::Context> context,v8::Local<v8::Object> object,v8::Local<v8::Name> name)1128 bool doesAttributeHaveObservableSideEffectOnGet(v8::Local<v8::Context> context,
1129                                                 v8::Local<v8::Object> object,
1130                                                 v8::Local<v8::Name> name) {
1131   // TODO(dgozman): we should remove this, annotate more embedder properties as
1132   // side-effect free, and call all getters which do not produce side effects.
1133   if (!name->IsString()) return false;
1134   v8::Isolate* isolate = context->GetIsolate();
1135   if (!name.As<v8::String>()->StringEquals(toV8String(isolate, "body"))) {
1136     return false;
1137   }
1138 
1139   v8::TryCatch tryCatch(isolate);
1140   v8::Local<v8::Value> request;
1141   if (context->Global()
1142           ->GetRealNamedProperty(context, toV8String(isolate, "Request"))
1143           .ToLocal(&request)) {
1144     if (request->IsObject() &&
1145         object->InstanceOf(context, request.As<v8::Object>())
1146             .FromMaybe(false)) {
1147       return true;
1148     }
1149   }
1150   if (tryCatch.HasCaught()) tryCatch.Reset();
1151 
1152   v8::Local<v8::Value> response;
1153   if (context->Global()
1154           ->GetRealNamedProperty(context, toV8String(isolate, "Response"))
1155           .ToLocal(&response)) {
1156     if (response->IsObject() &&
1157         object->InstanceOf(context, response.As<v8::Object>())
1158             .FromMaybe(false)) {
1159       return true;
1160     }
1161   }
1162   return false;
1163 }
1164 template <typename ArrayView, typename ArrayBuffer>
addTypedArrayView(v8::Local<v8::Context> context,v8::Local<ArrayBuffer> buffer,size_t length,const char * name,ValueMirror::PropertyAccumulator * accumulator)1165 void addTypedArrayView(v8::Local<v8::Context> context,
1166                        v8::Local<ArrayBuffer> buffer, size_t length,
1167                        const char* name,
1168                        ValueMirror::PropertyAccumulator* accumulator) {
1169   accumulator->Add(PropertyMirror{
1170       String16(name), false, false, false, true, false,
1171       ValueMirror::create(context, ArrayView::New(buffer, 0, length)), nullptr,
1172       nullptr, nullptr, nullptr});
1173 }
1174 
1175 template <typename ArrayBuffer>
addTypedArrayViews(v8::Local<v8::Context> context,v8::Local<ArrayBuffer> buffer,ValueMirror::PropertyAccumulator * accumulator)1176 void addTypedArrayViews(v8::Local<v8::Context> context,
1177                         v8::Local<ArrayBuffer> buffer,
1178                         ValueMirror::PropertyAccumulator* accumulator) {
1179   // TODO(alph): these should be internal properties.
1180   // TODO(v8:9308): Reconsider how large arrays are previewed.
1181   const size_t byte_length = buffer->ByteLength();
1182 
1183   size_t length = byte_length;
1184   if (length > v8::TypedArray::kMaxLength) return;
1185 
1186   addTypedArrayView<v8::Int8Array>(context, buffer, length, "[[Int8Array]]",
1187                                    accumulator);
1188   addTypedArrayView<v8::Uint8Array>(context, buffer, length, "[[Uint8Array]]",
1189                                     accumulator);
1190 
1191   length = byte_length / 2;
1192   if (length > v8::TypedArray::kMaxLength || (byte_length % 2) != 0) return;
1193 
1194   addTypedArrayView<v8::Int16Array>(context, buffer, length, "[[Int16Array]]",
1195                                     accumulator);
1196 
1197   length = byte_length / 4;
1198   if (length > v8::TypedArray::kMaxLength || (byte_length % 4) != 0) return;
1199 
1200   addTypedArrayView<v8::Int32Array>(context, buffer, length, "[[Int32Array]]",
1201                                     accumulator);
1202 }
1203 }  // anonymous namespace
1204 
1205 ValueMirror::~ValueMirror() = default;
1206 
1207 // static
getProperties(v8::Local<v8::Context> context,v8::Local<v8::Object> object,bool ownProperties,bool accessorPropertiesOnly,PropertyAccumulator * accumulator)1208 bool ValueMirror::getProperties(v8::Local<v8::Context> context,
1209                                 v8::Local<v8::Object> object,
1210                                 bool ownProperties, bool accessorPropertiesOnly,
1211                                 PropertyAccumulator* accumulator) {
1212   v8::Isolate* isolate = context->GetIsolate();
1213   v8::TryCatch tryCatch(isolate);
1214   v8::Local<v8::Set> set = v8::Set::New(isolate);
1215 
1216   v8::MicrotasksScope microtasksScope(isolate,
1217                                       v8::MicrotasksScope::kDoNotRunMicrotasks);
1218   V8InternalValueType internalType = v8InternalValueTypeFrom(context, object);
1219   if (internalType == V8InternalValueType::kScope) {
1220     v8::Local<v8::Value> value;
1221     if (!object->Get(context, toV8String(isolate, "object")).ToLocal(&value) ||
1222         !value->IsObject()) {
1223       return false;
1224     } else {
1225       object = value.As<v8::Object>();
1226     }
1227   }
1228   if (internalType == V8InternalValueType::kScopeList) {
1229     if (!set->Add(context, toV8String(isolate, "length")).ToLocal(&set)) {
1230       return false;
1231     }
1232   }
1233   bool shouldSkipProto = internalType == V8InternalValueType::kScopeList;
1234 
1235   bool formatAccessorsAsProperties =
1236       clientFor(context)->formatAccessorsAsProperties(object);
1237 
1238   if (object->IsArrayBuffer()) {
1239     addTypedArrayViews(context, object.As<v8::ArrayBuffer>(), accumulator);
1240   }
1241   if (object->IsSharedArrayBuffer()) {
1242     addTypedArrayViews(context, object.As<v8::SharedArrayBuffer>(),
1243                        accumulator);
1244   }
1245 
1246   for (auto iterator = v8::debug::PropertyIterator::Create(object);
1247        !iterator->Done(); iterator->Advance()) {
1248     bool isOwn = iterator->is_own();
1249     if (!isOwn && ownProperties) break;
1250     v8::Local<v8::Name> v8Name = iterator->name();
1251     v8::Maybe<bool> result = set->Has(context, v8Name);
1252     if (result.IsNothing()) return false;
1253     if (result.FromJust()) continue;
1254     if (!set->Add(context, v8Name).ToLocal(&set)) return false;
1255 
1256     String16 name;
1257     std::unique_ptr<ValueMirror> symbolMirror;
1258     if (v8Name->IsString()) {
1259       name = toProtocolString(isolate, v8Name.As<v8::String>());
1260     } else {
1261       v8::Local<v8::Symbol> symbol = v8Name.As<v8::Symbol>();
1262       name = descriptionForSymbol(context, symbol);
1263       symbolMirror = ValueMirror::create(context, symbol);
1264     }
1265 
1266     v8::PropertyAttribute attributes;
1267     std::unique_ptr<ValueMirror> valueMirror;
1268     std::unique_ptr<ValueMirror> getterMirror;
1269     std::unique_ptr<ValueMirror> setterMirror;
1270     std::unique_ptr<ValueMirror> exceptionMirror;
1271     bool writable = false;
1272     bool enumerable = false;
1273     bool configurable = false;
1274 
1275     bool isAccessorProperty = false;
1276     v8::TryCatch tryCatch(isolate);
1277     if (!iterator->attributes().To(&attributes)) {
1278       exceptionMirror = ValueMirror::create(context, tryCatch.Exception());
1279     } else {
1280       if (iterator->is_native_accessor()) {
1281         if (iterator->has_native_getter()) {
1282           getterMirror = createNativeGetter(context, object, v8Name);
1283         }
1284         if (iterator->has_native_setter()) {
1285           setterMirror = createNativeSetter(context, object, v8Name);
1286         }
1287         writable = !(attributes & v8::PropertyAttribute::ReadOnly);
1288         enumerable = !(attributes & v8::PropertyAttribute::DontEnum);
1289         configurable = !(attributes & v8::PropertyAttribute::DontDelete);
1290         isAccessorProperty = getterMirror || setterMirror;
1291       } else {
1292         v8::TryCatch tryCatch(isolate);
1293         v8::debug::PropertyDescriptor descriptor;
1294         if (!iterator->descriptor().To(&descriptor)) {
1295           exceptionMirror = ValueMirror::create(context, tryCatch.Exception());
1296         } else {
1297           writable = descriptor.has_writable ? descriptor.writable : false;
1298           enumerable =
1299               descriptor.has_enumerable ? descriptor.enumerable : false;
1300           configurable =
1301               descriptor.has_configurable ? descriptor.configurable : false;
1302           if (!descriptor.value.IsEmpty()) {
1303             valueMirror = ValueMirror::create(context, descriptor.value);
1304           }
1305           bool getterIsNativeFunction = false;
1306           if (!descriptor.get.IsEmpty()) {
1307             v8::Local<v8::Value> get = descriptor.get;
1308             getterMirror = ValueMirror::create(context, get);
1309             getterIsNativeFunction =
1310                 get->IsFunction() && get.As<v8::Function>()->ScriptId() ==
1311                                          v8::UnboundScript::kNoScriptId;
1312           }
1313           if (!descriptor.set.IsEmpty()) {
1314             setterMirror = ValueMirror::create(context, descriptor.set);
1315           }
1316           isAccessorProperty = getterMirror || setterMirror;
1317           bool isSymbolDescription =
1318               object->IsSymbol() && name == "description";
1319           if (isSymbolDescription ||
1320               (name != "__proto__" && getterIsNativeFunction &&
1321                formatAccessorsAsProperties &&
1322                !doesAttributeHaveObservableSideEffectOnGet(context, object,
1323                                                            v8Name))) {
1324             v8::TryCatch tryCatch(isolate);
1325             v8::Local<v8::Value> value;
1326             if (object->Get(context, v8Name).ToLocal(&value)) {
1327               valueMirror = ValueMirror::create(context, value);
1328               isOwn = true;
1329               setterMirror = nullptr;
1330               getterMirror = nullptr;
1331             }
1332           }
1333         }
1334       }
1335     }
1336     if (accessorPropertiesOnly && !isAccessorProperty) continue;
1337     auto mirror = PropertyMirror{name,
1338                                  writable,
1339                                  configurable,
1340                                  enumerable,
1341                                  isOwn,
1342                                  iterator->is_array_index(),
1343                                  std::move(valueMirror),
1344                                  std::move(getterMirror),
1345                                  std::move(setterMirror),
1346                                  std::move(symbolMirror),
1347                                  std::move(exceptionMirror)};
1348     if (!accumulator->Add(std::move(mirror))) return true;
1349   }
1350   if (!shouldSkipProto && ownProperties && !object->IsProxy() &&
1351       !accessorPropertiesOnly) {
1352     v8::Local<v8::Value> prototype = object->GetPrototype();
1353     if (prototype->IsObject()) {
1354       accumulator->Add(PropertyMirror{String16("__proto__"), true, true, false,
1355                                       true, false,
1356                                       ValueMirror::create(context, prototype),
1357                                       nullptr, nullptr, nullptr, nullptr});
1358     }
1359   }
1360   return true;
1361 }
1362 
1363 // static
getInternalProperties(v8::Local<v8::Context> context,v8::Local<v8::Object> object,std::vector<InternalPropertyMirror> * mirrors)1364 void ValueMirror::getInternalProperties(
1365     v8::Local<v8::Context> context, v8::Local<v8::Object> object,
1366     std::vector<InternalPropertyMirror>* mirrors) {
1367   v8::Isolate* isolate = context->GetIsolate();
1368   v8::MicrotasksScope microtasksScope(isolate,
1369                                       v8::MicrotasksScope::kDoNotRunMicrotasks);
1370   v8::TryCatch tryCatch(isolate);
1371   if (object->IsFunction()) {
1372     v8::Local<v8::Function> function = object.As<v8::Function>();
1373     auto location = LocationMirror::create(function);
1374     if (location) {
1375       mirrors->emplace_back(InternalPropertyMirror{
1376           String16("[[FunctionLocation]]"), std::move(location)});
1377     }
1378     if (function->IsGeneratorFunction()) {
1379       mirrors->emplace_back(InternalPropertyMirror{
1380           String16("[[IsGenerator]]"),
1381           ValueMirror::create(context, v8::True(context->GetIsolate()))});
1382     }
1383   }
1384   if (object->IsGeneratorObject()) {
1385     auto location = LocationMirror::createForGenerator(object);
1386     if (location) {
1387       mirrors->emplace_back(InternalPropertyMirror{
1388           String16("[[GeneratorLocation]]"), std::move(location)});
1389     }
1390   }
1391   V8Debugger* debugger =
1392       static_cast<V8InspectorImpl*>(v8::debug::GetInspector(isolate))
1393           ->debugger();
1394   v8::Local<v8::Array> properties;
1395   if (debugger->internalProperties(context, object).ToLocal(&properties)) {
1396     for (uint32_t i = 0; i < properties->Length(); i += 2) {
1397       v8::Local<v8::Value> name;
1398       if (!properties->Get(context, i).ToLocal(&name) || !name->IsString()) {
1399         tryCatch.Reset();
1400         continue;
1401       }
1402       v8::Local<v8::Value> value;
1403       if (!properties->Get(context, i + 1).ToLocal(&value)) {
1404         tryCatch.Reset();
1405         continue;
1406       }
1407       auto wrapper = ValueMirror::create(context, value);
1408       if (wrapper) {
1409         mirrors->emplace_back(InternalPropertyMirror{
1410             toProtocolStringWithTypeCheck(context->GetIsolate(), name),
1411             std::move(wrapper)});
1412       }
1413     }
1414   }
1415 }
1416 
1417 // static
getPrivateProperties(v8::Local<v8::Context> context,v8::Local<v8::Object> object)1418 std::vector<PrivatePropertyMirror> ValueMirror::getPrivateProperties(
1419     v8::Local<v8::Context> context, v8::Local<v8::Object> object) {
1420   std::vector<PrivatePropertyMirror> mirrors;
1421   v8::Isolate* isolate = context->GetIsolate();
1422   v8::MicrotasksScope microtasksScope(isolate,
1423                                       v8::MicrotasksScope::kDoNotRunMicrotasks);
1424   v8::TryCatch tryCatch(isolate);
1425   v8::Local<v8::Array> privateProperties;
1426 
1427   std::vector<v8::Local<v8::Value>> names;
1428   std::vector<v8::Local<v8::Value>> values;
1429   if (!v8::debug::GetPrivateMembers(context, object, &names, &values))
1430     return mirrors;
1431 
1432   size_t len = values.size();
1433   for (size_t i = 0; i < len; i++) {
1434     v8::Local<v8::Value> name = names[i];
1435     DCHECK(name->IsString());
1436     v8::Local<v8::Value> value = values[i];
1437 
1438     std::unique_ptr<ValueMirror> valueMirror;
1439     std::unique_ptr<ValueMirror> getterMirror;
1440     std::unique_ptr<ValueMirror> setterMirror;
1441     if (v8::debug::AccessorPair::IsAccessorPair(value)) {
1442       v8::Local<v8::debug::AccessorPair> accessors =
1443           value.As<v8::debug::AccessorPair>();
1444       v8::Local<v8::Value> getter = accessors->getter();
1445       v8::Local<v8::Value> setter = accessors->setter();
1446       if (!getter->IsNull()) {
1447         getterMirror = ValueMirror::create(context, getter);
1448       }
1449       if (!setter->IsNull()) {
1450         setterMirror = ValueMirror::create(context, setter);
1451       }
1452     } else {
1453       valueMirror = ValueMirror::create(context, value);
1454     }
1455 
1456     mirrors.emplace_back(PrivatePropertyMirror{
1457         toProtocolStringWithTypeCheck(context->GetIsolate(), name),
1458         std::move(valueMirror), std::move(getterMirror),
1459         std::move(setterMirror)});
1460   }
1461   return mirrors;
1462 }
1463 
descriptionForNode(v8::Local<v8::Context> context,v8::Local<v8::Value> value)1464 String16 descriptionForNode(v8::Local<v8::Context> context,
1465                             v8::Local<v8::Value> value) {
1466   if (!value->IsObject()) return String16();
1467   v8::Local<v8::Object> object = value.As<v8::Object>();
1468   v8::Isolate* isolate = context->GetIsolate();
1469   v8::TryCatch tryCatch(isolate);
1470   v8::Local<v8::Value> nodeName;
1471   if (!object->Get(context, toV8String(isolate, "nodeName"))
1472            .ToLocal(&nodeName)) {
1473     return String16();
1474   }
1475   String16 description;
1476   v8::Local<v8::Function> toLowerCase =
1477       v8::debug::GetBuiltin(isolate, v8::debug::kStringToLowerCase);
1478   if (nodeName->IsString()) {
1479     if (!toLowerCase->Call(context, nodeName, 0, nullptr).ToLocal(&nodeName))
1480       return String16();
1481     if (nodeName->IsString()) {
1482       description = toProtocolString(isolate, nodeName.As<v8::String>());
1483     }
1484   }
1485   if (!description.length()) {
1486     v8::Local<v8::Value> value;
1487     if (!object->Get(context, toV8String(isolate, "constructor"))
1488              .ToLocal(&value) ||
1489         !value->IsObject()) {
1490       return String16();
1491     }
1492     if (!value.As<v8::Object>()
1493              ->Get(context, toV8String(isolate, "name"))
1494              .ToLocal(&value) ||
1495         !value->IsString()) {
1496       return String16();
1497     }
1498     description = toProtocolString(isolate, value.As<v8::String>());
1499   }
1500   v8::Local<v8::Value> nodeType;
1501   if (!object->Get(context, toV8String(isolate, "nodeType"))
1502            .ToLocal(&nodeType) ||
1503       !nodeType->IsInt32()) {
1504     return description;
1505   }
1506   if (nodeType.As<v8::Int32>()->Value() == 1) {
1507     v8::Local<v8::Value> idValue;
1508     if (!object->Get(context, toV8String(isolate, "id")).ToLocal(&idValue)) {
1509       return description;
1510     }
1511     if (idValue->IsString()) {
1512       String16 id = toProtocolString(isolate, idValue.As<v8::String>());
1513       if (id.length()) {
1514         description = String16::concat(description, '#', id);
1515       }
1516     }
1517     v8::Local<v8::Value> classNameValue;
1518     if (!object->Get(context, toV8String(isolate, "className"))
1519              .ToLocal(&classNameValue)) {
1520       return description;
1521     }
1522     if (classNameValue->IsString() &&
1523         classNameValue.As<v8::String>()->Length()) {
1524       String16 classes =
1525           toProtocolString(isolate, classNameValue.As<v8::String>());
1526       String16Builder output;
1527       bool previousIsDot = false;
1528       for (size_t i = 0; i < classes.length(); ++i) {
1529         if (classes[i] == ' ') {
1530           if (!previousIsDot) {
1531             output.append('.');
1532             previousIsDot = true;
1533           }
1534         } else {
1535           output.append(classes[i]);
1536           previousIsDot = classes[i] == '.';
1537         }
1538       }
1539       description = String16::concat(description, '.', output.toString());
1540     }
1541   } else if (nodeType.As<v8::Int32>()->Value() == 1) {
1542     return String16::concat("<!DOCTYPE ", description, '>');
1543   }
1544   return description;
1545 }
1546 
clientMirror(v8::Local<v8::Context> context,v8::Local<v8::Value> value,const String16 & subtype)1547 std::unique_ptr<ValueMirror> clientMirror(v8::Local<v8::Context> context,
1548                                           v8::Local<v8::Value> value,
1549                                           const String16& subtype) {
1550   // TODO(alph): description and length retrieval should move to embedder.
1551   if (subtype == "node") {
1552     return std::make_unique<ObjectMirror>(value, subtype,
1553                                           descriptionForNode(context, value));
1554   }
1555   if (subtype == "error") {
1556     return std::make_unique<ObjectMirror>(
1557         value, RemoteObject::SubtypeEnum::Error,
1558         descriptionForError(context, value.As<v8::Object>(),
1559                             ErrorType::kClient));
1560   }
1561   if (subtype == "array" && value->IsObject()) {
1562     v8::Isolate* isolate = context->GetIsolate();
1563     v8::TryCatch tryCatch(isolate);
1564     v8::Local<v8::Object> object = value.As<v8::Object>();
1565     v8::Local<v8::Value> lengthValue;
1566     if (object->Get(context, toV8String(isolate, "length"))
1567             .ToLocal(&lengthValue)) {
1568       if (lengthValue->IsInt32()) {
1569         return std::make_unique<ObjectMirror>(
1570             value, RemoteObject::SubtypeEnum::Array,
1571             descriptionForCollection(isolate, object,
1572                                      lengthValue.As<v8::Int32>()->Value()));
1573       }
1574     }
1575   }
1576   return std::make_unique<ObjectMirror>(
1577       value,
1578       descriptionForObject(context->GetIsolate(), value.As<v8::Object>()));
1579 }
1580 
create(v8::Local<v8::Context> context,v8::Local<v8::Value> value)1581 std::unique_ptr<ValueMirror> ValueMirror::create(v8::Local<v8::Context> context,
1582                                                  v8::Local<v8::Value> value) {
1583   if (value->IsNull()) {
1584     return std::make_unique<PrimitiveValueMirror>(
1585         value, RemoteObject::TypeEnum::Object);
1586   }
1587   if (value->IsBoolean()) {
1588     return std::make_unique<PrimitiveValueMirror>(
1589         value, RemoteObject::TypeEnum::Boolean);
1590   }
1591   if (value->IsNumber()) {
1592     return std::make_unique<NumberMirror>(value.As<v8::Number>());
1593   }
1594   v8::Isolate* isolate = context->GetIsolate();
1595   if (value->IsString()) {
1596     return std::make_unique<PrimitiveValueMirror>(
1597         value, RemoteObject::TypeEnum::String);
1598   }
1599   if (value->IsBigInt()) {
1600     return std::make_unique<BigIntMirror>(value.As<v8::BigInt>());
1601   }
1602   if (value->IsSymbol()) {
1603     return std::make_unique<SymbolMirror>(value.As<v8::Symbol>());
1604   }
1605   auto clientSubtype = (value->IsUndefined() || value->IsObject())
1606                            ? clientFor(context)->valueSubtype(value)
1607                            : nullptr;
1608   if (clientSubtype) {
1609     String16 subtype = toString16(clientSubtype->string());
1610     return clientMirror(context, value, subtype);
1611   }
1612   if (value->IsUndefined()) {
1613     return std::make_unique<PrimitiveValueMirror>(
1614         value, RemoteObject::TypeEnum::Undefined);
1615   }
1616   if (value->IsRegExp()) {
1617     return std::make_unique<ObjectMirror>(
1618         value, RemoteObject::SubtypeEnum::Regexp,
1619         descriptionForRegExp(isolate, value.As<v8::RegExp>()));
1620   }
1621   if (value->IsProxy()) {
1622     return std::make_unique<ObjectMirror>(
1623         value, RemoteObject::SubtypeEnum::Proxy, "Proxy");
1624   }
1625   if (value->IsFunction()) {
1626     return std::make_unique<FunctionMirror>(value);
1627   }
1628   if (value->IsDate()) {
1629     return std::make_unique<ObjectMirror>(
1630         value, RemoteObject::SubtypeEnum::Date,
1631         descriptionForDate(context, value.As<v8::Date>()));
1632   }
1633   if (value->IsPromise()) {
1634     v8::Local<v8::Promise> promise = value.As<v8::Promise>();
1635     return std::make_unique<ObjectMirror>(
1636         promise, RemoteObject::SubtypeEnum::Promise,
1637         descriptionForObject(isolate, promise));
1638   }
1639   if (value->IsNativeError()) {
1640     return std::make_unique<ObjectMirror>(
1641         value, RemoteObject::SubtypeEnum::Error,
1642         descriptionForError(context, value.As<v8::Object>(),
1643                             ErrorType::kNative));
1644   }
1645   if (value->IsMap()) {
1646     v8::Local<v8::Map> map = value.As<v8::Map>();
1647     return std::make_unique<ObjectMirror>(
1648         value, RemoteObject::SubtypeEnum::Map,
1649         descriptionForCollection(isolate, map, map->Size()));
1650   }
1651   if (value->IsSet()) {
1652     v8::Local<v8::Set> set = value.As<v8::Set>();
1653     return std::make_unique<ObjectMirror>(
1654         value, RemoteObject::SubtypeEnum::Set,
1655         descriptionForCollection(isolate, set, set->Size()));
1656   }
1657   if (value->IsWeakMap()) {
1658     return std::make_unique<ObjectMirror>(
1659         value, RemoteObject::SubtypeEnum::Weakmap,
1660         descriptionForObject(isolate, value.As<v8::Object>()));
1661   }
1662   if (value->IsWeakSet()) {
1663     return std::make_unique<ObjectMirror>(
1664         value, RemoteObject::SubtypeEnum::Weakset,
1665         descriptionForObject(isolate, value.As<v8::Object>()));
1666   }
1667   if (value->IsMapIterator() || value->IsSetIterator()) {
1668     return std::make_unique<ObjectMirror>(
1669         value, RemoteObject::SubtypeEnum::Iterator,
1670         descriptionForObject(isolate, value.As<v8::Object>()));
1671   }
1672   if (value->IsGeneratorObject()) {
1673     v8::Local<v8::Object> object = value.As<v8::Object>();
1674     return std::make_unique<ObjectMirror>(
1675         object, RemoteObject::SubtypeEnum::Generator,
1676         descriptionForObject(isolate, object));
1677   }
1678   if (value->IsTypedArray()) {
1679     v8::Local<v8::TypedArray> array = value.As<v8::TypedArray>();
1680     return std::make_unique<ObjectMirror>(
1681         value, RemoteObject::SubtypeEnum::Typedarray,
1682         descriptionForCollection(isolate, array, array->Length()));
1683   }
1684   if (value->IsArrayBuffer()) {
1685     v8::Local<v8::ArrayBuffer> buffer = value.As<v8::ArrayBuffer>();
1686     return std::make_unique<ObjectMirror>(
1687         value, RemoteObject::SubtypeEnum::Arraybuffer,
1688         descriptionForCollection(isolate, buffer, buffer->ByteLength()));
1689   }
1690   if (value->IsSharedArrayBuffer()) {
1691     v8::Local<v8::SharedArrayBuffer> buffer = value.As<v8::SharedArrayBuffer>();
1692     return std::make_unique<ObjectMirror>(
1693         value, RemoteObject::SubtypeEnum::Arraybuffer,
1694         descriptionForCollection(isolate, buffer, buffer->ByteLength()));
1695   }
1696   if (value->IsDataView()) {
1697     v8::Local<v8::DataView> view = value.As<v8::DataView>();
1698     return std::make_unique<ObjectMirror>(
1699         value, RemoteObject::SubtypeEnum::Dataview,
1700         descriptionForCollection(isolate, view, view->ByteLength()));
1701   }
1702   V8InternalValueType internalType =
1703       v8InternalValueTypeFrom(context, v8::Local<v8::Object>::Cast(value));
1704   if (value->IsArray() && internalType == V8InternalValueType::kScopeList) {
1705     return std::make_unique<ObjectMirror>(
1706         value, "internal#scopeList",
1707         descriptionForScopeList(value.As<v8::Array>()));
1708   }
1709   if (value->IsObject() && internalType == V8InternalValueType::kEntry) {
1710     return std::make_unique<ObjectMirror>(
1711         value, "internal#entry",
1712         descriptionForEntry(context, value.As<v8::Object>()));
1713   }
1714   if (value->IsObject() && internalType == V8InternalValueType::kScope) {
1715     return std::make_unique<ObjectMirror>(
1716         value, "internal#scope",
1717         descriptionForScope(context, value.As<v8::Object>()));
1718   }
1719   size_t length = 0;
1720   if (value->IsArray() || isArrayLike(context, value, &length)) {
1721     length = value->IsArray() ? value.As<v8::Array>()->Length() : length;
1722     return std::make_unique<ObjectMirror>(
1723         value, RemoteObject::SubtypeEnum::Array,
1724         descriptionForCollection(isolate, value.As<v8::Object>(), length));
1725   }
1726   if (value->IsObject()) {
1727     return std::make_unique<ObjectMirror>(
1728         value, descriptionForObject(isolate, value.As<v8::Object>()));
1729   }
1730   return nullptr;
1731 }
1732 }  // namespace v8_inspector
1733