summaryrefslogtreecommitdiff
path: root/deps/v8/src/inspector/value-mirror.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/inspector/value-mirror.cc')
-rw-r--r--deps/v8/src/inspector/value-mirror.cc1617
1 files changed, 1617 insertions, 0 deletions
diff --git a/deps/v8/src/inspector/value-mirror.cc b/deps/v8/src/inspector/value-mirror.cc
new file mode 100644
index 0000000000..aac6481828
--- /dev/null
+++ b/deps/v8/src/inspector/value-mirror.cc
@@ -0,0 +1,1617 @@
+// Copyright 2018 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/inspector/value-mirror.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "src/debug/debug-interface.h"
+#include "src/inspector/v8-debugger.h"
+#include "src/inspector/v8-inspector-impl.h"
+#include "src/inspector/v8-value-utils.h"
+
+namespace v8_inspector {
+
+using protocol::Response;
+using protocol::Runtime::RemoteObject;
+using protocol::Runtime::ObjectPreview;
+using protocol::Runtime::PropertyPreview;
+using protocol::Runtime::EntryPreview;
+using protocol::Runtime::InternalPropertyDescriptor;
+
+namespace {
+V8InspectorClient* clientFor(v8::Local<v8::Context> context) {
+ return static_cast<V8InspectorImpl*>(
+ v8::debug::GetInspector(context->GetIsolate()))
+ ->client();
+}
+
+V8InternalValueType v8InternalValueTypeFrom(v8::Local<v8::Context> context,
+ v8::Local<v8::Value> value) {
+ if (!value->IsObject()) return V8InternalValueType::kNone;
+ V8InspectorImpl* inspector = static_cast<V8InspectorImpl*>(
+ v8::debug::GetInspector(context->GetIsolate()));
+ int contextId = InspectedContext::contextId(context);
+ InspectedContext* inspectedContext = inspector->getContext(contextId);
+ if (!inspectedContext) return V8InternalValueType::kNone;
+ return inspectedContext->getInternalType(value.As<v8::Object>());
+}
+
+Response toProtocolValue(v8::Local<v8::Context> context,
+ v8::Local<v8::Value> value, int maxDepth,
+ std::unique_ptr<protocol::Value>* result) {
+ if (!maxDepth) return Response::Error("Object reference chain is too long");
+ maxDepth--;
+
+ if (value->IsNull() || value->IsUndefined()) {
+ *result = protocol::Value::null();
+ return Response::OK();
+ }
+ if (value->IsBoolean()) {
+ *result =
+ protocol::FundamentalValue::create(value.As<v8::Boolean>()->Value());
+ return Response::OK();
+ }
+ if (value->IsNumber()) {
+ double doubleValue = value.As<v8::Number>()->Value();
+ int intValue = static_cast<int>(doubleValue);
+ if (intValue == doubleValue) {
+ *result = protocol::FundamentalValue::create(intValue);
+ return Response::OK();
+ }
+ *result = protocol::FundamentalValue::create(doubleValue);
+ return Response::OK();
+ }
+ if (value->IsString()) {
+ *result = protocol::StringValue::create(
+ toProtocolString(context->GetIsolate(), value.As<v8::String>()));
+ return Response::OK();
+ }
+ if (value->IsArray()) {
+ v8::Local<v8::Array> array = value.As<v8::Array>();
+ std::unique_ptr<protocol::ListValue> inspectorArray =
+ protocol::ListValue::create();
+ uint32_t length = array->Length();
+ for (uint32_t i = 0; i < length; i++) {
+ v8::Local<v8::Value> value;
+ if (!array->Get(context, i).ToLocal(&value))
+ return Response::InternalError();
+ std::unique_ptr<protocol::Value> element;
+ Response response = toProtocolValue(context, value, maxDepth, &element);
+ if (!response.isSuccess()) return response;
+ inspectorArray->pushValue(std::move(element));
+ }
+ *result = std::move(inspectorArray);
+ return Response::OK();
+ }
+ if (value->IsObject()) {
+ std::unique_ptr<protocol::DictionaryValue> jsonObject =
+ protocol::DictionaryValue::create();
+ v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(value);
+ v8::Local<v8::Array> propertyNames;
+ if (!object->GetPropertyNames(context).ToLocal(&propertyNames))
+ return Response::InternalError();
+ uint32_t length = propertyNames->Length();
+ for (uint32_t i = 0; i < length; i++) {
+ v8::Local<v8::Value> name;
+ if (!propertyNames->Get(context, i).ToLocal(&name))
+ return Response::InternalError();
+ // FIXME(yurys): v8::Object should support GetOwnPropertyNames
+ if (name->IsString()) {
+ v8::Maybe<bool> hasRealNamedProperty = object->HasRealNamedProperty(
+ context, v8::Local<v8::String>::Cast(name));
+ if (hasRealNamedProperty.IsNothing() ||
+ !hasRealNamedProperty.FromJust())
+ continue;
+ }
+ v8::Local<v8::String> propertyName;
+ if (!name->ToString(context).ToLocal(&propertyName)) continue;
+ v8::Local<v8::Value> property;
+ if (!object->Get(context, name).ToLocal(&property))
+ return Response::InternalError();
+ if (property->IsUndefined()) continue;
+ std::unique_ptr<protocol::Value> propertyValue;
+ Response response =
+ toProtocolValue(context, property, maxDepth, &propertyValue);
+ if (!response.isSuccess()) return response;
+ jsonObject->setValue(
+ toProtocolString(context->GetIsolate(), propertyName),
+ std::move(propertyValue));
+ }
+ *result = std::move(jsonObject);
+ return Response::OK();
+ }
+ return Response::Error("Object couldn't be returned by value");
+}
+
+Response toProtocolValue(v8::Local<v8::Context> context,
+ v8::Local<v8::Value> value,
+ std::unique_ptr<protocol::Value>* result) {
+ if (value->IsUndefined()) return Response::OK();
+ return toProtocolValue(context, value, 1000, result);
+}
+
+enum AbbreviateMode { kMiddle, kEnd };
+
+String16 abbreviateString(const String16& value, AbbreviateMode mode) {
+ const size_t maxLength = 100;
+ if (value.length() <= maxLength) return value;
+ UChar ellipsis = static_cast<UChar>(0x2026);
+ if (mode == kMiddle) {
+ return String16::concat(
+ value.substring(0, maxLength / 2), String16(&ellipsis, 1),
+ value.substring(value.length() - maxLength / 2 + 1));
+ }
+ return String16::concat(value.substring(0, maxLength - 1), ellipsis);
+}
+
+String16 descriptionForSymbol(v8::Local<v8::Context> context,
+ v8::Local<v8::Symbol> symbol) {
+ return String16::concat(
+ "Symbol(",
+ toProtocolStringWithTypeCheck(context->GetIsolate(), symbol->Name()),
+ ")");
+}
+
+String16 descriptionForBigInt(v8::Local<v8::Context> context,
+ v8::Local<v8::BigInt> value) {
+ v8::Isolate* isolate = context->GetIsolate();
+ v8::TryCatch tryCatch(isolate);
+ v8::Local<v8::String> description;
+ if (!value->ToString(context).ToLocal(&description)) return String16();
+ return toProtocolString(isolate, description) + "n";
+}
+
+String16 descriptionForPrimitiveType(v8::Local<v8::Context> context,
+ v8::Local<v8::Value> value) {
+ if (value->IsUndefined()) return RemoteObject::TypeEnum::Undefined;
+ if (value->IsNull()) return RemoteObject::SubtypeEnum::Null;
+ if (value->IsBoolean()) {
+ return value.As<v8::Boolean>()->Value() ? "true" : "false";
+ }
+ if (value->IsString()) {
+ return toProtocolString(context->GetIsolate(), value.As<v8::String>());
+ }
+ UNREACHABLE();
+ return String16();
+}
+
+String16 descriptionForRegExp(v8::Isolate* isolate,
+ v8::Local<v8::RegExp> value) {
+ String16Builder description;
+ description.append('/');
+ description.append(toProtocolString(isolate, value->GetSource()));
+ description.append('/');
+ v8::RegExp::Flags flags = value->GetFlags();
+ if (flags & v8::RegExp::Flags::kGlobal) description.append('g');
+ if (flags & v8::RegExp::Flags::kIgnoreCase) description.append('i');
+ if (flags & v8::RegExp::Flags::kMultiline) description.append('m');
+ if (flags & v8::RegExp::Flags::kDotAll) description.append('s');
+ if (flags & v8::RegExp::Flags::kUnicode) description.append('u');
+ if (flags & v8::RegExp::Flags::kSticky) description.append('y');
+ return description.toString();
+}
+
+enum class ErrorType { kNative, kClient };
+
+String16 descriptionForError(v8::Local<v8::Context> context,
+ v8::Local<v8::Object> object, ErrorType type) {
+ v8::Isolate* isolate = context->GetIsolate();
+ v8::TryCatch tryCatch(isolate);
+ String16 className = toProtocolString(isolate, object->GetConstructorName());
+ v8::Local<v8::Value> stackValue;
+ if (!object->Get(context, toV8String(isolate, "stack"))
+ .ToLocal(&stackValue) ||
+ !stackValue->IsString()) {
+ return className;
+ }
+ String16 stack = toProtocolString(isolate, stackValue.As<v8::String>());
+ String16 description = stack;
+ if (type == ErrorType::kClient) {
+ if (stack.substring(0, className.length()) != className) {
+ v8::Local<v8::Value> messageValue;
+ if (!object->Get(context, toV8String(isolate, "message"))
+ .ToLocal(&messageValue) ||
+ !messageValue->IsString()) {
+ return stack;
+ }
+ String16 message = toProtocolStringWithTypeCheck(isolate, messageValue);
+ size_t index = stack.find(message);
+ String16 stackWithoutMessage =
+ index != String16::kNotFound
+ ? stack.substring(index + message.length())
+ : String16();
+ description = className + ": " + message + stackWithoutMessage;
+ }
+ }
+ return description;
+}
+
+String16 descriptionForObject(v8::Isolate* isolate,
+ v8::Local<v8::Object> object) {
+ return toProtocolString(isolate, object->GetConstructorName());
+}
+
+String16 descriptionForDate(v8::Local<v8::Context> context,
+ v8::Local<v8::Date> date) {
+ v8::Isolate* isolate = context->GetIsolate();
+ v8::TryCatch tryCatch(isolate);
+ v8::Local<v8::String> description;
+ if (!date->ToString(context).ToLocal(&description)) {
+ return descriptionForObject(isolate, date);
+ }
+ return toProtocolString(isolate, description);
+}
+
+String16 descriptionForScopeList(v8::Local<v8::Array> list) {
+ return String16::concat(
+ "Scopes[", String16::fromInteger(static_cast<size_t>(list->Length())),
+ ']');
+}
+
+String16 descriptionForScope(v8::Local<v8::Context> context,
+ v8::Local<v8::Object> object) {
+ v8::Isolate* isolate = context->GetIsolate();
+ v8::Local<v8::Value> value;
+ if (!object->GetRealNamedProperty(context, toV8String(isolate, "description"))
+ .ToLocal(&value)) {
+ return String16();
+ }
+ return toProtocolStringWithTypeCheck(isolate, value);
+}
+
+String16 descriptionForCollection(v8::Isolate* isolate,
+ v8::Local<v8::Object> object, size_t length) {
+ String16 className = toProtocolString(isolate, object->GetConstructorName());
+ return String16::concat(className, '(', String16::fromInteger(length), ')');
+}
+
+String16 descriptionForEntry(v8::Local<v8::Context> context,
+ v8::Local<v8::Object> object) {
+ v8::Isolate* isolate = context->GetIsolate();
+ String16 key;
+ v8::Local<v8::Value> tmp;
+ if (object->GetRealNamedProperty(context, toV8String(isolate, "key"))
+ .ToLocal(&tmp)) {
+ auto wrapper = ValueMirror::create(context, tmp);
+ if (wrapper) {
+ std::unique_ptr<ObjectPreview> preview;
+ int limit = 5;
+ wrapper->buildEntryPreview(context, &limit, &limit, &preview);
+ if (preview) {
+ key = preview->getDescription(String16());
+ if (preview->getType() == RemoteObject::TypeEnum::String) {
+ key = String16::concat('\"', key, '\"');
+ }
+ }
+ }
+ }
+
+ String16 value;
+ if (object->GetRealNamedProperty(context, toV8String(isolate, "value"))
+ .ToLocal(&tmp)) {
+ auto wrapper = ValueMirror::create(context, tmp);
+ if (wrapper) {
+ std::unique_ptr<ObjectPreview> preview;
+ int limit = 5;
+ wrapper->buildEntryPreview(context, &limit, &limit, &preview);
+ if (preview) {
+ value = preview->getDescription(String16());
+ if (preview->getType() == RemoteObject::TypeEnum::String) {
+ value = String16::concat('\"', value, '\"');
+ }
+ }
+ }
+ }
+
+ return key.length() ? ("{" + key + " => " + value + "}") : value;
+}
+
+String16 descriptionForFunction(v8::Local<v8::Context> context,
+ v8::Local<v8::Function> value) {
+ v8::Isolate* isolate = context->GetIsolate();
+ v8::TryCatch tryCatch(isolate);
+ v8::Local<v8::String> description;
+ if (!value->ToString(context).ToLocal(&description)) {
+ return descriptionForObject(isolate, value);
+ }
+ return toProtocolString(isolate, description);
+}
+
+class PrimitiveValueMirror final : public ValueMirror {
+ public:
+ PrimitiveValueMirror(v8::Local<v8::Value> value, const String16& type)
+ : m_value(value), m_type(type) {}
+
+ v8::Local<v8::Value> v8Value() const override { return m_value; }
+ Response buildRemoteObject(
+ v8::Local<v8::Context> context, WrapMode mode,
+ std::unique_ptr<RemoteObject>* result) const override {
+ std::unique_ptr<protocol::Value> protocolValue;
+ toProtocolValue(context, m_value, &protocolValue);
+ *result = RemoteObject::create()
+ .setType(m_type)
+ .setValue(std::move(protocolValue))
+ .build();
+ if (m_value->IsNull())
+ (*result)->setSubtype(RemoteObject::SubtypeEnum::Null);
+ return Response::OK();
+ }
+
+ void buildEntryPreview(
+ v8::Local<v8::Context> context, int* nameLimit, int* indexLimit,
+ std::unique_ptr<ObjectPreview>* preview) const override {
+ *preview =
+ ObjectPreview::create()
+ .setType(m_type)
+ .setDescription(descriptionForPrimitiveType(context, m_value))
+ .setOverflow(false)
+ .setProperties(protocol::Array<PropertyPreview>::create())
+ .build();
+ if (m_value->IsNull())
+ (*preview)->setSubtype(RemoteObject::SubtypeEnum::Null);
+ }
+
+ void buildPropertyPreview(
+ v8::Local<v8::Context> context, const String16& name,
+ std::unique_ptr<PropertyPreview>* preview) const override {
+ *preview = PropertyPreview::create()
+ .setName(name)
+ .setValue(abbreviateString(
+ descriptionForPrimitiveType(context, m_value), kMiddle))
+ .setType(m_type)
+ .build();
+ if (m_value->IsNull())
+ (*preview)->setSubtype(RemoteObject::SubtypeEnum::Null);
+ }
+
+ private:
+ v8::Local<v8::Value> m_value;
+ String16 m_type;
+ String16 m_subtype;
+};
+
+class NumberMirror final : public ValueMirror {
+ public:
+ explicit NumberMirror(v8::Local<v8::Number> value) : m_value(value) {}
+ v8::Local<v8::Value> v8Value() const override { return m_value; }
+
+ Response buildRemoteObject(
+ v8::Local<v8::Context> context, WrapMode mode,
+ std::unique_ptr<RemoteObject>* result) const override {
+ bool unserializable = false;
+ String16 descriptionValue = description(&unserializable);
+ *result = RemoteObject::create()
+ .setType(RemoteObject::TypeEnum::Number)
+ .setDescription(descriptionValue)
+ .build();
+ if (unserializable) {
+ (*result)->setUnserializableValue(descriptionValue);
+ } else {
+ (*result)->setValue(protocol::FundamentalValue::create(m_value->Value()));
+ }
+ return Response::OK();
+ }
+ void buildPropertyPreview(
+ v8::Local<v8::Context> context, const String16& name,
+ std::unique_ptr<PropertyPreview>* result) const override {
+ bool unserializable = false;
+ *result = PropertyPreview::create()
+ .setName(name)
+ .setType(RemoteObject::TypeEnum::Number)
+ .setValue(description(&unserializable))
+ .build();
+ }
+ void buildEntryPreview(
+ v8::Local<v8::Context> context, int* nameLimit, int* indexLimit,
+ std::unique_ptr<ObjectPreview>* preview) const override {
+ bool unserializable = false;
+ *preview = ObjectPreview::create()
+ .setType(RemoteObject::TypeEnum::Number)
+ .setDescription(description(&unserializable))
+ .setOverflow(false)
+ .setProperties(protocol::Array<PropertyPreview>::create())
+ .build();
+ }
+
+ private:
+ String16 description(bool* unserializable) const {
+ *unserializable = true;
+ double rawValue = m_value->Value();
+ if (std::isnan(rawValue)) return "NaN";
+ if (rawValue == 0.0 && std::signbit(rawValue)) return "-0";
+ if (std::isinf(rawValue)) {
+ return std::signbit(rawValue) ? "-Infinity" : "Infinity";
+ }
+ *unserializable = false;
+ return String16::fromDouble(rawValue);
+ }
+
+ v8::Local<v8::Number> m_value;
+};
+
+class BigIntMirror final : public ValueMirror {
+ public:
+ explicit BigIntMirror(v8::Local<v8::BigInt> value) : m_value(value) {}
+
+ Response buildRemoteObject(
+ v8::Local<v8::Context> context, WrapMode mode,
+ std::unique_ptr<RemoteObject>* result) const override {
+ String16 description = descriptionForBigInt(context, m_value);
+ *result = RemoteObject::create()
+ .setType(RemoteObject::TypeEnum::Bigint)
+ .setUnserializableValue(description)
+ .setDescription(description)
+ .build();
+ return Response::OK();
+ }
+
+ void buildPropertyPreview(v8::Local<v8::Context> context,
+ const String16& name,
+ std::unique_ptr<protocol::Runtime::PropertyPreview>*
+ preview) const override {
+ *preview = PropertyPreview::create()
+ .setName(name)
+ .setType(RemoteObject::TypeEnum::Bigint)
+ .setValue(abbreviateString(
+ descriptionForBigInt(context, m_value), kMiddle))
+ .build();
+ }
+
+ void buildEntryPreview(v8::Local<v8::Context> context, int* nameLimit,
+ int* indexLimit,
+ std::unique_ptr<protocol::Runtime::ObjectPreview>*
+ preview) const override {
+ *preview = ObjectPreview::create()
+ .setType(RemoteObject::TypeEnum::Bigint)
+ .setDescription(descriptionForBigInt(context, m_value))
+ .setOverflow(false)
+ .setProperties(protocol::Array<PropertyPreview>::create())
+ .build();
+ }
+
+ v8::Local<v8::Value> v8Value() const override { return m_value; }
+
+ private:
+ v8::Local<v8::BigInt> m_value;
+};
+
+class SymbolMirror final : public ValueMirror {
+ public:
+ explicit SymbolMirror(v8::Local<v8::Value> value)
+ : m_symbol(value.As<v8::Symbol>()) {}
+
+ Response buildRemoteObject(
+ v8::Local<v8::Context> context, WrapMode mode,
+ std::unique_ptr<RemoteObject>* result) const override {
+ if (mode == WrapMode::kForceValue) {
+ return Response::Error("Object couldn't be returned by value");
+ }
+ *result = RemoteObject::create()
+ .setType(RemoteObject::TypeEnum::Symbol)
+ .setDescription(descriptionForSymbol(context, m_symbol))
+ .build();
+ return Response::OK();
+ }
+
+ void buildPropertyPreview(v8::Local<v8::Context> context,
+ const String16& name,
+ std::unique_ptr<protocol::Runtime::PropertyPreview>*
+ preview) const override {
+ *preview = PropertyPreview::create()
+ .setName(name)
+ .setType(RemoteObject::TypeEnum::Symbol)
+ .setValue(abbreviateString(
+ descriptionForSymbol(context, m_symbol), kEnd))
+ .build();
+ }
+
+ v8::Local<v8::Value> v8Value() const override { return m_symbol; }
+
+ private:
+ v8::Local<v8::Symbol> m_symbol;
+};
+
+class LocationMirror final : public ValueMirror {
+ public:
+ static std::unique_ptr<LocationMirror> create(
+ v8::Local<v8::Function> function) {
+ return create(function, function->ScriptId(),
+ function->GetScriptLineNumber(),
+ function->GetScriptColumnNumber());
+ }
+ static std::unique_ptr<LocationMirror> createForGenerator(
+ v8::Local<v8::Value> value) {
+ v8::Local<v8::debug::GeneratorObject> generatorObject =
+ v8::debug::GeneratorObject::Cast(value);
+ if (!generatorObject->IsSuspended()) {
+ return create(generatorObject->Function());
+ }
+ v8::Local<v8::debug::Script> script;
+ if (!generatorObject->Script().ToLocal(&script)) return nullptr;
+ v8::debug::Location suspendedLocation =
+ generatorObject->SuspendedLocation();
+ return create(value, script->Id(), suspendedLocation.GetLineNumber(),
+ suspendedLocation.GetColumnNumber());
+ }
+
+ Response buildRemoteObject(
+ v8::Local<v8::Context> context, WrapMode mode,
+ std::unique_ptr<RemoteObject>* result) const override {
+ auto location = protocol::DictionaryValue::create();
+ location->setString("scriptId", String16::fromInteger(m_scriptId));
+ location->setInteger("lineNumber", m_lineNumber);
+ location->setInteger("columnNumber", m_columnNumber);
+ *result = RemoteObject::create()
+ .setType(RemoteObject::TypeEnum::Object)
+ .setSubtype("internal#location")
+ .setDescription("Object")
+ .setValue(std::move(location))
+ .build();
+ return Response::OK();
+ }
+ v8::Local<v8::Value> v8Value() const override { return m_value; }
+
+ private:
+ static std::unique_ptr<LocationMirror> create(v8::Local<v8::Value> value,
+ int scriptId, int lineNumber,
+ int columnNumber) {
+ if (scriptId == v8::UnboundScript::kNoScriptId) return nullptr;
+ if (lineNumber == v8::Function::kLineOffsetNotFound ||
+ columnNumber == v8::Function::kLineOffsetNotFound) {
+ return nullptr;
+ }
+ return std::unique_ptr<LocationMirror>(
+ new LocationMirror(value, scriptId, lineNumber, columnNumber));
+ }
+
+ LocationMirror(v8::Local<v8::Value> value, int scriptId, int lineNumber,
+ int columnNumber)
+ : m_value(value),
+ m_scriptId(scriptId),
+ m_lineNumber(lineNumber),
+ m_columnNumber(columnNumber) {}
+
+ v8::Local<v8::Value> m_value;
+ int m_scriptId;
+ int m_lineNumber;
+ int m_columnNumber;
+};
+
+class FunctionMirror final : public ValueMirror {
+ public:
+ explicit FunctionMirror(v8::Local<v8::Value> value)
+ : m_value(value.As<v8::Function>()) {}
+
+ v8::Local<v8::Value> v8Value() const override { return m_value; }
+
+ Response buildRemoteObject(
+ v8::Local<v8::Context> context, WrapMode mode,
+ std::unique_ptr<RemoteObject>* result) const override {
+ // TODO(alph): drop this functionality.
+ if (mode == WrapMode::kForceValue) {
+ std::unique_ptr<protocol::Value> protocolValue;
+ Response response = toProtocolValue(context, m_value, &protocolValue);
+ if (!response.isSuccess()) return response;
+ *result = RemoteObject::create()
+ .setType(RemoteObject::TypeEnum::Function)
+ .setValue(std::move(protocolValue))
+ .build();
+ } else {
+ *result = RemoteObject::create()
+ .setType(RemoteObject::TypeEnum::Function)
+ .setClassName(toProtocolStringWithTypeCheck(
+ context->GetIsolate(), m_value->GetConstructorName()))
+ .setDescription(descriptionForFunction(context, m_value))
+ .build();
+ }
+ return Response::OK();
+ }
+
+ void buildPropertyPreview(
+ v8::Local<v8::Context> context, const String16& name,
+ std::unique_ptr<PropertyPreview>* result) const override {
+ *result = PropertyPreview::create()
+ .setName(name)
+ .setType(RemoteObject::TypeEnum::Function)
+ .setValue(String16())
+ .build();
+ }
+ void buildEntryPreview(
+ v8::Local<v8::Context> context, int* nameLimit, int* indexLimit,
+ std::unique_ptr<ObjectPreview>* preview) const override {
+ *preview = ObjectPreview::create()
+ .setType(RemoteObject::TypeEnum::Function)
+ .setDescription(descriptionForFunction(context, m_value))
+ .setOverflow(false)
+ .setProperties(protocol::Array<PropertyPreview>::create())
+ .build();
+ }
+
+ private:
+ v8::Local<v8::Function> m_value;
+};
+
+bool isArrayLike(v8::Local<v8::Context> context, v8::Local<v8::Value> value,
+ size_t* length) {
+ if (!value->IsObject()) return false;
+ v8::Isolate* isolate = context->GetIsolate();
+ v8::TryCatch tryCatch(isolate);
+ v8::MicrotasksScope microtasksScope(isolate,
+ v8::MicrotasksScope::kDoNotRunMicrotasks);
+ v8::Local<v8::Object> object = value.As<v8::Object>();
+ v8::Local<v8::Value> spliceValue;
+ if (!object->IsArgumentsObject() &&
+ (!object->GetRealNamedProperty(context, toV8String(isolate, "splice"))
+ .ToLocal(&spliceValue) ||
+ !spliceValue->IsFunction())) {
+ return false;
+ }
+ v8::Local<v8::Value> lengthValue;
+ v8::Maybe<bool> result =
+ object->HasOwnProperty(context, toV8String(isolate, "length"));
+ if (result.IsNothing()) return false;
+ if (!result.FromJust() ||
+ !object->Get(context, toV8String(isolate, "length"))
+ .ToLocal(&lengthValue) ||
+ !lengthValue->IsUint32()) {
+ return false;
+ }
+ *length = v8::Local<v8::Uint32>::Cast(lengthValue)->Value();
+ return true;
+}
+
+struct EntryMirror {
+ std::unique_ptr<ValueMirror> key;
+ std::unique_ptr<ValueMirror> value;
+
+ static bool getEntries(v8::Local<v8::Context> context,
+ v8::Local<v8::Object> object, size_t limit,
+ bool* overflow, std::vector<EntryMirror>* mirrors) {
+ bool isKeyValue = false;
+ v8::Local<v8::Array> entries;
+ if (!object->PreviewEntries(&isKeyValue).ToLocal(&entries)) return false;
+ for (uint32_t i = 0; i < entries->Length(); i += isKeyValue ? 2 : 1) {
+ v8::Local<v8::Value> tmp;
+
+ std::unique_ptr<ValueMirror> keyMirror;
+ if (isKeyValue && entries->Get(context, i).ToLocal(&tmp)) {
+ keyMirror = ValueMirror::create(context, tmp);
+ }
+ std::unique_ptr<ValueMirror> valueMirror;
+ if (entries->Get(context, isKeyValue ? i + 1 : i).ToLocal(&tmp)) {
+ valueMirror = ValueMirror::create(context, tmp);
+ } else {
+ continue;
+ }
+ if (mirrors->size() == limit) {
+ *overflow = true;
+ return true;
+ }
+ mirrors->emplace_back(
+ EntryMirror{std::move(keyMirror), std::move(valueMirror)});
+ }
+ return mirrors->size() > 0;
+ }
+};
+
+class PreviewPropertyAccumulator : public ValueMirror::PropertyAccumulator {
+ public:
+ PreviewPropertyAccumulator(const std::vector<String16>& blacklist,
+ int skipIndex, int* nameLimit, int* indexLimit,
+ bool* overflow,
+ std::vector<PropertyMirror>* mirrors)
+ : m_blacklist(blacklist),
+ m_skipIndex(skipIndex),
+ m_nameLimit(nameLimit),
+ m_indexLimit(indexLimit),
+ m_overflow(overflow),
+ m_mirrors(mirrors) {}
+
+ bool Add(PropertyMirror mirror) override {
+ if (mirror.exception) return true;
+ if ((!mirror.getter || !mirror.getter->v8Value()->IsFunction()) &&
+ !mirror.value) {
+ return true;
+ }
+ if (!mirror.isOwn) return true;
+ if (std::find(m_blacklist.begin(), m_blacklist.end(), mirror.name) !=
+ m_blacklist.end()) {
+ return true;
+ }
+ if (mirror.isIndex && m_skipIndex > 0) {
+ --m_skipIndex;
+ if (m_skipIndex > 0) return true;
+ }
+ int* limit = mirror.isIndex ? m_indexLimit : m_nameLimit;
+ if (!*limit) {
+ *m_overflow = true;
+ return false;
+ }
+ --*limit;
+ m_mirrors->push_back(std::move(mirror));
+ return true;
+ }
+
+ private:
+ std::vector<String16> m_blacklist;
+ int m_skipIndex;
+ int* m_nameLimit;
+ int* m_indexLimit;
+ bool* m_overflow;
+ std::vector<PropertyMirror>* m_mirrors;
+};
+
+bool getPropertiesForPreview(v8::Local<v8::Context> context,
+ v8::Local<v8::Object> object, int* nameLimit,
+ int* indexLimit, bool* overflow,
+ std::vector<PropertyMirror>* properties) {
+ std::vector<String16> blacklist;
+ size_t length = 0;
+ if (object->IsArray() || isArrayLike(context, object, &length) ||
+ object->IsStringObject()) {
+ blacklist.push_back("length");
+ } else {
+ auto clientSubtype = clientFor(context)->valueSubtype(object);
+ if (clientSubtype && toString16(clientSubtype->string()) == "array") {
+ blacklist.push_back("length");
+ }
+ }
+ if (object->IsArrayBuffer() || object->IsSharedArrayBuffer()) {
+ blacklist.push_back("[[Int8Array]]");
+ blacklist.push_back("[[Uint8Array]]");
+ blacklist.push_back("[[Int16Array]]");
+ blacklist.push_back("[[Int32Array]]");
+ }
+ int skipIndex = object->IsStringObject()
+ ? object.As<v8::StringObject>()->ValueOf()->Length() + 1
+ : -1;
+ PreviewPropertyAccumulator accumulator(blacklist, skipIndex, nameLimit,
+ indexLimit, overflow, properties);
+ return ValueMirror::getProperties(context, object, false, false,
+ &accumulator);
+}
+
+void getInternalPropertiesForPreview(
+ v8::Local<v8::Context> context, v8::Local<v8::Object> object,
+ int* nameLimit, bool* overflow,
+ std::vector<InternalPropertyMirror>* properties) {
+ std::vector<InternalPropertyMirror> mirrors;
+ ValueMirror::getInternalProperties(context, object, &mirrors);
+ std::vector<String16> whitelist;
+ if (object->IsBooleanObject() || object->IsNumberObject() ||
+ object->IsStringObject() || object->IsSymbolObject() ||
+ object->IsBigIntObject()) {
+ whitelist.emplace_back("[[PrimitiveValue]]");
+ } else if (object->IsPromise()) {
+ whitelist.emplace_back("[[PromiseStatus]]");
+ whitelist.emplace_back("[[PromiseValue]]");
+ } else if (object->IsGeneratorObject()) {
+ whitelist.emplace_back("[[GeneratorStatus]]");
+ }
+ for (auto& mirror : mirrors) {
+ if (std::find(whitelist.begin(), whitelist.end(), mirror.name) ==
+ whitelist.end()) {
+ continue;
+ }
+ if (!*nameLimit) {
+ *overflow = true;
+ return;
+ }
+ --*nameLimit;
+ properties->push_back(std::move(mirror));
+ }
+}
+
+class ObjectMirror final : public ValueMirror {
+ public:
+ ObjectMirror(v8::Local<v8::Value> value, const String16& description)
+ : m_value(value.As<v8::Object>()),
+ m_description(description),
+ m_hasSubtype(false) {}
+ ObjectMirror(v8::Local<v8::Value> value, const String16& subtype,
+ const String16& description)
+ : m_value(value.As<v8::Object>()),
+ m_description(description),
+ m_hasSubtype(true),
+ m_subtype(subtype) {}
+
+ v8::Local<v8::Value> v8Value() const override { return m_value; }
+
+ Response buildRemoteObject(
+ v8::Local<v8::Context> context, WrapMode mode,
+ std::unique_ptr<RemoteObject>* result) const override {
+ if (mode == WrapMode::kForceValue) {
+ std::unique_ptr<protocol::Value> protocolValue;
+ Response response = toProtocolValue(context, m_value, &protocolValue);
+ if (!response.isSuccess()) return response;
+ *result = RemoteObject::create()
+ .setType(RemoteObject::TypeEnum::Object)
+ .setValue(std::move(protocolValue))
+ .build();
+ } else {
+ v8::Isolate* isolate = context->GetIsolate();
+ *result = RemoteObject::create()
+ .setType(RemoteObject::TypeEnum::Object)
+ .setClassName(toProtocolString(
+ isolate, m_value->GetConstructorName()))
+ .setDescription(m_description)
+ .build();
+ if (m_hasSubtype) (*result)->setSubtype(m_subtype);
+ if (mode == WrapMode::kWithPreview) {
+ std::unique_ptr<ObjectPreview> previewValue;
+ int nameLimit = 5;
+ int indexLimit = 100;
+ buildObjectPreview(context, false, &nameLimit, &indexLimit,
+ &previewValue);
+ (*result)->setPreview(std::move(previewValue));
+ }
+ }
+ return Response::OK();
+ }
+
+ void buildObjectPreview(
+ v8::Local<v8::Context> context, bool generatePreviewForTable,
+ int* nameLimit, int* indexLimit,
+ std::unique_ptr<ObjectPreview>* result) const override {
+ buildObjectPreviewInternal(context, false /* forEntry */,
+ generatePreviewForTable, nameLimit, indexLimit,
+ result);
+ }
+
+ void buildEntryPreview(
+ v8::Local<v8::Context> context, int* nameLimit, int* indexLimit,
+ std::unique_ptr<ObjectPreview>* result) const override {
+ buildObjectPreviewInternal(context, true /* forEntry */,
+ false /* generatePreviewForTable */, nameLimit,
+ indexLimit, result);
+ }
+
+ void buildPropertyPreview(
+ v8::Local<v8::Context> context, const String16& name,
+ std::unique_ptr<PropertyPreview>* result) const override {
+ *result = PropertyPreview::create()
+ .setName(name)
+ .setType(RemoteObject::TypeEnum::Object)
+ .setValue(abbreviateString(
+ m_description,
+ m_subtype == RemoteObject::SubtypeEnum::Regexp ? kMiddle
+ : kEnd))
+ .build();
+ if (m_hasSubtype) (*result)->setSubtype(m_subtype);
+ }
+
+ private:
+ void buildObjectPreviewInternal(
+ v8::Local<v8::Context> context, bool forEntry,
+ bool generatePreviewForTable, int* nameLimit, int* indexLimit,
+ std::unique_ptr<ObjectPreview>* result) const {
+ std::unique_ptr<protocol::Array<PropertyPreview>> properties =
+ protocol::Array<PropertyPreview>::create();
+ std::unique_ptr<protocol::Array<EntryPreview>> entriesPreview;
+ bool overflow = false;
+
+ v8::Local<v8::Value> value = m_value;
+ while (value->IsProxy()) value = value.As<v8::Proxy>()->GetTarget();
+ if (value->IsObject() && !value->IsProxy()) {
+ v8::Local<v8::Object> objectForPreview = value.As<v8::Object>();
+ std::vector<InternalPropertyMirror> internalProperties;
+ getInternalPropertiesForPreview(context, objectForPreview, nameLimit,
+ &overflow, &internalProperties);
+ for (size_t i = 0; i < internalProperties.size(); ++i) {
+ std::unique_ptr<PropertyPreview> propertyPreview;
+ internalProperties[i].value->buildPropertyPreview(
+ context, internalProperties[i].name, &propertyPreview);
+ if (propertyPreview) {
+ properties->addItem(std::move(propertyPreview));
+ }
+ }
+
+ std::vector<PropertyMirror> mirrors;
+ if (getPropertiesForPreview(context, objectForPreview, nameLimit,
+ indexLimit, &overflow, &mirrors)) {
+ for (size_t i = 0; i < mirrors.size(); ++i) {
+ std::unique_ptr<PropertyPreview> preview;
+ std::unique_ptr<ObjectPreview> valuePreview;
+ if (mirrors[i].value) {
+ mirrors[i].value->buildPropertyPreview(context, mirrors[i].name,
+ &preview);
+ if (generatePreviewForTable) {
+ int tableLimit = 1000;
+ mirrors[i].value->buildObjectPreview(context, false, &tableLimit,
+ &tableLimit, &valuePreview);
+ }
+ } else {
+ preview = PropertyPreview::create()
+ .setName(mirrors[i].name)
+ .setType(PropertyPreview::TypeEnum::Accessor)
+ .build();
+ }
+ if (valuePreview) {
+ preview->setValuePreview(std::move(valuePreview));
+ }
+ properties->addItem(std::move(preview));
+ }
+ }
+
+ std::vector<EntryMirror> entries;
+ if (EntryMirror::getEntries(context, objectForPreview, 5, &overflow,
+ &entries)) {
+ if (forEntry) {
+ overflow = true;
+ } else {
+ entriesPreview = protocol::Array<EntryPreview>::create();
+ for (const auto& entry : entries) {
+ std::unique_ptr<ObjectPreview> valuePreview;
+ entry.value->buildEntryPreview(context, nameLimit, indexLimit,
+ &valuePreview);
+ if (!valuePreview) continue;
+ std::unique_ptr<ObjectPreview> keyPreview;
+ if (entry.key) {
+ entry.key->buildEntryPreview(context, nameLimit, indexLimit,
+ &keyPreview);
+ if (!keyPreview) continue;
+ }
+ std::unique_ptr<EntryPreview> entryPreview =
+ EntryPreview::create()
+ .setValue(std::move(valuePreview))
+ .build();
+ if (keyPreview) entryPreview->setKey(std::move(keyPreview));
+ entriesPreview->addItem(std::move(entryPreview));
+ }
+ }
+ }
+ }
+ *result = ObjectPreview::create()
+ .setType(RemoteObject::TypeEnum::Object)
+ .setDescription(m_description)
+ .setOverflow(overflow)
+ .setProperties(std::move(properties))
+ .build();
+ if (m_hasSubtype) (*result)->setSubtype(m_subtype);
+ if (entriesPreview) (*result)->setEntries(std::move(entriesPreview));
+ }
+
+ v8::Local<v8::Object> m_value;
+ String16 m_description;
+ bool m_hasSubtype;
+ String16 m_subtype;
+};
+
+void nativeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
+ v8::Local<v8::Object> data = info.Data().As<v8::Object>();
+ v8::Isolate* isolate = info.GetIsolate();
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Value> name;
+ if (!data->GetRealNamedProperty(context, toV8String(isolate, "name"))
+ .ToLocal(&name)) {
+ return;
+ }
+ v8::Local<v8::Value> object;
+ if (!data->GetRealNamedProperty(context, toV8String(isolate, "object"))
+ .ToLocal(&object) ||
+ !object->IsObject()) {
+ return;
+ }
+ v8::Local<v8::Value> value;
+ if (!object.As<v8::Object>()->Get(context, name).ToLocal(&value)) return;
+ info.GetReturnValue().Set(value);
+}
+
+std::unique_ptr<ValueMirror> createNativeGetter(v8::Local<v8::Context> context,
+ v8::Local<v8::Value> object,
+ v8::Local<v8::Name> name) {
+ v8::Isolate* isolate = context->GetIsolate();
+ v8::TryCatch tryCatch(isolate);
+
+ v8::Local<v8::Object> data = v8::Object::New(isolate);
+ if (data->Set(context, toV8String(isolate, "name"), name).IsNothing()) {
+ return nullptr;
+ }
+ if (data->Set(context, toV8String(isolate, "object"), object).IsNothing()) {
+ return nullptr;
+ }
+
+ v8::Local<v8::Function> function;
+ if (!v8::Function::New(context, nativeGetterCallback, data, 0,
+ v8::ConstructorBehavior::kThrow)
+ .ToLocal(&function)) {
+ return nullptr;
+ }
+ return ValueMirror::create(context, function);
+}
+
+void nativeSetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
+ if (info.Length() < 1) return;
+ v8::Local<v8::Object> data = info.Data().As<v8::Object>();
+ v8::Isolate* isolate = info.GetIsolate();
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ v8::Local<v8::Value> name;
+ if (!data->GetRealNamedProperty(context, toV8String(isolate, "name"))
+ .ToLocal(&name)) {
+ return;
+ }
+ v8::Local<v8::Value> object;
+ if (!data->GetRealNamedProperty(context, toV8String(isolate, "object"))
+ .ToLocal(&object) ||
+ !object->IsObject()) {
+ return;
+ }
+ v8::Local<v8::Value> value;
+ if (!object.As<v8::Object>()->Set(context, name, info[0]).IsNothing()) return;
+}
+
+std::unique_ptr<ValueMirror> createNativeSetter(v8::Local<v8::Context> context,
+ v8::Local<v8::Value> object,
+ v8::Local<v8::Name> name) {
+ v8::Isolate* isolate = context->GetIsolate();
+ v8::TryCatch tryCatch(isolate);
+
+ v8::Local<v8::Object> data = v8::Object::New(isolate);
+ if (data->Set(context, toV8String(isolate, "name"), name).IsNothing()) {
+ return nullptr;
+ }
+ if (data->Set(context, toV8String(isolate, "object"), object).IsNothing()) {
+ return nullptr;
+ }
+
+ v8::Local<v8::Function> function;
+ if (!v8::Function::New(context, nativeSetterCallback, data, 1,
+ v8::ConstructorBehavior::kThrow)
+ .ToLocal(&function)) {
+ return nullptr;
+ }
+ return ValueMirror::create(context, function);
+}
+
+bool doesAttributeHaveObservableSideEffectOnGet(v8::Local<v8::Context> context,
+ v8::Local<v8::Object> object,
+ v8::Local<v8::Name> name) {
+ // TODO(dgozman): we should remove this, annotate more embedder properties as
+ // side-effect free, and call all getters which do not produce side effects.
+ if (!name->IsString()) return false;
+ v8::Isolate* isolate = context->GetIsolate();
+ if (!name.As<v8::String>()->StringEquals(toV8String(isolate, "body"))) {
+ return false;
+ }
+
+ v8::TryCatch tryCatch(isolate);
+ v8::Local<v8::Value> request;
+ if (context->Global()
+ ->GetRealNamedProperty(context, toV8String(isolate, "Request"))
+ .ToLocal(&request)) {
+ if (request->IsObject() &&
+ object->InstanceOf(context, request.As<v8::Object>())
+ .FromMaybe(false)) {
+ return true;
+ }
+ }
+ if (tryCatch.HasCaught()) tryCatch.Reset();
+
+ v8::Local<v8::Value> response;
+ if (context->Global()
+ ->GetRealNamedProperty(context, toV8String(isolate, "Response"))
+ .ToLocal(&response)) {
+ if (response->IsObject() &&
+ object->InstanceOf(context, response.As<v8::Object>())
+ .FromMaybe(false)) {
+ return true;
+ }
+ }
+ return false;
+}
+template <typename ArrayView, typename ArrayBuffer>
+void addTypedArrayView(v8::Local<v8::Context> context,
+ v8::Local<ArrayBuffer> buffer, size_t length,
+ const char* name,
+ ValueMirror::PropertyAccumulator* accumulator) {
+ accumulator->Add(PropertyMirror{
+ String16(name), false, false, false, true, false,
+ ValueMirror::create(context, ArrayView::New(buffer, 0, length)), nullptr,
+ nullptr, nullptr, nullptr});
+}
+
+template <typename ArrayBuffer>
+void addTypedArrayViews(v8::Local<v8::Context> context,
+ v8::Local<ArrayBuffer> buffer,
+ ValueMirror::PropertyAccumulator* accumulator) {
+ // TODO(alph): these should be internal properties.
+ size_t length = buffer->ByteLength();
+ addTypedArrayView<v8::Int8Array>(context, buffer, length, "[[Int8Array]]",
+ accumulator);
+ addTypedArrayView<v8::Uint8Array>(context, buffer, length, "[[Uint8Array]]",
+ accumulator);
+ if (buffer->ByteLength() % 2 == 0) {
+ addTypedArrayView<v8::Int16Array>(context, buffer, length / 2,
+ "[[Int16Array]]", accumulator);
+ }
+ if (buffer->ByteLength() % 4 == 0) {
+ addTypedArrayView<v8::Int32Array>(context, buffer, length / 4,
+ "[[Int32Array]]", accumulator);
+ }
+}
+} // anonymous namespace
+
+ValueMirror::~ValueMirror() = default;
+
+// static
+bool ValueMirror::getProperties(v8::Local<v8::Context> context,
+ v8::Local<v8::Object> object,
+ bool ownProperties, bool accessorPropertiesOnly,
+ PropertyAccumulator* accumulator) {
+ v8::Isolate* isolate = context->GetIsolate();
+ v8::TryCatch tryCatch(isolate);
+ v8::Local<v8::Set> set = v8::Set::New(isolate);
+
+ v8::MicrotasksScope microtasksScope(isolate,
+ v8::MicrotasksScope::kDoNotRunMicrotasks);
+ V8InternalValueType internalType = v8InternalValueTypeFrom(context, object);
+ if (internalType == V8InternalValueType::kScope) {
+ v8::Local<v8::Value> value;
+ if (!object->Get(context, toV8String(isolate, "object")).ToLocal(&value) ||
+ !value->IsObject()) {
+ return false;
+ } else {
+ object = value.As<v8::Object>();
+ }
+ }
+ if (internalType == V8InternalValueType::kScopeList) {
+ if (!set->Add(context, toV8String(isolate, "length")).ToLocal(&set)) {
+ return false;
+ }
+ }
+ bool shouldSkipProto = internalType == V8InternalValueType::kScopeList;
+
+ bool formatAccessorsAsProperties =
+ clientFor(context)->formatAccessorsAsProperties(object);
+
+ if (object->IsArrayBuffer()) {
+ addTypedArrayViews(context, object.As<v8::ArrayBuffer>(), accumulator);
+ }
+ if (object->IsSharedArrayBuffer()) {
+ addTypedArrayViews(context, object.As<v8::SharedArrayBuffer>(),
+ accumulator);
+ }
+
+ for (auto iterator = v8::debug::PropertyIterator::Create(object);
+ !iterator->Done(); iterator->Advance()) {
+ bool isOwn = iterator->is_own();
+ if (!isOwn && ownProperties) break;
+ v8::Local<v8::Name> v8Name = iterator->name();
+ v8::Maybe<bool> result = set->Has(context, v8Name);
+ if (result.IsNothing()) return false;
+ if (result.FromJust()) continue;
+ if (!set->Add(context, v8Name).ToLocal(&set)) return false;
+
+ String16 name;
+ std::unique_ptr<ValueMirror> symbolMirror;
+ if (v8Name->IsString()) {
+ name = toProtocolString(isolate, v8Name.As<v8::String>());
+ } else {
+ v8::Local<v8::Symbol> symbol = v8Name.As<v8::Symbol>();
+ name = descriptionForSymbol(context, symbol);
+ symbolMirror = ValueMirror::create(context, symbol);
+ }
+
+ v8::PropertyAttribute attributes;
+ std::unique_ptr<ValueMirror> valueMirror;
+ std::unique_ptr<ValueMirror> getterMirror;
+ std::unique_ptr<ValueMirror> setterMirror;
+ std::unique_ptr<ValueMirror> exceptionMirror;
+ bool writable = false;
+ bool enumerable = false;
+ bool configurable = false;
+
+ bool isAccessorProperty = false;
+ v8::TryCatch tryCatch(isolate);
+ if (!iterator->attributes().To(&attributes)) {
+ exceptionMirror = ValueMirror::create(context, tryCatch.Exception());
+ } else {
+ if (iterator->is_native_accessor()) {
+ if (iterator->has_native_getter()) {
+ getterMirror = createNativeGetter(context, object, v8Name);
+ }
+ if (iterator->has_native_setter()) {
+ setterMirror = createNativeSetter(context, object, v8Name);
+ }
+ writable = !(attributes & v8::PropertyAttribute::ReadOnly);
+ enumerable = !(attributes & v8::PropertyAttribute::DontEnum);
+ configurable = !(attributes & v8::PropertyAttribute::DontDelete);
+ isAccessorProperty = getterMirror || setterMirror;
+ } else {
+ v8::TryCatch tryCatch(isolate);
+ v8::debug::PropertyDescriptor descriptor;
+ if (!iterator->descriptor().To(&descriptor)) {
+ exceptionMirror = ValueMirror::create(context, tryCatch.Exception());
+ } else {
+ writable = descriptor.has_writable ? descriptor.writable : false;
+ enumerable =
+ descriptor.has_enumerable ? descriptor.enumerable : false;
+ configurable =
+ descriptor.has_configurable ? descriptor.configurable : false;
+ if (!descriptor.value.IsEmpty()) {
+ valueMirror = ValueMirror::create(context, descriptor.value);
+ }
+ bool getterIsNativeFunction = false;
+ if (!descriptor.get.IsEmpty()) {
+ v8::Local<v8::Value> get = descriptor.get;
+ getterMirror = ValueMirror::create(context, get);
+ getterIsNativeFunction =
+ get->IsFunction() && get.As<v8::Function>()->ScriptId() ==
+ v8::UnboundScript::kNoScriptId;
+ }
+ if (!descriptor.set.IsEmpty()) {
+ setterMirror = ValueMirror::create(context, descriptor.set);
+ }
+ isAccessorProperty = getterMirror || setterMirror;
+ bool isSymbolDescription =
+ object->IsSymbol() && name == "description";
+ if (isSymbolDescription ||
+ (name != "__proto__" && getterIsNativeFunction &&
+ formatAccessorsAsProperties &&
+ !doesAttributeHaveObservableSideEffectOnGet(context, object,
+ v8Name))) {
+ v8::TryCatch tryCatch(isolate);
+ v8::Local<v8::Value> value;
+ if (object->Get(context, v8Name).ToLocal(&value)) {
+ valueMirror = ValueMirror::create(context, value);
+ isOwn = true;
+ setterMirror = nullptr;
+ getterMirror = nullptr;
+ }
+ }
+ }
+ }
+ }
+ if (accessorPropertiesOnly && !isAccessorProperty) continue;
+ auto mirror = PropertyMirror{name,
+ writable,
+ configurable,
+ enumerable,
+ isOwn,
+ iterator->is_array_index(),
+ std::move(valueMirror),
+ std::move(getterMirror),
+ std::move(setterMirror),
+ std::move(symbolMirror),
+ std::move(exceptionMirror)};
+ if (!accumulator->Add(std::move(mirror))) return true;
+ }
+ if (!shouldSkipProto && ownProperties && !object->IsProxy() &&
+ !accessorPropertiesOnly) {
+ v8::Local<v8::Value> prototype = object->GetPrototype();
+ if (prototype->IsObject()) {
+ accumulator->Add(PropertyMirror{String16("__proto__"), true, true, false,
+ true, false,
+ ValueMirror::create(context, prototype),
+ nullptr, nullptr, nullptr, nullptr});
+ }
+ }
+ return true;
+}
+
+// static
+void ValueMirror::getInternalProperties(
+ v8::Local<v8::Context> context, v8::Local<v8::Object> object,
+ std::vector<InternalPropertyMirror>* mirrors) {
+ v8::Isolate* isolate = context->GetIsolate();
+ v8::MicrotasksScope microtasksScope(isolate,
+ v8::MicrotasksScope::kDoNotRunMicrotasks);
+ v8::TryCatch tryCatch(isolate);
+ if (object->IsFunction()) {
+ v8::Local<v8::Function> function = object.As<v8::Function>();
+ auto location = LocationMirror::create(function);
+ if (location) {
+ mirrors->emplace_back(InternalPropertyMirror{
+ String16("[[FunctionLocation]]"), std::move(location)});
+ }
+ if (function->IsGeneratorFunction()) {
+ mirrors->emplace_back(InternalPropertyMirror{
+ String16("[[IsGenerator]]"),
+ ValueMirror::create(context, v8::True(context->GetIsolate()))});
+ }
+ }
+ if (object->IsGeneratorObject()) {
+ auto location = LocationMirror::createForGenerator(object);
+ if (location) {
+ mirrors->emplace_back(InternalPropertyMirror{
+ String16("[[GeneratorLocation]]"), std::move(location)});
+ }
+ }
+ V8Debugger* debugger =
+ static_cast<V8InspectorImpl*>(v8::debug::GetInspector(isolate))
+ ->debugger();
+ v8::Local<v8::Array> properties;
+ if (debugger->internalProperties(context, object).ToLocal(&properties)) {
+ for (uint32_t i = 0; i < properties->Length(); i += 2) {
+ v8::Local<v8::Value> name;
+ if (!properties->Get(context, i).ToLocal(&name) || !name->IsString()) {
+ tryCatch.Reset();
+ continue;
+ }
+ v8::Local<v8::Value> value;
+ if (!properties->Get(context, i + 1).ToLocal(&value)) {
+ tryCatch.Reset();
+ continue;
+ }
+ auto wrapper = ValueMirror::create(context, value);
+ if (wrapper) {
+ mirrors->emplace_back(InternalPropertyMirror{
+ toProtocolStringWithTypeCheck(context->GetIsolate(), name),
+ std::move(wrapper)});
+ }
+ }
+ }
+}
+
+String16 descriptionForNode(v8::Local<v8::Context> context,
+ v8::Local<v8::Value> value) {
+ if (!value->IsObject()) return String16();
+ v8::Local<v8::Object> object = value.As<v8::Object>();
+ v8::Isolate* isolate = context->GetIsolate();
+ v8::TryCatch tryCatch(isolate);
+ v8::Local<v8::Value> nodeName;
+ if (!object->Get(context, toV8String(isolate, "nodeName"))
+ .ToLocal(&nodeName)) {
+ return String16();
+ }
+ String16 description;
+ v8::Local<v8::Function> toLowerCase =
+ v8::debug::GetBuiltin(isolate, v8::debug::kStringToLowerCase);
+ if (nodeName->IsString()) {
+ if (!toLowerCase->Call(context, nodeName, 0, nullptr).ToLocal(&nodeName))
+ return String16();
+ if (nodeName->IsString()) {
+ description = toProtocolString(isolate, nodeName.As<v8::String>());
+ }
+ }
+ if (!description.length()) {
+ v8::Local<v8::Value> value;
+ if (!object->Get(context, toV8String(isolate, "constructor"))
+ .ToLocal(&value) ||
+ !value->IsObject()) {
+ return String16();
+ }
+ if (!value.As<v8::Object>()
+ ->Get(context, toV8String(isolate, "name"))
+ .ToLocal(&value) ||
+ !value->IsString()) {
+ return String16();
+ }
+ description = toProtocolString(isolate, value.As<v8::String>());
+ }
+ v8::Local<v8::Value> nodeType;
+ if (!object->Get(context, toV8String(isolate, "nodeType"))
+ .ToLocal(&nodeType) ||
+ !nodeType->IsInt32()) {
+ return description;
+ }
+ if (nodeType.As<v8::Int32>()->Value() == 1) {
+ v8::Local<v8::Value> idValue;
+ if (!object->Get(context, toV8String(isolate, "id")).ToLocal(&idValue)) {
+ return description;
+ }
+ if (idValue->IsString()) {
+ String16 id = toProtocolString(isolate, idValue.As<v8::String>());
+ if (id.length()) {
+ description = String16::concat(description, '#', id);
+ }
+ }
+ v8::Local<v8::Value> classNameValue;
+ if (!object->Get(context, toV8String(isolate, "className"))
+ .ToLocal(&classNameValue)) {
+ return description;
+ }
+ if (classNameValue->IsString() &&
+ classNameValue.As<v8::String>()->Length()) {
+ String16 classes =
+ toProtocolString(isolate, classNameValue.As<v8::String>());
+ String16Builder output;
+ bool previousIsDot = false;
+ for (size_t i = 0; i < classes.length(); ++i) {
+ if (classes[i] == ' ') {
+ if (!previousIsDot) {
+ output.append('.');
+ previousIsDot = true;
+ }
+ } else {
+ output.append(classes[i]);
+ previousIsDot = classes[i] == '.';
+ }
+ }
+ description = String16::concat(description, '.', output.toString());
+ }
+ } else if (nodeType.As<v8::Int32>()->Value() == 1) {
+ return String16::concat("<!DOCTYPE ", description, '>');
+ }
+ return description;
+}
+
+std::unique_ptr<ValueMirror> clientMirror(v8::Local<v8::Context> context,
+ v8::Local<v8::Value> value,
+ const String16& subtype) {
+ // TODO(alph): description and length retrieval should move to embedder.
+ if (subtype == "node") {
+ return v8::base::make_unique<ObjectMirror>(
+ value, subtype, descriptionForNode(context, value));
+ }
+ if (subtype == "error") {
+ return v8::base::make_unique<ObjectMirror>(
+ value, RemoteObject::SubtypeEnum::Error,
+ descriptionForError(context, value.As<v8::Object>(),
+ ErrorType::kClient));
+ }
+ if (subtype == "array" && value->IsObject()) {
+ v8::Isolate* isolate = context->GetIsolate();
+ v8::TryCatch tryCatch(isolate);
+ v8::Local<v8::Object> object = value.As<v8::Object>();
+ v8::Local<v8::Value> lengthValue;
+ if (object->Get(context, toV8String(isolate, "length"))
+ .ToLocal(&lengthValue)) {
+ if (lengthValue->IsInt32()) {
+ return v8::base::make_unique<ObjectMirror>(
+ value, RemoteObject::SubtypeEnum::Array,
+ descriptionForCollection(isolate, object,
+ lengthValue.As<v8::Int32>()->Value()));
+ }
+ }
+ }
+ return v8::base::make_unique<ObjectMirror>(
+ value,
+ descriptionForObject(context->GetIsolate(), value.As<v8::Object>()));
+}
+
+std::unique_ptr<ValueMirror> ValueMirror::create(v8::Local<v8::Context> context,
+ v8::Local<v8::Value> value) {
+ if (value->IsNull()) {
+ return v8::base::make_unique<PrimitiveValueMirror>(
+ value, RemoteObject::TypeEnum::Object);
+ }
+ if (value->IsBoolean()) {
+ return v8::base::make_unique<PrimitiveValueMirror>(
+ value, RemoteObject::TypeEnum::Boolean);
+ }
+ if (value->IsNumber()) {
+ return v8::base::make_unique<NumberMirror>(value.As<v8::Number>());
+ }
+ v8::Isolate* isolate = context->GetIsolate();
+ if (value->IsString()) {
+ return v8::base::make_unique<PrimitiveValueMirror>(
+ value, RemoteObject::TypeEnum::String);
+ }
+ if (value->IsBigInt()) {
+ return v8::base::make_unique<BigIntMirror>(value.As<v8::BigInt>());
+ }
+ if (value->IsSymbol()) {
+ return v8::base::make_unique<SymbolMirror>(value.As<v8::Symbol>());
+ }
+ auto clientSubtype = (value->IsUndefined() || value->IsObject())
+ ? clientFor(context)->valueSubtype(value)
+ : nullptr;
+ if (clientSubtype) {
+ String16 subtype = toString16(clientSubtype->string());
+ return clientMirror(context, value, subtype);
+ }
+ if (value->IsUndefined()) {
+ return v8::base::make_unique<PrimitiveValueMirror>(
+ value, RemoteObject::TypeEnum::Undefined);
+ }
+ if (value->IsRegExp()) {
+ return v8::base::make_unique<ObjectMirror>(
+ value, RemoteObject::SubtypeEnum::Regexp,
+ descriptionForRegExp(isolate, value.As<v8::RegExp>()));
+ }
+ if (value->IsFunction()) {
+ return v8::base::make_unique<FunctionMirror>(value);
+ }
+ if (value->IsProxy()) {
+ return v8::base::make_unique<ObjectMirror>(
+ value, RemoteObject::SubtypeEnum::Proxy, "Proxy");
+ }
+ if (value->IsDate()) {
+ return v8::base::make_unique<ObjectMirror>(
+ value, RemoteObject::SubtypeEnum::Date,
+ descriptionForDate(context, value.As<v8::Date>()));
+ }
+ if (value->IsPromise()) {
+ v8::Local<v8::Promise> promise = value.As<v8::Promise>();
+ return v8::base::make_unique<ObjectMirror>(
+ promise, RemoteObject::SubtypeEnum::Promise,
+ descriptionForObject(isolate, promise));
+ }
+ if (value->IsNativeError()) {
+ return v8::base::make_unique<ObjectMirror>(
+ value, RemoteObject::SubtypeEnum::Error,
+ descriptionForError(context, value.As<v8::Object>(),
+ ErrorType::kNative));
+ }
+ if (value->IsMap()) {
+ v8::Local<v8::Map> map = value.As<v8::Map>();
+ return v8::base::make_unique<ObjectMirror>(
+ value, RemoteObject::SubtypeEnum::Map,
+ descriptionForCollection(isolate, map, map->Size()));
+ }
+ if (value->IsSet()) {
+ v8::Local<v8::Set> set = value.As<v8::Set>();
+ return v8::base::make_unique<ObjectMirror>(
+ value, RemoteObject::SubtypeEnum::Set,
+ descriptionForCollection(isolate, set, set->Size()));
+ }
+ if (value->IsWeakMap()) {
+ return v8::base::make_unique<ObjectMirror>(
+ value, RemoteObject::SubtypeEnum::Weakmap,
+ descriptionForObject(isolate, value.As<v8::Object>()));
+ }
+ if (value->IsWeakSet()) {
+ return v8::base::make_unique<ObjectMirror>(
+ value, RemoteObject::SubtypeEnum::Weakset,
+ descriptionForObject(isolate, value.As<v8::Object>()));
+ }
+ if (value->IsMapIterator() || value->IsSetIterator()) {
+ return v8::base::make_unique<ObjectMirror>(
+ value, RemoteObject::SubtypeEnum::Iterator,
+ descriptionForObject(isolate, value.As<v8::Object>()));
+ }
+ if (value->IsGeneratorObject()) {
+ v8::Local<v8::Object> object = value.As<v8::Object>();
+ return v8::base::make_unique<ObjectMirror>(
+ object, RemoteObject::SubtypeEnum::Generator,
+ descriptionForObject(isolate, object));
+ }
+ if (value->IsTypedArray()) {
+ v8::Local<v8::TypedArray> array = value.As<v8::TypedArray>();
+ return v8::base::make_unique<ObjectMirror>(
+ value, RemoteObject::SubtypeEnum::Typedarray,
+ descriptionForCollection(isolate, array, array->Length()));
+ }
+ if (value->IsArrayBuffer()) {
+ v8::Local<v8::ArrayBuffer> buffer = value.As<v8::ArrayBuffer>();
+ return v8::base::make_unique<ObjectMirror>(
+ value, RemoteObject::SubtypeEnum::Arraybuffer,
+ descriptionForCollection(isolate, buffer, buffer->ByteLength()));
+ }
+ if (value->IsSharedArrayBuffer()) {
+ v8::Local<v8::SharedArrayBuffer> buffer = value.As<v8::SharedArrayBuffer>();
+ return v8::base::make_unique<ObjectMirror>(
+ value, RemoteObject::SubtypeEnum::Arraybuffer,
+ descriptionForCollection(isolate, buffer, buffer->ByteLength()));
+ }
+ if (value->IsDataView()) {
+ v8::Local<v8::DataView> view = value.As<v8::DataView>();
+ return v8::base::make_unique<ObjectMirror>(
+ value, RemoteObject::SubtypeEnum::Dataview,
+ descriptionForCollection(isolate, view, view->ByteLength()));
+ }
+ V8InternalValueType internalType =
+ v8InternalValueTypeFrom(context, v8::Local<v8::Object>::Cast(value));
+ if (value->IsArray() && internalType == V8InternalValueType::kScopeList) {
+ return v8::base::make_unique<ObjectMirror>(
+ value, "internal#scopeList",
+ descriptionForScopeList(value.As<v8::Array>()));
+ }
+ if (value->IsObject() && internalType == V8InternalValueType::kEntry) {
+ return v8::base::make_unique<ObjectMirror>(
+ value, "internal#entry",
+ descriptionForEntry(context, value.As<v8::Object>()));
+ }
+ if (value->IsObject() && internalType == V8InternalValueType::kScope) {
+ return v8::base::make_unique<ObjectMirror>(
+ value, "internal#scope",
+ descriptionForScope(context, value.As<v8::Object>()));
+ }
+ size_t length = 0;
+ if (value->IsArray() || isArrayLike(context, value, &length)) {
+ length = value->IsArray() ? value.As<v8::Array>()->Length() : length;
+ return v8::base::make_unique<ObjectMirror>(
+ value, RemoteObject::SubtypeEnum::Array,
+ descriptionForCollection(isolate, value.As<v8::Object>(), length));
+ }
+ if (value->IsObject()) {
+ return v8::base::make_unique<ObjectMirror>(
+ value, descriptionForObject(isolate, value.As<v8::Object>()));
+ }
+ return nullptr;
+}
+} // namespace v8_inspector