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
v8Value() const400 v8::Local<v8::Value> v8Value() const override { return m_value; }
buildRemoteObject(v8::Local<v8::Context> context,WrapMode mode,std::unique_ptr<RemoteObject> * result) const401 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
buildEntryPreview(v8::Local<v8::Context> context,int * nameLimit,int * indexLimit,std::unique_ptr<ObjectPreview> * preview) const415 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
buildPropertyPreview(v8::Local<v8::Context> context,const String16 & name,std::unique_ptr<PropertyPreview> * preview) const429 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
buildWebDriverValue(v8::Local<v8::Context> context,int max_depth,std::unique_ptr<protocol::Runtime::WebDriverValue> * result) const442 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) {}
v8Value() const491 v8::Local<v8::Value> v8Value() const override { return m_value; }
492
buildRemoteObject(v8::Local<v8::Context> context,WrapMode mode,std::unique_ptr<RemoteObject> * result) const493 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 }
buildPropertyPreview(v8::Local<v8::Context> context,const String16 & name,std::unique_ptr<PropertyPreview> * result) const509 void buildPropertyPreview(
510 v8::Local<v8::Context> context, const String16& name,
511 std::unique_ptr<PropertyPreview>* result) const override {
512 bool unserializable = false;
513 *result = PropertyPreview::create()
514 .setName(name)
515 .setType(RemoteObject::TypeEnum::Number)
516 .setValue(description(&unserializable))
517 .build();
518 }
buildEntryPreview(v8::Local<v8::Context> context,int * nameLimit,int * indexLimit,std::unique_ptr<ObjectPreview> * preview) const519 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
buildWebDriverValue(v8::Local<v8::Context> context,int max_depth,std::unique_ptr<protocol::Runtime::WebDriverValue> * result) const532 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
buildRemoteObject(v8::Local<v8::Context> context,WrapMode mode,std::unique_ptr<RemoteObject> * result) const571 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
buildPropertyPreview(v8::Local<v8::Context> context,const String16 & name,std::unique_ptr<protocol::Runtime::PropertyPreview> * preview) const583 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
buildEntryPreview(v8::Local<v8::Context> context,int * nameLimit,int * indexLimit,std::unique_ptr<protocol::Runtime::ObjectPreview> * preview) const595 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
v8Value() const609 v8::Local<v8::Value> v8Value() const override { return m_value; }
610
buildWebDriverValue(v8::Local<v8::Context> context,int max_depth,std::unique_ptr<protocol::Runtime::WebDriverValue> * result) const611 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
buildRemoteObject(v8::Local<v8::Context> context,WrapMode mode,std::unique_ptr<RemoteObject> * result) const634 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
buildPropertyPreview(v8::Local<v8::Context> context,const String16 & name,std::unique_ptr<protocol::Runtime::PropertyPreview> * preview) const647 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
v8Value() const659 v8::Local<v8::Value> v8Value() const override { return m_symbol; }
660
buildWebDriverValue(v8::Local<v8::Context> context,int max_depth,std::unique_ptr<protocol::Runtime::WebDriverValue> * result) const661 protocol::Response buildWebDriverValue(
662 v8::Local<v8::Context> context, int max_depth,
663 std::unique_ptr<protocol::Runtime::WebDriverValue>* result)
664 const override {
665 // https://w3c.github.io/webdriver-bidi/#data-types-protocolValue-RemoteValue-serialization
666 *result = protocol::Runtime::WebDriverValue::create()
667 .setType(protocol::Runtime::WebDriverValue::TypeEnum::Symbol)
668 .build();
669 return Response::Success();
670 }
671
672 private:
673 v8::Local<v8::Symbol> m_symbol;
674 };
675
676 class LocationMirror final : public ValueMirror {
677 public:
create(v8::Local<v8::Function> function)678 static std::unique_ptr<LocationMirror> create(
679 v8::Local<v8::Function> function) {
680 return create(function, function->ScriptId(),
681 function->GetScriptLineNumber(),
682 function->GetScriptColumnNumber());
683 }
createForGenerator(v8::Local<v8::Value> value)684 static std::unique_ptr<LocationMirror> createForGenerator(
685 v8::Local<v8::Value> value) {
686 v8::Local<v8::debug::GeneratorObject> generatorObject =
687 v8::debug::GeneratorObject::Cast(value);
688 if (!generatorObject->IsSuspended()) {
689 return create(generatorObject->Function());
690 }
691 v8::Local<v8::debug::Script> script;
692 if (!generatorObject->Script().ToLocal(&script)) return nullptr;
693 v8::debug::Location suspendedLocation =
694 generatorObject->SuspendedLocation();
695 return create(value, script->Id(), suspendedLocation.GetLineNumber(),
696 suspendedLocation.GetColumnNumber());
697 }
698
buildRemoteObject(v8::Local<v8::Context> context,WrapMode mode,std::unique_ptr<RemoteObject> * result) const699 Response buildRemoteObject(
700 v8::Local<v8::Context> context, WrapMode mode,
701 std::unique_ptr<RemoteObject>* result) const override {
702 auto location = protocol::DictionaryValue::create();
703 location->setString("scriptId", String16::fromInteger(m_scriptId));
704 location->setInteger("lineNumber", m_lineNumber);
705 location->setInteger("columnNumber", m_columnNumber);
706 *result = RemoteObject::create()
707 .setType(RemoteObject::TypeEnum::Object)
708 .setSubtype("internal#location")
709 .setDescription("Object")
710 .setValue(std::move(location))
711 .build();
712 return Response::Success();
713 }
v8Value() const714 v8::Local<v8::Value> v8Value() const override { return m_value; }
715
buildWebDriverValue(v8::Local<v8::Context> context,int max_depth,std::unique_ptr<protocol::Runtime::WebDriverValue> * result) const716 protocol::Response buildWebDriverValue(
717 v8::Local<v8::Context> context, int max_depth,
718 std::unique_ptr<protocol::Runtime::WebDriverValue>* result)
719 const override {
720 *result = protocol::Runtime::WebDriverValue::create()
721 .setType(protocol::Runtime::WebDriverValue::TypeEnum::Object)
722 .build();
723 return Response::Success();
724 }
725
726 private:
create(v8::Local<v8::Value> value,int scriptId,int lineNumber,int columnNumber)727 static std::unique_ptr<LocationMirror> create(v8::Local<v8::Value> value,
728 int scriptId, int lineNumber,
729 int columnNumber) {
730 if (scriptId == v8::UnboundScript::kNoScriptId) return nullptr;
731 if (lineNumber == v8::Function::kLineOffsetNotFound ||
732 columnNumber == v8::Function::kLineOffsetNotFound) {
733 return nullptr;
734 }
735 return std::unique_ptr<LocationMirror>(
736 new LocationMirror(value, scriptId, lineNumber, columnNumber));
737 }
738
LocationMirror(v8::Local<v8::Value> value,int scriptId,int lineNumber,int columnNumber)739 LocationMirror(v8::Local<v8::Value> value, int scriptId, int lineNumber,
740 int columnNumber)
741 : m_value(value),
742 m_scriptId(scriptId),
743 m_lineNumber(lineNumber),
744 m_columnNumber(columnNumber) {}
745
746 v8::Local<v8::Value> m_value;
747 int m_scriptId;
748 int m_lineNumber;
749 int m_columnNumber;
750 };
751
752 class FunctionMirror final : public ValueMirror {
753 public:
FunctionMirror(v8::Local<v8::Value> value)754 explicit FunctionMirror(v8::Local<v8::Value> value)
755 : m_value(value.As<v8::Function>()) {}
756
v8Value() const757 v8::Local<v8::Value> v8Value() const override { return m_value; }
758
buildRemoteObject(v8::Local<v8::Context> context,WrapMode mode,std::unique_ptr<RemoteObject> * result) const759 Response buildRemoteObject(
760 v8::Local<v8::Context> context, WrapMode mode,
761 std::unique_ptr<RemoteObject>* result) const override {
762 // TODO(alph): drop this functionality.
763 if (mode == WrapMode::kForceValue) {
764 std::unique_ptr<protocol::Value> protocolValue;
765 Response response = toProtocolValue(context, m_value, &protocolValue);
766 if (!response.IsSuccess()) return response;
767 *result = RemoteObject::create()
768 .setType(RemoteObject::TypeEnum::Function)
769 .setValue(std::move(protocolValue))
770 .build();
771 } else {
772 *result = RemoteObject::create()
773 .setType(RemoteObject::TypeEnum::Function)
774 .setClassName(toProtocolStringWithTypeCheck(
775 context->GetIsolate(), m_value->GetConstructorName()))
776 .setDescription(descriptionForFunction(m_value))
777 .build();
778 }
779 return Response::Success();
780 }
781
buildPropertyPreview(v8::Local<v8::Context> context,const String16 & name,std::unique_ptr<PropertyPreview> * result) const782 void buildPropertyPreview(
783 v8::Local<v8::Context> context, const String16& name,
784 std::unique_ptr<PropertyPreview>* result) const override {
785 *result = PropertyPreview::create()
786 .setName(name)
787 .setType(RemoteObject::TypeEnum::Function)
788 .setValue(String16())
789 .build();
790 }
buildEntryPreview(v8::Local<v8::Context> context,int * nameLimit,int * indexLimit,std::unique_ptr<ObjectPreview> * preview) const791 void buildEntryPreview(
792 v8::Local<v8::Context> context, int* nameLimit, int* indexLimit,
793 std::unique_ptr<ObjectPreview>* preview) const override {
794 *preview =
795 ObjectPreview::create()
796 .setType(RemoteObject::TypeEnum::Function)
797 .setDescription(descriptionForFunction(m_value))
798 .setOverflow(false)
799 .setProperties(std::make_unique<protocol::Array<PropertyPreview>>())
800 .build();
801 }
802
buildWebDriverValue(v8::Local<v8::Context> context,int max_depth,std::unique_ptr<protocol::Runtime::WebDriverValue> * result) const803 protocol::Response buildWebDriverValue(
804 v8::Local<v8::Context> context, int max_depth,
805 std::unique_ptr<protocol::Runtime::WebDriverValue>* result)
806 const override {
807 // https://w3c.github.io/webdriver-bidi/#data-types-protocolValue-RemoteValue-serialization
808 *result =
809 protocol::Runtime::WebDriverValue::create()
810 .setType(protocol::Runtime::WebDriverValue::TypeEnum::Function)
811 .build();
812 return Response::Success();
813 }
814
815 private:
816 v8::Local<v8::Function> m_value;
817 };
818
isArrayLike(v8::Local<v8::Context> context,v8::Local<v8::Value> value,size_t * length)819 bool isArrayLike(v8::Local<v8::Context> context, v8::Local<v8::Value> value,
820 size_t* length) {
821 if (!value->IsObject()) return false;
822 v8::Isolate* isolate = context->GetIsolate();
823 v8::TryCatch tryCatch(isolate);
824 v8::MicrotasksScope microtasksScope(isolate,
825 v8::MicrotasksScope::kDoNotRunMicrotasks);
826 v8::Local<v8::Object> object = value.As<v8::Object>();
827 v8::Local<v8::Value> spliceValue;
828 if (!object->IsArgumentsObject() &&
829 (!object->GetRealNamedProperty(context, toV8String(isolate, "splice"))
830 .ToLocal(&spliceValue) ||
831 !spliceValue->IsFunction())) {
832 return false;
833 }
834 v8::Local<v8::Value> lengthValue;
835 v8::Maybe<bool> result =
836 object->HasOwnProperty(context, toV8String(isolate, "length"));
837 if (result.IsNothing()) return false;
838 if (!result.FromJust() ||
839 !object->Get(context, toV8String(isolate, "length"))
840 .ToLocal(&lengthValue) ||
841 !lengthValue->IsUint32()) {
842 return false;
843 }
844 *length = lengthValue.As<v8::Uint32>()->Value();
845 return true;
846 }
847
848 struct EntryMirror {
849 std::unique_ptr<ValueMirror> key;
850 std::unique_ptr<ValueMirror> value;
851
getEntriesv8_inspector::__anon176de1c60111::EntryMirror852 static bool getEntries(v8::Local<v8::Context> context,
853 v8::Local<v8::Object> object, size_t limit,
854 bool* overflow, std::vector<EntryMirror>* mirrors) {
855 bool isKeyValue = false;
856 v8::Local<v8::Array> entries;
857 if (!object->PreviewEntries(&isKeyValue).ToLocal(&entries)) return false;
858 for (uint32_t i = 0; i < entries->Length(); i += isKeyValue ? 2 : 1) {
859 v8::Local<v8::Value> tmp;
860
861 std::unique_ptr<ValueMirror> keyMirror;
862 if (isKeyValue && entries->Get(context, i).ToLocal(&tmp)) {
863 keyMirror = ValueMirror::create(context, tmp);
864 }
865 std::unique_ptr<ValueMirror> valueMirror;
866 if (entries->Get(context, isKeyValue ? i + 1 : i).ToLocal(&tmp)) {
867 valueMirror = ValueMirror::create(context, tmp);
868 } else {
869 continue;
870 }
871 if (mirrors->size() == limit) {
872 *overflow = true;
873 return true;
874 }
875 mirrors->emplace_back(
876 EntryMirror{std::move(keyMirror), std::move(valueMirror)});
877 }
878 return mirrors->size() > 0;
879 }
880 };
881
882 class PreviewPropertyAccumulator : public ValueMirror::PropertyAccumulator {
883 public:
PreviewPropertyAccumulator(const std::vector<String16> & blocklist,int skipIndex,int * nameLimit,int * indexLimit,bool * overflow,std::vector<PropertyMirror> * mirrors)884 PreviewPropertyAccumulator(const std::vector<String16>& blocklist,
885 int skipIndex, int* nameLimit, int* indexLimit,
886 bool* overflow,
887 std::vector<PropertyMirror>* mirrors)
888 : m_blocklist(blocklist),
889 m_skipIndex(skipIndex),
890 m_nameLimit(nameLimit),
891 m_indexLimit(indexLimit),
892 m_overflow(overflow),
893 m_mirrors(mirrors) {}
894
Add(PropertyMirror mirror)895 bool Add(PropertyMirror mirror) override {
896 if (mirror.exception) return true;
897 if ((!mirror.getter || !mirror.getter->v8Value()->IsFunction()) &&
898 !mirror.value) {
899 return true;
900 }
901 if (!mirror.isOwn && !mirror.isSynthetic) return true;
902 if (std::find(m_blocklist.begin(), m_blocklist.end(), mirror.name) !=
903 m_blocklist.end()) {
904 return true;
905 }
906 if (mirror.isIndex && m_skipIndex > 0) {
907 --m_skipIndex;
908 if (m_skipIndex > 0) return true;
909 }
910 int* limit = mirror.isIndex ? m_indexLimit : m_nameLimit;
911 if (!*limit) {
912 *m_overflow = true;
913 return false;
914 }
915 --*limit;
916 m_mirrors->push_back(std::move(mirror));
917 return true;
918 }
919
920 private:
921 std::vector<String16> m_blocklist;
922 int m_skipIndex;
923 int* m_nameLimit;
924 int* m_indexLimit;
925 bool* m_overflow;
926 std::vector<PropertyMirror>* m_mirrors;
927 };
928
getPropertiesForPreview(v8::Local<v8::Context> context,v8::Local<v8::Object> object,int * nameLimit,int * indexLimit,bool * overflow,std::vector<PropertyMirror> * properties)929 bool getPropertiesForPreview(v8::Local<v8::Context> context,
930 v8::Local<v8::Object> object, int* nameLimit,
931 int* indexLimit, bool* overflow,
932 std::vector<PropertyMirror>* properties) {
933 std::vector<String16> blocklist;
934 size_t length = 0;
935 if (object->IsArray() || isArrayLike(context, object, &length) ||
936 object->IsStringObject()) {
937 blocklist.push_back("length");
938 #if V8_ENABLE_WEBASSEMBLY
939 } else if (v8::debug::WasmValueObject::IsWasmValueObject(object)) {
940 blocklist.push_back("type");
941 #endif // V8_ENABLE_WEBASSEMBLY
942 } else {
943 auto clientSubtype = clientFor(context)->valueSubtype(object);
944 if (clientSubtype && toString16(clientSubtype->string()) == "array") {
945 blocklist.push_back("length");
946 }
947 }
948 if (object->IsArrayBuffer() || object->IsSharedArrayBuffer()) {
949 blocklist.push_back("[[Int8Array]]");
950 blocklist.push_back("[[Uint8Array]]");
951 blocklist.push_back("[[Int16Array]]");
952 blocklist.push_back("[[Int32Array]]");
953 }
954 int skipIndex = object->IsStringObject()
955 ? object.As<v8::StringObject>()->ValueOf()->Length() + 1
956 : -1;
957 PreviewPropertyAccumulator accumulator(blocklist, skipIndex, nameLimit,
958 indexLimit, overflow, properties);
959 return ValueMirror::getProperties(context, object, false, false, false,
960 &accumulator);
961 }
962
getInternalPropertiesForPreview(v8::Local<v8::Context> context,v8::Local<v8::Object> object,int * nameLimit,bool * overflow,std::vector<InternalPropertyMirror> * properties)963 void getInternalPropertiesForPreview(
964 v8::Local<v8::Context> context, v8::Local<v8::Object> object,
965 int* nameLimit, bool* overflow,
966 std::vector<InternalPropertyMirror>* properties) {
967 std::vector<InternalPropertyMirror> mirrors;
968 ValueMirror::getInternalProperties(context, object, &mirrors);
969 std::vector<String16> allowlist;
970 if (object->IsBooleanObject() || object->IsNumberObject() ||
971 object->IsStringObject() || object->IsSymbolObject() ||
972 object->IsBigIntObject()) {
973 allowlist.emplace_back("[[PrimitiveValue]]");
974 } else if (object->IsPromise()) {
975 allowlist.emplace_back("[[PromiseState]]");
976 allowlist.emplace_back("[[PromiseResult]]");
977 } else if (object->IsGeneratorObject()) {
978 allowlist.emplace_back("[[GeneratorState]]");
979 }
980 for (auto& mirror : mirrors) {
981 if (std::find(allowlist.begin(), allowlist.end(), mirror.name) ==
982 allowlist.end()) {
983 continue;
984 }
985 if (!*nameLimit) {
986 *overflow = true;
987 return;
988 }
989 --*nameLimit;
990 properties->push_back(std::move(mirror));
991 }
992 }
993
getPrivatePropertiesForPreview(v8::Local<v8::Context> context,v8::Local<v8::Object> object,int * nameLimit,bool * overflow,protocol::Array<PropertyPreview> * privateProperties)994 void getPrivatePropertiesForPreview(
995 v8::Local<v8::Context> context, v8::Local<v8::Object> object,
996 int* nameLimit, bool* overflow,
997 protocol::Array<PropertyPreview>* privateProperties) {
998 std::vector<PrivatePropertyMirror> mirrors =
999 ValueMirror::getPrivateProperties(context, object,
1000 /* accessPropertiesOnly */ false);
1001 for (auto& mirror : mirrors) {
1002 std::unique_ptr<PropertyPreview> propertyPreview;
1003 if (mirror.value) {
1004 mirror.value->buildPropertyPreview(context, mirror.name,
1005 &propertyPreview);
1006 } else {
1007 propertyPreview = PropertyPreview::create()
1008 .setName(mirror.name)
1009 .setType(PropertyPreview::TypeEnum::Accessor)
1010 .build();
1011 }
1012 if (!propertyPreview) continue;
1013 if (!*nameLimit) {
1014 *overflow = true;
1015 return;
1016 }
1017 --*nameLimit;
1018 privateProperties->emplace_back(std::move(propertyPreview));
1019 }
1020 }
1021
1022 class ObjectMirror final : public ValueMirror {
1023 public:
ObjectMirror(v8::Local<v8::Value> value,const String16 & description)1024 ObjectMirror(v8::Local<v8::Value> value, const String16& description)
1025 : m_value(value.As<v8::Object>()),
1026 m_description(description),
1027 m_hasSubtype(false) {}
ObjectMirror(v8::Local<v8::Value> value,const String16 & subtype,const String16 & description)1028 ObjectMirror(v8::Local<v8::Value> value, const String16& subtype,
1029 const String16& description)
1030 : m_value(value.As<v8::Object>()),
1031 m_description(description),
1032 m_hasSubtype(true),
1033 m_subtype(subtype) {}
1034
v8Value() const1035 v8::Local<v8::Value> v8Value() const override { return m_value; }
1036
buildRemoteObject(v8::Local<v8::Context> context,WrapMode mode,std::unique_ptr<RemoteObject> * result) const1037 Response buildRemoteObject(
1038 v8::Local<v8::Context> context, WrapMode mode,
1039 std::unique_ptr<RemoteObject>* result) const override {
1040 if (mode == WrapMode::kForceValue) {
1041 std::unique_ptr<protocol::Value> protocolValue;
1042 Response response = toProtocolValue(context, m_value, &protocolValue);
1043 if (!response.IsSuccess()) return response;
1044 *result = RemoteObject::create()
1045 .setType(RemoteObject::TypeEnum::Object)
1046 .setValue(std::move(protocolValue))
1047 .build();
1048 } else {
1049 v8::Isolate* isolate = context->GetIsolate();
1050 *result = RemoteObject::create()
1051 .setType(RemoteObject::TypeEnum::Object)
1052 .setClassName(toProtocolString(
1053 isolate, m_value->GetConstructorName()))
1054 .setDescription(m_description)
1055 .build();
1056 if (m_hasSubtype) (*result)->setSubtype(m_subtype);
1057 if (mode == WrapMode::kWithPreview) {
1058 std::unique_ptr<ObjectPreview> previewValue;
1059 int nameLimit = 5;
1060 int indexLimit = 100;
1061 buildObjectPreview(context, false, &nameLimit, &indexLimit,
1062 &previewValue);
1063 (*result)->setPreview(std::move(previewValue));
1064 }
1065 }
1066 return Response::Success();
1067 }
1068
buildObjectPreview(v8::Local<v8::Context> context,bool generatePreviewForTable,int * nameLimit,int * indexLimit,std::unique_ptr<ObjectPreview> * result) const1069 void buildObjectPreview(
1070 v8::Local<v8::Context> context, bool generatePreviewForTable,
1071 int* nameLimit, int* indexLimit,
1072 std::unique_ptr<ObjectPreview>* result) const override {
1073 buildObjectPreviewInternal(context, false /* forEntry */,
1074 generatePreviewForTable, nameLimit, indexLimit,
1075 result);
1076 }
1077
buildEntryPreview(v8::Local<v8::Context> context,int * nameLimit,int * indexLimit,std::unique_ptr<ObjectPreview> * result) const1078 void buildEntryPreview(
1079 v8::Local<v8::Context> context, int* nameLimit, int* indexLimit,
1080 std::unique_ptr<ObjectPreview>* result) const override {
1081 buildObjectPreviewInternal(context, true /* forEntry */,
1082 false /* generatePreviewForTable */, nameLimit,
1083 indexLimit, result);
1084 }
1085
buildPropertyPreview(v8::Local<v8::Context> context,const String16 & name,std::unique_ptr<PropertyPreview> * result) const1086 void buildPropertyPreview(
1087 v8::Local<v8::Context> context, const String16& name,
1088 std::unique_ptr<PropertyPreview>* result) const override {
1089 *result = PropertyPreview::create()
1090 .setName(name)
1091 .setType(RemoteObject::TypeEnum::Object)
1092 .setValue(abbreviateString(
1093 m_description,
1094 m_subtype == RemoteObject::SubtypeEnum::Regexp ? kMiddle
1095 : kEnd))
1096 .build();
1097 if (m_hasSubtype) (*result)->setSubtype(m_subtype);
1098 }
1099
buildWebDriverValue(v8::Local<v8::Context> context,int max_depth,std::unique_ptr<protocol::Runtime::WebDriverValue> * result) const1100 protocol::Response buildWebDriverValue(
1101 v8::Local<v8::Context> context, int max_depth,
1102 std::unique_ptr<protocol::Runtime::WebDriverValue>* result)
1103 const override {
1104 // https://w3c.github.io/webdriver-bidi/#data-types-protocolValue-RemoteValue-serialization
1105
1106 // Check if embedder implemented custom serialization.
1107 std::unique_ptr<v8_inspector::WebDriverValue> embedder_serialized_result =
1108 clientFor(context)->serializeToWebDriverValue(m_value, max_depth);
1109
1110 if (embedder_serialized_result) {
1111 // Embedder-implemented serialization.
1112 *result = protocol::Runtime::WebDriverValue::create()
1113 .setType(toString16(embedder_serialized_result->type))
1114 .build();
1115
1116 if (!embedder_serialized_result->value.IsEmpty()) {
1117 // Embedder-implemented serialization has value.
1118 std::unique_ptr<protocol::Value> protocol_value;
1119 Response response = toProtocolValue(
1120 context, embedder_serialized_result->value.ToLocalChecked(),
1121 &protocol_value);
1122 if (!response.IsSuccess()) return response;
1123
1124 (*result)->setValue(std::move(protocol_value));
1125 }
1126 return Response::Success();
1127 }
1128
1129 // No embedder-implemented serialization. Serialize as V8 Object.
1130 Response response = V8WebDriverSerializer::serializeV8Value(
1131 m_value, context, max_depth, result);
1132 return response;
1133 }
1134
1135 private:
buildObjectPreviewInternal(v8::Local<v8::Context> context,bool forEntry,bool generatePreviewForTable,int * nameLimit,int * indexLimit,std::unique_ptr<ObjectPreview> * result) const1136 void buildObjectPreviewInternal(
1137 v8::Local<v8::Context> context, bool forEntry,
1138 bool generatePreviewForTable, int* nameLimit, int* indexLimit,
1139 std::unique_ptr<ObjectPreview>* result) const {
1140 auto properties = std::make_unique<protocol::Array<PropertyPreview>>();
1141 std::unique_ptr<protocol::Array<EntryPreview>> entriesPreview;
1142 bool overflow = false;
1143
1144 v8::Local<v8::Value> value = m_value;
1145 while (value->IsProxy()) value = value.As<v8::Proxy>()->GetTarget();
1146
1147 if (value->IsObject() && !value->IsProxy()) {
1148 v8::Local<v8::Object> objectForPreview = value.As<v8::Object>();
1149 std::vector<InternalPropertyMirror> internalProperties;
1150 getInternalPropertiesForPreview(context, objectForPreview, nameLimit,
1151 &overflow, &internalProperties);
1152 for (size_t i = 0; i < internalProperties.size(); ++i) {
1153 std::unique_ptr<PropertyPreview> propertyPreview;
1154 internalProperties[i].value->buildPropertyPreview(
1155 context, internalProperties[i].name, &propertyPreview);
1156 if (propertyPreview) {
1157 properties->emplace_back(std::move(propertyPreview));
1158 }
1159 }
1160
1161 getPrivatePropertiesForPreview(context, objectForPreview, nameLimit,
1162 &overflow, properties.get());
1163
1164 std::vector<PropertyMirror> mirrors;
1165 if (getPropertiesForPreview(context, objectForPreview, nameLimit,
1166 indexLimit, &overflow, &mirrors)) {
1167 for (size_t i = 0; i < mirrors.size(); ++i) {
1168 std::unique_ptr<PropertyPreview> preview;
1169 std::unique_ptr<ObjectPreview> valuePreview;
1170 if (mirrors[i].value) {
1171 mirrors[i].value->buildPropertyPreview(context, mirrors[i].name,
1172 &preview);
1173 if (generatePreviewForTable) {
1174 int tableLimit = 1000;
1175 mirrors[i].value->buildObjectPreview(context, false, &tableLimit,
1176 &tableLimit, &valuePreview);
1177 }
1178 } else {
1179 preview = PropertyPreview::create()
1180 .setName(mirrors[i].name)
1181 .setType(PropertyPreview::TypeEnum::Accessor)
1182 .build();
1183 }
1184 if (valuePreview) {
1185 preview->setValuePreview(std::move(valuePreview));
1186 }
1187 properties->emplace_back(std::move(preview));
1188 }
1189 }
1190
1191 std::vector<EntryMirror> entries;
1192 if (EntryMirror::getEntries(context, objectForPreview, 5, &overflow,
1193 &entries)) {
1194 if (forEntry) {
1195 overflow = true;
1196 } else {
1197 entriesPreview = std::make_unique<protocol::Array<EntryPreview>>();
1198 for (const auto& entry : entries) {
1199 std::unique_ptr<ObjectPreview> valuePreview;
1200 entry.value->buildEntryPreview(context, nameLimit, indexLimit,
1201 &valuePreview);
1202 if (!valuePreview) continue;
1203 std::unique_ptr<ObjectPreview> keyPreview;
1204 if (entry.key) {
1205 entry.key->buildEntryPreview(context, nameLimit, indexLimit,
1206 &keyPreview);
1207 if (!keyPreview) continue;
1208 }
1209 std::unique_ptr<EntryPreview> entryPreview =
1210 EntryPreview::create()
1211 .setValue(std::move(valuePreview))
1212 .build();
1213 if (keyPreview) entryPreview->setKey(std::move(keyPreview));
1214 entriesPreview->emplace_back(std::move(entryPreview));
1215 }
1216 }
1217 }
1218 }
1219 *result = ObjectPreview::create()
1220 .setType(RemoteObject::TypeEnum::Object)
1221 .setDescription(m_description)
1222 .setOverflow(overflow)
1223 .setProperties(std::move(properties))
1224 .build();
1225 if (m_hasSubtype) (*result)->setSubtype(m_subtype);
1226 if (entriesPreview) (*result)->setEntries(std::move(entriesPreview));
1227 }
1228
1229 v8::Local<v8::Object> m_value;
1230 String16 m_description;
1231 bool m_hasSubtype;
1232 String16 m_subtype;
1233 };
1234
nativeGetterCallback(const v8::FunctionCallbackInfo<v8::Value> & info)1235 void nativeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
1236 v8::Local<v8::Object> data = info.Data().As<v8::Object>();
1237 v8::Isolate* isolate = info.GetIsolate();
1238 v8::Local<v8::Context> context = isolate->GetCurrentContext();
1239 v8::Local<v8::Value> name;
1240 if (!data->GetRealNamedProperty(context, toV8String(isolate, "name"))
1241 .ToLocal(&name)) {
1242 return;
1243 }
1244 v8::Local<v8::Value> object;
1245 if (!data->GetRealNamedProperty(context, toV8String(isolate, "object"))
1246 .ToLocal(&object) ||
1247 !object->IsObject()) {
1248 return;
1249 }
1250 v8::Local<v8::Value> value;
1251 if (!object.As<v8::Object>()->Get(context, name).ToLocal(&value)) return;
1252 info.GetReturnValue().Set(value);
1253 }
1254
createNativeGetter(v8::Local<v8::Context> context,v8::Local<v8::Value> object,v8::Local<v8::Name> name)1255 std::unique_ptr<ValueMirror> createNativeGetter(v8::Local<v8::Context> context,
1256 v8::Local<v8::Value> object,
1257 v8::Local<v8::Name> name) {
1258 v8::Isolate* isolate = context->GetIsolate();
1259 v8::TryCatch tryCatch(isolate);
1260
1261 v8::Local<v8::Object> data = v8::Object::New(isolate);
1262 if (data->Set(context, toV8String(isolate, "name"), name).IsNothing()) {
1263 return nullptr;
1264 }
1265 if (data->Set(context, toV8String(isolate, "object"), object).IsNothing()) {
1266 return nullptr;
1267 }
1268
1269 v8::Local<v8::Function> function;
1270 if (!v8::Function::New(context, nativeGetterCallback, data, 0,
1271 v8::ConstructorBehavior::kThrow)
1272 .ToLocal(&function)) {
1273 return nullptr;
1274 }
1275 return ValueMirror::create(context, function);
1276 }
1277
nativeSetterCallback(const v8::FunctionCallbackInfo<v8::Value> & info)1278 void nativeSetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
1279 if (info.Length() < 1) return;
1280 v8::Local<v8::Object> data = info.Data().As<v8::Object>();
1281 v8::Isolate* isolate = info.GetIsolate();
1282 v8::Local<v8::Context> context = isolate->GetCurrentContext();
1283 v8::Local<v8::Value> name;
1284 if (!data->GetRealNamedProperty(context, toV8String(isolate, "name"))
1285 .ToLocal(&name)) {
1286 return;
1287 }
1288 v8::Local<v8::Value> object;
1289 if (!data->GetRealNamedProperty(context, toV8String(isolate, "object"))
1290 .ToLocal(&object) ||
1291 !object->IsObject()) {
1292 return;
1293 }
1294 v8::Local<v8::Value> value;
1295 if (!object.As<v8::Object>()->Set(context, name, info[0]).IsNothing()) return;
1296 }
1297
createNativeSetter(v8::Local<v8::Context> context,v8::Local<v8::Value> object,v8::Local<v8::Name> name)1298 std::unique_ptr<ValueMirror> createNativeSetter(v8::Local<v8::Context> context,
1299 v8::Local<v8::Value> object,
1300 v8::Local<v8::Name> name) {
1301 v8::Isolate* isolate = context->GetIsolate();
1302 v8::TryCatch tryCatch(isolate);
1303
1304 v8::Local<v8::Object> data = v8::Object::New(isolate);
1305 if (data->Set(context, toV8String(isolate, "name"), name).IsNothing()) {
1306 return nullptr;
1307 }
1308 if (data->Set(context, toV8String(isolate, "object"), object).IsNothing()) {
1309 return nullptr;
1310 }
1311
1312 v8::Local<v8::Function> function;
1313 if (!v8::Function::New(context, nativeSetterCallback, data, 1,
1314 v8::ConstructorBehavior::kThrow)
1315 .ToLocal(&function)) {
1316 return nullptr;
1317 }
1318 return ValueMirror::create(context, function);
1319 }
1320
doesAttributeHaveObservableSideEffectOnGet(v8::Local<v8::Context> context,v8::Local<v8::Object> object,v8::Local<v8::Name> name)1321 bool doesAttributeHaveObservableSideEffectOnGet(v8::Local<v8::Context> context,
1322 v8::Local<v8::Object> object,
1323 v8::Local<v8::Name> name) {
1324 // TODO(dgozman): we should remove this, annotate more embedder properties as
1325 // side-effect free, and call all getters which do not produce side effects.
1326 if (!name->IsString()) return false;
1327 v8::Isolate* isolate = context->GetIsolate();
1328 if (!name.As<v8::String>()->StringEquals(toV8String(isolate, "body"))) {
1329 return false;
1330 }
1331
1332 v8::TryCatch tryCatch(isolate);
1333 v8::Local<v8::Value> request;
1334 if (context->Global()
1335 ->GetRealNamedProperty(context, toV8String(isolate, "Request"))
1336 .ToLocal(&request)) {
1337 if (request->IsObject() &&
1338 object->InstanceOf(context, request.As<v8::Object>())
1339 .FromMaybe(false)) {
1340 return true;
1341 }
1342 }
1343 if (tryCatch.HasCaught()) tryCatch.Reset();
1344
1345 v8::Local<v8::Value> response;
1346 if (context->Global()
1347 ->GetRealNamedProperty(context, toV8String(isolate, "Response"))
1348 .ToLocal(&response)) {
1349 if (response->IsObject() &&
1350 object->InstanceOf(context, response.As<v8::Object>())
1351 .FromMaybe(false)) {
1352 return true;
1353 }
1354 }
1355 return false;
1356 }
1357
1358 } // anonymous namespace
1359
1360 ValueMirror::~ValueMirror() = default;
1361
1362 // static
getProperties(v8::Local<v8::Context> context,v8::Local<v8::Object> object,bool ownProperties,bool accessorPropertiesOnly,bool nonIndexedPropertiesOnly,PropertyAccumulator * accumulator)1363 bool ValueMirror::getProperties(v8::Local<v8::Context> context,
1364 v8::Local<v8::Object> object,
1365 bool ownProperties, bool accessorPropertiesOnly,
1366 bool nonIndexedPropertiesOnly,
1367 PropertyAccumulator* accumulator) {
1368 v8::Isolate* isolate = context->GetIsolate();
1369 v8::TryCatch tryCatch(isolate);
1370 v8::Local<v8::Set> set = v8::Set::New(isolate);
1371
1372 v8::MicrotasksScope microtasksScope(isolate,
1373 v8::MicrotasksScope::kDoNotRunMicrotasks);
1374 V8InternalValueType internalType = v8InternalValueTypeFrom(context, object);
1375 if (internalType == V8InternalValueType::kScope) {
1376 v8::Local<v8::Value> value;
1377 if (!object->Get(context, toV8String(isolate, "object")).ToLocal(&value) ||
1378 !value->IsObject()) {
1379 return false;
1380 } else {
1381 object = value.As<v8::Object>();
1382 }
1383 }
1384 if (internalType == V8InternalValueType::kScopeList) {
1385 if (!set->Add(context, toV8String(isolate, "length")).ToLocal(&set)) {
1386 return false;
1387 }
1388 }
1389
1390 auto iterator = v8::debug::PropertyIterator::Create(context, object,
1391 nonIndexedPropertiesOnly);
1392 if (!iterator) {
1393 CHECK(tryCatch.HasCaught());
1394 return false;
1395 }
1396 while (!iterator->Done()) {
1397 bool isOwn = iterator->is_own();
1398 if (!isOwn && ownProperties) break;
1399 v8::Local<v8::Name> v8Name = iterator->name();
1400 v8::Maybe<bool> result = set->Has(context, v8Name);
1401 if (result.IsNothing()) return false;
1402 if (result.FromJust()) {
1403 if (!iterator->Advance().FromMaybe(false)) {
1404 CHECK(tryCatch.HasCaught());
1405 return false;
1406 }
1407 continue;
1408 }
1409 if (!set->Add(context, v8Name).ToLocal(&set)) return false;
1410
1411 String16 name;
1412 std::unique_ptr<ValueMirror> symbolMirror;
1413 if (v8Name->IsString()) {
1414 name = toProtocolString(isolate, v8Name.As<v8::String>());
1415 } else {
1416 v8::Local<v8::Symbol> symbol = v8Name.As<v8::Symbol>();
1417 name = descriptionForSymbol(context, symbol);
1418 symbolMirror = ValueMirror::create(context, symbol);
1419 }
1420
1421 v8::PropertyAttribute attributes;
1422 std::unique_ptr<ValueMirror> valueMirror;
1423 std::unique_ptr<ValueMirror> getterMirror;
1424 std::unique_ptr<ValueMirror> setterMirror;
1425 std::unique_ptr<ValueMirror> exceptionMirror;
1426 bool writable = false;
1427 bool enumerable = false;
1428 bool configurable = false;
1429
1430 bool isAccessorProperty = false;
1431 v8::TryCatch tryCatchAttributes(isolate);
1432 if (!iterator->attributes().To(&attributes)) {
1433 exceptionMirror =
1434 ValueMirror::create(context, tryCatchAttributes.Exception());
1435 } else {
1436 if (iterator->is_native_accessor()) {
1437 if (iterator->has_native_getter()) {
1438 getterMirror = createNativeGetter(context, object, v8Name);
1439 }
1440 if (iterator->has_native_setter()) {
1441 setterMirror = createNativeSetter(context, object, v8Name);
1442 }
1443 writable = !(attributes & v8::PropertyAttribute::ReadOnly);
1444 enumerable = !(attributes & v8::PropertyAttribute::DontEnum);
1445 configurable = !(attributes & v8::PropertyAttribute::DontDelete);
1446 isAccessorProperty = getterMirror || setterMirror;
1447 } else {
1448 v8::TryCatch tryCatchDescriptor(isolate);
1449 v8::debug::PropertyDescriptor descriptor;
1450 if (!iterator->descriptor().To(&descriptor)) {
1451 exceptionMirror =
1452 ValueMirror::create(context, tryCatchDescriptor.Exception());
1453 } else {
1454 writable = descriptor.has_writable ? descriptor.writable : false;
1455 enumerable =
1456 descriptor.has_enumerable ? descriptor.enumerable : false;
1457 configurable =
1458 descriptor.has_configurable ? descriptor.configurable : false;
1459 if (!descriptor.value.IsEmpty()) {
1460 valueMirror = ValueMirror::create(context, descriptor.value);
1461 }
1462 v8::Local<v8::Function> getterFunction;
1463 if (!descriptor.get.IsEmpty()) {
1464 v8::Local<v8::Value> get = descriptor.get;
1465 getterMirror = ValueMirror::create(context, get);
1466 if (get->IsFunction()) getterFunction = get.As<v8::Function>();
1467 }
1468 if (!descriptor.set.IsEmpty()) {
1469 setterMirror = ValueMirror::create(context, descriptor.set);
1470 }
1471 isAccessorProperty = getterMirror || setterMirror;
1472 if (name != "__proto__" && !getterFunction.IsEmpty() &&
1473 getterFunction->ScriptId() == v8::UnboundScript::kNoScriptId &&
1474 !doesAttributeHaveObservableSideEffectOnGet(context, object,
1475 v8Name)) {
1476 v8::TryCatch tryCatchFunction(isolate);
1477 v8::Local<v8::Value> value;
1478 if (object->Get(context, v8Name).ToLocal(&value)) {
1479 if (value->IsPromise() &&
1480 value.As<v8::Promise>()->State() == v8::Promise::kRejected) {
1481 value.As<v8::Promise>()->MarkAsHandled();
1482 } else {
1483 valueMirror = ValueMirror::create(context, value);
1484 setterMirror = nullptr;
1485 getterMirror = nullptr;
1486 }
1487 }
1488 }
1489 }
1490 }
1491 }
1492 if (accessorPropertiesOnly && !isAccessorProperty) continue;
1493 auto mirror = PropertyMirror{name,
1494 writable,
1495 configurable,
1496 enumerable,
1497 isOwn,
1498 iterator->is_array_index(),
1499 isAccessorProperty && valueMirror,
1500 std::move(valueMirror),
1501 std::move(getterMirror),
1502 std::move(setterMirror),
1503 std::move(symbolMirror),
1504 std::move(exceptionMirror)};
1505 if (!accumulator->Add(std::move(mirror))) return true;
1506
1507 if (!iterator->Advance().FromMaybe(false)) {
1508 CHECK(tryCatch.HasCaught());
1509 return false;
1510 }
1511 }
1512 return true;
1513 }
1514
1515 // static
getInternalProperties(v8::Local<v8::Context> context,v8::Local<v8::Object> object,std::vector<InternalPropertyMirror> * mirrors)1516 void ValueMirror::getInternalProperties(
1517 v8::Local<v8::Context> context, v8::Local<v8::Object> object,
1518 std::vector<InternalPropertyMirror>* mirrors) {
1519 v8::Isolate* isolate = context->GetIsolate();
1520 v8::MicrotasksScope microtasksScope(isolate,
1521 v8::MicrotasksScope::kDoNotRunMicrotasks);
1522 v8::TryCatch tryCatch(isolate);
1523 if (object->IsFunction()) {
1524 v8::Local<v8::Function> function = object.As<v8::Function>();
1525 auto location = LocationMirror::create(function);
1526 if (location) {
1527 mirrors->emplace_back(InternalPropertyMirror{
1528 String16("[[FunctionLocation]]"), std::move(location)});
1529 }
1530 if (function->IsGeneratorFunction()) {
1531 mirrors->emplace_back(InternalPropertyMirror{
1532 String16("[[IsGenerator]]"),
1533 ValueMirror::create(context, v8::True(context->GetIsolate()))});
1534 }
1535 }
1536 if (object->IsGeneratorObject()) {
1537 auto location = LocationMirror::createForGenerator(object);
1538 if (location) {
1539 mirrors->emplace_back(InternalPropertyMirror{
1540 String16("[[GeneratorLocation]]"), std::move(location)});
1541 }
1542 }
1543 V8Debugger* debugger =
1544 static_cast<V8InspectorImpl*>(v8::debug::GetInspector(isolate))
1545 ->debugger();
1546 v8::Local<v8::Array> properties;
1547 if (debugger->internalProperties(context, object).ToLocal(&properties)) {
1548 for (uint32_t i = 0; i < properties->Length(); i += 2) {
1549 v8::Local<v8::Value> name;
1550 if (!properties->Get(context, i).ToLocal(&name) || !name->IsString()) {
1551 tryCatch.Reset();
1552 continue;
1553 }
1554 v8::Local<v8::Value> value;
1555 if (!properties->Get(context, i + 1).ToLocal(&value)) {
1556 tryCatch.Reset();
1557 continue;
1558 }
1559 auto wrapper = ValueMirror::create(context, value);
1560 if (wrapper) {
1561 mirrors->emplace_back(InternalPropertyMirror{
1562 toProtocolStringWithTypeCheck(context->GetIsolate(), name),
1563 std::move(wrapper)});
1564 }
1565 }
1566 }
1567 }
1568
1569 // static
getPrivateProperties(v8::Local<v8::Context> context,v8::Local<v8::Object> object,bool accessorPropertiesOnly)1570 std::vector<PrivatePropertyMirror> ValueMirror::getPrivateProperties(
1571 v8::Local<v8::Context> context, v8::Local<v8::Object> object,
1572 bool accessorPropertiesOnly) {
1573 std::vector<PrivatePropertyMirror> mirrors;
1574 v8::Isolate* isolate = context->GetIsolate();
1575 v8::MicrotasksScope microtasksScope(isolate,
1576 v8::MicrotasksScope::kDoNotRunMicrotasks);
1577 v8::TryCatch tryCatch(isolate);
1578 v8::Local<v8::Array> privateProperties;
1579
1580 std::vector<v8::Local<v8::Value>> names;
1581 std::vector<v8::Local<v8::Value>> values;
1582 if (!v8::debug::GetPrivateMembers(context, object, &names, &values))
1583 return mirrors;
1584
1585 size_t len = values.size();
1586 for (size_t i = 0; i < len; i++) {
1587 v8::Local<v8::Value> name = names[i];
1588 DCHECK(name->IsString());
1589 v8::Local<v8::Value> value = values[i];
1590
1591 std::unique_ptr<ValueMirror> valueMirror;
1592 std::unique_ptr<ValueMirror> getterMirror;
1593 std::unique_ptr<ValueMirror> setterMirror;
1594 if (v8::debug::AccessorPair::IsAccessorPair(value)) {
1595 v8::Local<v8::debug::AccessorPair> accessors =
1596 value.As<v8::debug::AccessorPair>();
1597 v8::Local<v8::Value> getter = accessors->getter();
1598 v8::Local<v8::Value> setter = accessors->setter();
1599 if (!getter->IsNull()) {
1600 getterMirror = ValueMirror::create(context, getter);
1601 }
1602 if (!setter->IsNull()) {
1603 setterMirror = ValueMirror::create(context, setter);
1604 }
1605 } else if (accessorPropertiesOnly) {
1606 continue;
1607 } else {
1608 valueMirror = ValueMirror::create(context, value);
1609 }
1610
1611 mirrors.emplace_back(PrivatePropertyMirror{
1612 toProtocolStringWithTypeCheck(context->GetIsolate(), name),
1613 std::move(valueMirror), std::move(getterMirror),
1614 std::move(setterMirror)});
1615 }
1616 return mirrors;
1617 }
1618
clientMirror(v8::Local<v8::Context> context,v8::Local<v8::Value> value,const String16 & subtype)1619 std::unique_ptr<ValueMirror> clientMirror(v8::Local<v8::Context> context,
1620 v8::Local<v8::Value> value,
1621 const String16& subtype) {
1622 auto descriptionForValueSubtype =
1623 clientFor(context)->descriptionForValueSubtype(context, value);
1624 if (descriptionForValueSubtype) {
1625 return std::make_unique<ObjectMirror>(
1626 value, subtype, toString16(descriptionForValueSubtype->string()));
1627 }
1628 if (subtype == "error") {
1629 return std::make_unique<ObjectMirror>(
1630 value, RemoteObject::SubtypeEnum::Error,
1631 descriptionForError(context, value.As<v8::Object>(),
1632 ErrorType::kClient));
1633 }
1634 if (subtype == "array" && value->IsObject()) {
1635 v8::Isolate* isolate = context->GetIsolate();
1636 v8::TryCatch tryCatch(isolate);
1637 v8::Local<v8::Object> object = value.As<v8::Object>();
1638 v8::Local<v8::Value> lengthValue;
1639 if (object->Get(context, toV8String(isolate, "length"))
1640 .ToLocal(&lengthValue)) {
1641 if (lengthValue->IsInt32()) {
1642 return std::make_unique<ObjectMirror>(
1643 value, RemoteObject::SubtypeEnum::Array,
1644 descriptionForCollection(isolate, object,
1645 lengthValue.As<v8::Int32>()->Value()));
1646 }
1647 }
1648 }
1649 return std::make_unique<ObjectMirror>(
1650 value,
1651 descriptionForObject(context->GetIsolate(), value.As<v8::Object>()));
1652 }
1653
create(v8::Local<v8::Context> context,v8::Local<v8::Value> value)1654 std::unique_ptr<ValueMirror> ValueMirror::create(v8::Local<v8::Context> context,
1655 v8::Local<v8::Value> value) {
1656 if (value->IsNull()) {
1657 return std::make_unique<PrimitiveValueMirror>(
1658 value, RemoteObject::TypeEnum::Object);
1659 }
1660 if (value->IsBoolean()) {
1661 return std::make_unique<PrimitiveValueMirror>(
1662 value, RemoteObject::TypeEnum::Boolean);
1663 }
1664 if (value->IsNumber()) {
1665 return std::make_unique<NumberMirror>(value.As<v8::Number>());
1666 }
1667 v8::Isolate* isolate = context->GetIsolate();
1668 if (value->IsString()) {
1669 return std::make_unique<PrimitiveValueMirror>(
1670 value, RemoteObject::TypeEnum::String);
1671 }
1672 if (value->IsBigInt()) {
1673 return std::make_unique<BigIntMirror>(value.As<v8::BigInt>());
1674 }
1675 if (value->IsSymbol()) {
1676 return std::make_unique<SymbolMirror>(value.As<v8::Symbol>());
1677 }
1678 auto clientSubtype = (value->IsUndefined() || value->IsObject())
1679 ? clientFor(context)->valueSubtype(value)
1680 : nullptr;
1681 if (clientSubtype) {
1682 String16 subtype = toString16(clientSubtype->string());
1683 return clientMirror(context, value, subtype);
1684 }
1685 if (value->IsUndefined()) {
1686 return std::make_unique<PrimitiveValueMirror>(
1687 value, RemoteObject::TypeEnum::Undefined);
1688 }
1689 if (value->IsRegExp()) {
1690 return std::make_unique<ObjectMirror>(
1691 value, RemoteObject::SubtypeEnum::Regexp,
1692 descriptionForRegExp(isolate, value.As<v8::RegExp>()));
1693 }
1694 if (value->IsProxy()) {
1695 return std::make_unique<ObjectMirror>(
1696 value, RemoteObject::SubtypeEnum::Proxy, "Proxy");
1697 }
1698 if (value->IsFunction()) {
1699 return std::make_unique<FunctionMirror>(value);
1700 }
1701 if (value->IsDate()) {
1702 return std::make_unique<ObjectMirror>(
1703 value, RemoteObject::SubtypeEnum::Date,
1704 descriptionForDate(context, value.As<v8::Date>()));
1705 }
1706 if (value->IsPromise()) {
1707 v8::Local<v8::Promise> promise = value.As<v8::Promise>();
1708 return std::make_unique<ObjectMirror>(
1709 promise, RemoteObject::SubtypeEnum::Promise,
1710 descriptionForObject(isolate, promise));
1711 }
1712 if (value->IsNativeError()) {
1713 return std::make_unique<ObjectMirror>(
1714 value, RemoteObject::SubtypeEnum::Error,
1715 descriptionForError(context, value.As<v8::Object>(),
1716 ErrorType::kNative));
1717 }
1718 if (value->IsMap()) {
1719 v8::Local<v8::Map> map = value.As<v8::Map>();
1720 return std::make_unique<ObjectMirror>(
1721 value, RemoteObject::SubtypeEnum::Map,
1722 descriptionForCollection(isolate, map, map->Size()));
1723 }
1724 if (value->IsSet()) {
1725 v8::Local<v8::Set> set = value.As<v8::Set>();
1726 return std::make_unique<ObjectMirror>(
1727 value, RemoteObject::SubtypeEnum::Set,
1728 descriptionForCollection(isolate, set, set->Size()));
1729 }
1730 if (value->IsWeakMap()) {
1731 return std::make_unique<ObjectMirror>(
1732 value, RemoteObject::SubtypeEnum::Weakmap,
1733 descriptionForObject(isolate, value.As<v8::Object>()));
1734 }
1735 if (value->IsWeakSet()) {
1736 return std::make_unique<ObjectMirror>(
1737 value, RemoteObject::SubtypeEnum::Weakset,
1738 descriptionForObject(isolate, value.As<v8::Object>()));
1739 }
1740 if (value->IsMapIterator() || value->IsSetIterator()) {
1741 return std::make_unique<ObjectMirror>(
1742 value, RemoteObject::SubtypeEnum::Iterator,
1743 descriptionForObject(isolate, value.As<v8::Object>()));
1744 }
1745 if (value->IsGeneratorObject()) {
1746 v8::Local<v8::Object> object = value.As<v8::Object>();
1747 return std::make_unique<ObjectMirror>(
1748 object, RemoteObject::SubtypeEnum::Generator,
1749 descriptionForObject(isolate, object));
1750 }
1751 if (value->IsTypedArray()) {
1752 v8::Local<v8::TypedArray> array = value.As<v8::TypedArray>();
1753 return std::make_unique<ObjectMirror>(
1754 value, RemoteObject::SubtypeEnum::Typedarray,
1755 descriptionForCollection(isolate, array, array->Length()));
1756 }
1757 if (value->IsArrayBuffer()) {
1758 v8::Local<v8::ArrayBuffer> buffer = value.As<v8::ArrayBuffer>();
1759 return std::make_unique<ObjectMirror>(
1760 value, RemoteObject::SubtypeEnum::Arraybuffer,
1761 descriptionForCollection(isolate, buffer, buffer->ByteLength()));
1762 }
1763 if (value->IsSharedArrayBuffer()) {
1764 v8::Local<v8::SharedArrayBuffer> buffer = value.As<v8::SharedArrayBuffer>();
1765 return std::make_unique<ObjectMirror>(
1766 value, RemoteObject::SubtypeEnum::Arraybuffer,
1767 descriptionForCollection(isolate, buffer, buffer->ByteLength()));
1768 }
1769 if (value->IsDataView()) {
1770 v8::Local<v8::DataView> view = value.As<v8::DataView>();
1771 return std::make_unique<ObjectMirror>(
1772 value, RemoteObject::SubtypeEnum::Dataview,
1773 descriptionForCollection(isolate, view, view->ByteLength()));
1774 }
1775 if (value->IsWasmMemoryObject()) {
1776 v8::Local<v8::WasmMemoryObject> memory = value.As<v8::WasmMemoryObject>();
1777 return std::make_unique<ObjectMirror>(
1778 value, RemoteObject::SubtypeEnum::Webassemblymemory,
1779 descriptionForCollection(
1780 isolate, memory, memory->Buffer()->ByteLength() / kWasmPageSize));
1781 }
1782 #if V8_ENABLE_WEBASSEMBLY
1783 if (v8::debug::WasmValueObject::IsWasmValueObject(value)) {
1784 v8::Local<v8::debug::WasmValueObject> object =
1785 value.As<v8::debug::WasmValueObject>();
1786 return std::make_unique<ObjectMirror>(
1787 value, RemoteObject::SubtypeEnum::Wasmvalue,
1788 descriptionForWasmValueObject(context, object));
1789 }
1790 #endif // V8_ENABLE_WEBASSEMBLY
1791 V8InternalValueType internalType =
1792 v8InternalValueTypeFrom(context, value.As<v8::Object>());
1793 if (value->IsArray() && internalType == V8InternalValueType::kScopeList) {
1794 return std::make_unique<ObjectMirror>(
1795 value, "internal#scopeList",
1796 descriptionForScopeList(value.As<v8::Array>()));
1797 }
1798 if (value->IsObject() && internalType == V8InternalValueType::kEntry) {
1799 return std::make_unique<ObjectMirror>(
1800 value, "internal#entry",
1801 descriptionForEntry(context, value.As<v8::Object>()));
1802 }
1803 if (value->IsObject() && internalType == V8InternalValueType::kScope) {
1804 return std::make_unique<ObjectMirror>(
1805 value, "internal#scope",
1806 descriptionForScope(context, value.As<v8::Object>()));
1807 }
1808 size_t length = 0;
1809 if (value->IsArray() || isArrayLike(context, value, &length)) {
1810 length = value->IsArray() ? value.As<v8::Array>()->Length() : length;
1811 return std::make_unique<ObjectMirror>(
1812 value, RemoteObject::SubtypeEnum::Array,
1813 descriptionForCollection(isolate, value.As<v8::Object>(), length));
1814 }
1815 if (value->IsObject()) {
1816 return std::make_unique<ObjectMirror>(
1817 value, descriptionForObject(isolate, value.As<v8::Object>()));
1818 }
1819 return nullptr;
1820 }
1821 } // namespace v8_inspector
1822