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