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