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