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