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