msggen support for cdp previews (#36781)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/36781

----

Add support to msggen for types like:

* Runtime.CustomPreview
* Runtime.EntryPreview
* Runtime.ObjectPreview
* Runtime.PropertyPreview

And their related use as properties.

There was quite a gap here. The work involves:

* Upgrade devtools-protocol to 0.0.1107588 even pick up the schema definitions in the first place.
* Next problem: all the preview stuff is experimental. Had to add support to --include-experimental flag for targeting types AND properties.
* Next problem: the protocol schema for previews is cyclical. ObjectPreview refers to PropertyPreview and EntryPreview, which both refer back to ObjectPreview. msggen only allowed for DAGs. Added support for allowing cycles.
* Next problem: Forward declarations are not enough to compensate for the cyclical references, because of the incomplete type definitions. This breaks optional, vector, and simple containment. To address this:
  * In the graph traversal code, where before we would error if the graph had a cycle, we allow for the cycle, but record the cyclical reference from a property on Type B's to Type A.
  * Whenever we emit the definition of Type B, its references to Type A are not naked, they are now wrapped in a unique_ptr.
  * However, because the unique_ptr only has a forward declaration, and not a complete type, we need to specify a custom deleter.
* Next problem: There are lots of cases where these types that now contain unique_ptrs were being copied, which no longer works. So, make all these codegen'd types move-only, and change a few places where were copying root objects, to move them instead.
* Many tests required changes to avoid copy construction/assignment that were occuring. MessageTypes are not copyable construcrtible/assignable now.

Changelog: [Internal]

Reviewed By: jpporto

Differential Revision: D43553742

fbshipit-source-id: 1e4c495aa600feb6f1901e6bc013d517ba8d8a2d
This commit is contained in:
Brian Long
2023-04-04 14:17:43 -07:00
committed by Facebook GitHub Bot
parent aa15fd44a6
commit de35e2881e
16 changed files with 475 additions and 74 deletions
@@ -13,7 +13,7 @@
"test": "jest"
},
"dependencies": {
"devtools-protocol": "0.0.959523",
"devtools-protocol": "0.0.1107588",
"yargs": "^17.6.2"
},
"devDependencies": {
@@ -23,26 +23,38 @@ export class Command {
domain: string,
obj: any,
ignoreExperimental: boolean,
includeExperimental: Set<string>,
): ?Command {
return ignoreExperimental && obj.experimental
return ignoreExperimental &&
obj.experimental &&
!includeExperimental.has(domain + '.' + obj.name)
? null
: new Command(domain, obj, ignoreExperimental);
: new Command(domain, obj, ignoreExperimental, includeExperimental);
}
constructor(domain: string, obj: any, ignoreExperimental: boolean) {
constructor(
domain: string,
obj: any,
ignoreExperimental: boolean,
includeExperimental: Set<string>,
) {
this.domain = domain;
this.name = obj.name;
this.description = obj.description;
this.experimental = obj.experimental;
this.parameters = Property.createArray(
domain,
obj.name,
obj.parameters || [],
ignoreExperimental,
includeExperimental,
);
this.returns = Property.createArray(
domain,
obj.name,
obj.returns || [],
ignoreExperimental,
includeExperimental,
);
}
+18 -4
View File
@@ -18,20 +18,34 @@ export class Event {
experimental: ?boolean;
parameters: Array<Property>;
static create(domain: string, obj: any, ignoreExperimental: boolean): ?Event {
return ignoreExperimental && obj.experimental
static create(
domain: string,
obj: any,
ignoreExperimental: boolean,
includeExperimental: Set<string>,
): ?Event {
return ignoreExperimental &&
obj.experimental &&
!includeExperimental.has(domain + '.' + obj.name)
? null
: new Event(domain, obj, ignoreExperimental);
: new Event(domain, obj, ignoreExperimental, includeExperimental);
}
constructor(domain: string, obj: any, ignoreExperimental: boolean) {
constructor(
domain: string,
obj: any,
ignoreExperimental: boolean,
includeExperimental: Set<string>,
) {
this.domain = domain;
this.name = obj.name;
this.description = obj.description;
this.parameters = Property.createArray(
domain,
obj.name,
obj.parameters || [],
ignoreExperimental,
includeExperimental,
);
}
+18 -10
View File
@@ -11,6 +11,7 @@
import invariant from 'assert';
type NodeId = string;
type NodeCycle = {from: NodeId, to: NodeId};
class Node {
id: NodeId;
@@ -49,7 +50,10 @@ export class Graph {
// traverse returns all nodes in the graph reachable from the given rootIds.
// the returned nodes are topologically sorted, with the deepest nodes
// returned first.
traverse(rootIds: Array<NodeId>): Array<NodeId> {
traverse(rootIds: Array<NodeId>): {
nodes: Array<NodeId>,
cycles: Array<NodeCycle>,
} {
// clear marks
for (const node of this.nodes.values()) {
node.state = 'none';
@@ -63,28 +67,32 @@ export class Graph {
root.children.add(node);
}
const output: Array<NodeId> = [];
postorder(root, output);
const nodes: Array<NodeId> = [];
const cycles: Array<NodeCycle> = [];
postorder(root, nodes, cycles);
// remove fake root node
output.splice(-1);
nodes.splice(-1);
return output;
return {nodes: nodes, cycles: cycles};
}
}
function postorder(node: Node, output: Array<NodeId>) {
function postorder(node: Node, nodes: Array<NodeId>, cycles: Array<NodeCycle>) {
if (node.state === 'visited') {
return;
}
invariant(node.state !== 'visiting', `Not a DAG: cycle involving ${node.id}`);
node.state = 'visiting';
for (const child of node.children) {
postorder(child, output);
if (child.state === 'visiting') {
// This will lead to a cycle; we need to mark that and move on
cycles.push({from: node.id, to: child.id});
} else {
postorder(child, nodes, cycles);
}
}
node.state = 'visited';
output.push(node.id);
nodes.push(node.id);
}
@@ -62,6 +62,8 @@ export class HeaderWriter {
namespace chrome {
namespace message {
template<typename T>
void deleter(T* p);
`);
}
@@ -222,8 +224,12 @@ export function emitTypeDecl(stream: Writable, type: PropsType) {
stream.write(`struct ${cppNs}::${cppType} : public Serializable {
${cppType}() = default;
${cppType}(${cppType}&&) = default;
${cppType}(const ${cppType}&) = delete;
explicit ${cppType}(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
${cppType}& operator=(const ${cppType}&) = delete;
${cppType}& operator=(${cppType}&&) = default;
`);
if (type instanceof PropsType) {
@@ -34,12 +34,19 @@ export class Property {
static createArray(
domain: string,
parent: string,
elements: Array<any>,
ignoreExperimental: boolean,
includeExperimental: Set<string>,
): Array<Property> {
let props = elements.map(elem => Property.create(domain, elem));
if (ignoreExperimental) {
props = props.filter(prop => !prop.experimental);
props = props.filter(prop => {
return (
!prop.experimental ||
includeExperimental.has(domain + '.' + parent + '.' + prop.name)
);
});
}
return props;
}
@@ -78,8 +85,14 @@ function maybeWrapOptional(
type: string,
optional: ?boolean,
recursive: ?boolean,
cyclical: ?boolean,
) {
if (optional) {
if (cyclical) {
// n.b. need to use unique_ptr with a custom deleter since
// due to the type cycle we only have the fwd def here and
// the type is incomplete
return `std::unique_ptr<${type}, std::function<void(${type}*)>>`;
} else if (optional) {
return recursive ? `std::unique_ptr<${type}>` : `std::optional<${type}>`;
}
return type;
@@ -150,11 +163,13 @@ class PrimitiveProperty extends Property {
class RefProperty extends Property {
$ref: string;
recursive: ?boolean;
cyclical: ?boolean;
constructor(domain: string, obj: any) {
super(domain, obj);
this.$ref = obj.$ref;
this.recursive = obj.recursive;
this.cyclical = obj.cyclical;
}
getRefDebuggerName(): ?string {
@@ -164,13 +179,23 @@ class RefProperty extends Property {
getFullCppType(): string {
const fullCppType = toFullCppType(this.domain, this.$ref);
return maybeWrapOptional(`${fullCppType}`, this.optional, this.recursive);
return maybeWrapOptional(
`${fullCppType}`,
this.optional,
this.recursive,
this.cyclical,
);
}
getInitializer(): string {
// must zero-init non-optional ref props since the ref could just be an
// alias to a C++ primitive type like int which we always want to zero-init
return this.optional ? '' : '{}';
const fullCppType = toFullCppType(this.domain, this.$ref);
return this.cyclical
? `{nullptr, deleter<${fullCppType}>}`
: this.optional
? ''
: '{}';
}
}
+23 -4
View File
@@ -18,11 +18,21 @@ export class Type {
exported: ?boolean;
experimental: ?boolean;
static create(domain: string, obj: any, ignoreExperimental: boolean): ?Type {
static create(
domain: string,
obj: any,
ignoreExperimental: boolean,
includeExperimental: Set<string>,
): ?Type {
let type = null;
if (obj.type === 'object' && obj.properties) {
type = new PropsType(domain, obj, ignoreExperimental);
type = new PropsType(
domain,
obj,
ignoreExperimental,
includeExperimental,
);
} else if (obj.type) {
type = new PrimitiveType(domain, obj, ignoreExperimental);
} else {
@@ -30,7 +40,9 @@ export class Type {
}
if (ignoreExperimental && type.experimental) {
type = null;
if (!includeExperimental.has(domain + '.' + type.id)) {
type = null;
}
}
return type;
@@ -82,13 +94,20 @@ export class PropsType extends Type {
type: 'object';
properties: Array<Property>;
constructor(domain: string, obj: any, ignoreExperimental: boolean) {
constructor(
domain: string,
obj: any,
ignoreExperimental: boolean,
includeExperimental: Set<string>,
) {
super(domain, obj);
this.type = obj.type;
this.properties = Property.createArray(
domain,
obj.id,
obj.properties || [],
ignoreExperimental,
includeExperimental,
);
}
+38 -3
View File
@@ -53,7 +53,12 @@ function parseDomains(
const domain = obj.domain;
for (const typeObj of obj.types || []) {
const type = Type.create(domain, typeObj, ignoreExperimental);
const type = Type.create(
domain,
typeObj,
ignoreExperimental,
includeExperimental,
);
if (type) {
desc.types.push(type);
}
@@ -65,6 +70,7 @@ function parseDomains(
commandObj,
!includeExperimental.has(`${domain}.${commandObj.name}`) &&
ignoreExperimental,
includeExperimental,
);
if (command) {
desc.commands.push(command);
@@ -72,7 +78,12 @@ function parseDomains(
}
for (const eventObj of obj.events || []) {
const event = Event.create(domain, eventObj, ignoreExperimental);
const event = Event.create(
domain,
eventObj,
ignoreExperimental,
includeExperimental,
);
if (event) {
desc.events.push(event);
}
@@ -161,7 +172,8 @@ function filterReachableFromRoots(
graph: Graph,
roots: Array<string>,
): Descriptor {
const topoSortedIds = graph.traverse(roots);
const traversal = graph.traverse(roots);
const topoSortedIds = traversal.nodes;
// Types can include other types by value, so they need to be topologically
// sorted in the header.
@@ -178,6 +190,29 @@ function filterReachableFromRoots(
}
}
const cycles: Set<string> = new Set();
for (const cycle of traversal.cycles) {
const from = typeMap.get(cycle.from);
const to = typeMap.get(cycle.to);
if (from && to) {
cycles.add(from.id + ' > ' + to.id);
}
}
for (const type of types) {
if (type instanceof PropsType) {
for (const prop of type.properties) {
const ref = (prop: Object).$ref;
if (ref) {
const cycleKey = `${type.id} > ${ref}`;
if (cycles.has(cycleKey)) {
(prop: Object).cyclical = true;
}
}
}
}
}
// Commands and events don't depend on each other, so just emit them in the
// order we got them from the JSON file.
const ids = new Set(topoSortedIds);
@@ -482,7 +482,7 @@ void Connection::Impl::handle(
resp.exceptionDetails =
m::runtime::makeExceptionDetails(result.exceptionDetails);
} else {
resp.result = *remoteObjPtr;
resp.result = std::move(*remoteObjPtr);
}
sendResponseToClient(resp);
@@ -642,7 +642,7 @@ void Connection::Impl::handle(const m::heapProfiler::StopSamplingRequest &req) {
m::heapProfiler::StopSamplingResponse resp;
resp.id = id;
m::heapProfiler::SamplingHeapProfile profile{json};
resp.profile = profile;
resp.profile = std::move(profile);
sendResponseToClient(resp);
})
.via(executor_.get())
@@ -690,7 +690,7 @@ void Connection::Impl::handle(
if (!remoteObjPtr->type.empty()) {
m::heapProfiler::GetObjectByHeapObjectIdResponse resp;
resp.id = id;
resp.result = *remoteObjPtr;
resp.result = std::move(*remoteObjPtr);
sendResponseToClient(resp);
} else {
sendResponseToClient(m::makeErrorResponse(
@@ -1075,7 +1075,7 @@ void Connection::Impl::handle(const m::runtime::CallFunctionOnRequest &req) {
resp.exceptionDetails =
m::runtime::makeExceptionDetails(result.exceptionDetails);
} else {
resp.result = *remoteObjPtr;
resp.result = std::move(*remoteObjPtr);
}
sendResponseToClient(resp);
@@ -1149,7 +1149,7 @@ void Connection::Impl::handle(const m::runtime::EvaluateRequest &req) {
resp.exceptionDetails =
m::runtime::makeExceptionDetails(result.exceptionDetails);
} else {
resp.result = *remoteObjPtr;
resp.result = std::move(*remoteObjPtr);
}
sendResponseToClient(resp);
@@ -1,5 +1,5 @@
// Copyright (c) Meta Platforms, Inc. and affiliates. All Rights Reserved.
// @generated SignedSource<<8397f2428e68a26e014f91fdb77c1eb3>>
// @generated SignedSource<<499c1dc13c66e60377fd3e29460a2321>>
#include "MessageTypes.h"
@@ -106,6 +106,72 @@ dynamic debugger::Location::toDynamic() const {
return obj;
}
runtime::PropertyPreview::PropertyPreview(const dynamic &obj) {
assign(name, obj, "name");
assign(type, obj, "type");
assign(value, obj, "value");
assign(valuePreview, obj, "valuePreview");
assign(subtype, obj, "subtype");
}
dynamic runtime::PropertyPreview::toDynamic() const {
dynamic obj = dynamic::object;
put(obj, "name", name);
put(obj, "type", type);
put(obj, "value", value);
put(obj, "valuePreview", valuePreview);
put(obj, "subtype", subtype);
return obj;
}
runtime::EntryPreview::EntryPreview(const dynamic &obj) {
assign(key, obj, "key");
assign(value, obj, "value");
}
dynamic runtime::EntryPreview::toDynamic() const {
dynamic obj = dynamic::object;
put(obj, "key", key);
put(obj, "value", value);
return obj;
}
runtime::ObjectPreview::ObjectPreview(const dynamic &obj) {
assign(type, obj, "type");
assign(subtype, obj, "subtype");
assign(description, obj, "description");
assign(overflow, obj, "overflow");
assign(properties, obj, "properties");
assign(entries, obj, "entries");
}
dynamic runtime::ObjectPreview::toDynamic() const {
dynamic obj = dynamic::object;
put(obj, "type", type);
put(obj, "subtype", subtype);
put(obj, "description", description);
put(obj, "overflow", overflow);
put(obj, "properties", properties);
put(obj, "entries", entries);
return obj;
}
runtime::CustomPreview::CustomPreview(const dynamic &obj) {
assign(header, obj, "header");
assign(bodyGetterId, obj, "bodyGetterId");
}
dynamic runtime::CustomPreview::toDynamic() const {
dynamic obj = dynamic::object;
put(obj, "header", header);
put(obj, "bodyGetterId", bodyGetterId);
return obj;
}
runtime::RemoteObject::RemoteObject(const dynamic &obj) {
assign(type, obj, "type");
assign(subtype, obj, "subtype");
@@ -114,6 +180,8 @@ runtime::RemoteObject::RemoteObject(const dynamic &obj) {
assign(unserializableValue, obj, "unserializableValue");
assign(description, obj, "description");
assign(objectId, obj, "objectId");
assign(preview, obj, "preview");
assign(customPreview, obj, "customPreview");
}
dynamic runtime::RemoteObject::toDynamic() const {
@@ -126,6 +194,8 @@ dynamic runtime::RemoteObject::toDynamic() const {
put(obj, "unserializableValue", unserializableValue);
put(obj, "description", description);
put(obj, "objectId", objectId);
put(obj, "preview", preview);
put(obj, "customPreview", customPreview);
return obj;
}
@@ -485,6 +555,7 @@ debugger::EvaluateOnCallFrameRequest::EvaluateOnCallFrameRequest(
assign(includeCommandLineAPI, params, "includeCommandLineAPI");
assign(silent, params, "silent");
assign(returnByValue, params, "returnByValue");
assign(generatePreview, params, "generatePreview");
assign(throwOnSideEffect, params, "throwOnSideEffect");
}
@@ -496,6 +567,7 @@ dynamic debugger::EvaluateOnCallFrameRequest::toDynamic() const {
put(params, "includeCommandLineAPI", includeCommandLineAPI);
put(params, "silent", silent);
put(params, "returnByValue", returnByValue);
put(params, "generatePreview", generatePreview);
put(params, "throwOnSideEffect", throwOnSideEffect);
dynamic obj = dynamic::object;
@@ -890,12 +962,26 @@ heapProfiler::StartSamplingRequest::StartSamplingRequest(const dynamic &obj)
if (it != obj.items().end()) {
dynamic params = it->second;
assign(samplingInterval, params, "samplingInterval");
assign(
includeObjectsCollectedByMajorGC,
params,
"includeObjectsCollectedByMajorGC");
assign(
includeObjectsCollectedByMinorGC,
params,
"includeObjectsCollectedByMinorGC");
}
}
dynamic heapProfiler::StartSamplingRequest::toDynamic() const {
dynamic params = dynamic::object;
put(params, "samplingInterval", samplingInterval);
put(params,
"includeObjectsCollectedByMajorGC",
includeObjectsCollectedByMajorGC);
put(params,
"includeObjectsCollectedByMinorGC",
includeObjectsCollectedByMinorGC);
dynamic obj = dynamic::object;
put(obj, "id", id);
@@ -1084,6 +1170,7 @@ runtime::CallFunctionOnRequest::CallFunctionOnRequest(const dynamic &obj)
assign(arguments, params, "arguments");
assign(silent, params, "silent");
assign(returnByValue, params, "returnByValue");
assign(generatePreview, params, "generatePreview");
assign(userGesture, params, "userGesture");
assign(awaitPromise, params, "awaitPromise");
assign(executionContextId, params, "executionContextId");
@@ -1097,6 +1184,7 @@ dynamic runtime::CallFunctionOnRequest::toDynamic() const {
put(params, "arguments", arguments);
put(params, "silent", silent);
put(params, "returnByValue", returnByValue);
put(params, "generatePreview", generatePreview);
put(params, "userGesture", userGesture);
put(params, "awaitPromise", awaitPromise);
put(params, "executionContextId", executionContextId);
@@ -1160,6 +1248,7 @@ runtime::EvaluateRequest::EvaluateRequest(const dynamic &obj)
assign(silent, params, "silent");
assign(contextId, params, "contextId");
assign(returnByValue, params, "returnByValue");
assign(generatePreview, params, "generatePreview");
assign(userGesture, params, "userGesture");
assign(awaitPromise, params, "awaitPromise");
}
@@ -1172,6 +1261,7 @@ dynamic runtime::EvaluateRequest::toDynamic() const {
put(params, "silent", silent);
put(params, "contextId", contextId);
put(params, "returnByValue", returnByValue);
put(params, "generatePreview", generatePreview);
put(params, "userGesture", userGesture);
put(params, "awaitPromise", awaitPromise);
@@ -1217,12 +1307,14 @@ runtime::GetPropertiesRequest::GetPropertiesRequest(const dynamic &obj)
dynamic params = obj.at("params");
assign(objectId, params, "objectId");
assign(ownProperties, params, "ownProperties");
assign(generatePreview, params, "generatePreview");
}
dynamic runtime::GetPropertiesRequest::toDynamic() const {
dynamic params = dynamic::object;
put(params, "objectId", objectId);
put(params, "ownProperties", ownProperties);
put(params, "generatePreview", generatePreview);
dynamic obj = dynamic::object;
put(obj, "id", id);
@@ -1,5 +1,5 @@
// Copyright (c) Meta Platforms, Inc. and affiliates. All Rights Reserved.
// @generated SignedSource<<521cb6eba7e5df060b4836f0f60126b9>>
// @generated SignedSource<<0c28a6bdf52b46bbad601080f35e98ee>>
#pragma once
@@ -14,6 +14,8 @@ namespace inspector {
namespace chrome {
namespace message {
template <typename T>
void deleter(T *p);
struct UnknownRequest;
namespace debugger {
@@ -54,6 +56,8 @@ struct CallFunctionOnResponse;
struct CompileScriptRequest;
struct CompileScriptResponse;
struct ConsoleAPICalledNotification;
struct CustomPreview;
struct EntryPreview;
struct EvaluateRequest;
struct EvaluateResponse;
struct ExceptionDetails;
@@ -67,7 +71,9 @@ struct GetPropertiesResponse;
struct GlobalLexicalScopeNamesRequest;
struct GlobalLexicalScopeNamesResponse;
struct InternalPropertyDescriptor;
struct ObjectPreview;
struct PropertyDescriptor;
struct PropertyPreview;
struct RemoteObject;
using RemoteObjectId = std::string;
struct RunIfWaitingForDebuggerRequest;
@@ -193,18 +199,94 @@ struct NoopRequestHandler : public RequestHandler {
/// Types
struct debugger::Location : public Serializable {
Location() = default;
Location(Location &&) = default;
Location(const Location &) = delete;
explicit Location(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
Location &operator=(const Location &) = delete;
Location &operator=(Location &&) = default;
runtime::ScriptId scriptId{};
int lineNumber{};
std::optional<int> columnNumber;
};
struct runtime::PropertyPreview : public Serializable {
PropertyPreview() = default;
PropertyPreview(PropertyPreview &&) = default;
PropertyPreview(const PropertyPreview &) = delete;
explicit PropertyPreview(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
PropertyPreview &operator=(const PropertyPreview &) = delete;
PropertyPreview &operator=(PropertyPreview &&) = default;
std::string name;
std::string type;
std::optional<std::string> value;
std::unique_ptr<
runtime::ObjectPreview,
std::function<void(runtime::ObjectPreview *)>>
valuePreview{nullptr, deleter<runtime::ObjectPreview>};
std::optional<std::string> subtype;
};
struct runtime::EntryPreview : public Serializable {
EntryPreview() = default;
EntryPreview(EntryPreview &&) = default;
EntryPreview(const EntryPreview &) = delete;
explicit EntryPreview(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
EntryPreview &operator=(const EntryPreview &) = delete;
EntryPreview &operator=(EntryPreview &&) = default;
std::unique_ptr<
runtime::ObjectPreview,
std::function<void(runtime::ObjectPreview *)>>
key{nullptr, deleter<runtime::ObjectPreview>};
std::unique_ptr<
runtime::ObjectPreview,
std::function<void(runtime::ObjectPreview *)>>
value{nullptr, deleter<runtime::ObjectPreview>};
};
struct runtime::ObjectPreview : public Serializable {
ObjectPreview() = default;
ObjectPreview(ObjectPreview &&) = default;
ObjectPreview(const ObjectPreview &) = delete;
explicit ObjectPreview(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
ObjectPreview &operator=(const ObjectPreview &) = delete;
ObjectPreview &operator=(ObjectPreview &&) = default;
std::string type;
std::optional<std::string> subtype;
std::optional<std::string> description;
bool overflow{};
std::vector<runtime::PropertyPreview> properties;
std::optional<std::vector<runtime::EntryPreview>> entries;
};
struct runtime::CustomPreview : public Serializable {
CustomPreview() = default;
CustomPreview(CustomPreview &&) = default;
CustomPreview(const CustomPreview &) = delete;
explicit CustomPreview(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
CustomPreview &operator=(const CustomPreview &) = delete;
CustomPreview &operator=(CustomPreview &&) = default;
std::string header;
std::optional<runtime::RemoteObjectId> bodyGetterId;
};
struct runtime::RemoteObject : public Serializable {
RemoteObject() = default;
RemoteObject(RemoteObject &&) = default;
RemoteObject(const RemoteObject &) = delete;
explicit RemoteObject(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
RemoteObject &operator=(const RemoteObject &) = delete;
RemoteObject &operator=(RemoteObject &&) = default;
std::string type;
std::optional<std::string> subtype;
@@ -213,12 +295,18 @@ struct runtime::RemoteObject : public Serializable {
std::optional<runtime::UnserializableValue> unserializableValue;
std::optional<std::string> description;
std::optional<runtime::RemoteObjectId> objectId;
std::optional<runtime::ObjectPreview> preview;
std::optional<runtime::CustomPreview> customPreview;
};
struct runtime::CallFrame : public Serializable {
CallFrame() = default;
CallFrame(CallFrame &&) = default;
CallFrame(const CallFrame &) = delete;
explicit CallFrame(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
CallFrame &operator=(const CallFrame &) = delete;
CallFrame &operator=(CallFrame &&) = default;
std::string functionName;
runtime::ScriptId scriptId{};
@@ -229,8 +317,12 @@ struct runtime::CallFrame : public Serializable {
struct runtime::StackTrace : public Serializable {
StackTrace() = default;
StackTrace(StackTrace &&) = default;
StackTrace(const StackTrace &) = delete;
explicit StackTrace(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
StackTrace &operator=(const StackTrace &) = delete;
StackTrace &operator=(StackTrace &&) = default;
std::optional<std::string> description;
std::vector<runtime::CallFrame> callFrames;
@@ -239,8 +331,12 @@ struct runtime::StackTrace : public Serializable {
struct runtime::ExceptionDetails : public Serializable {
ExceptionDetails() = default;
ExceptionDetails(ExceptionDetails &&) = default;
ExceptionDetails(const ExceptionDetails &) = delete;
explicit ExceptionDetails(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
ExceptionDetails &operator=(const ExceptionDetails &) = delete;
ExceptionDetails &operator=(ExceptionDetails &&) = default;
int exceptionId{};
std::string text;
@@ -255,8 +351,12 @@ struct runtime::ExceptionDetails : public Serializable {
struct debugger::Scope : public Serializable {
Scope() = default;
Scope(Scope &&) = default;
Scope(const Scope &) = delete;
explicit Scope(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
Scope &operator=(const Scope &) = delete;
Scope &operator=(Scope &&) = default;
std::string type;
runtime::RemoteObject object{};
@@ -267,8 +367,12 @@ struct debugger::Scope : public Serializable {
struct debugger::CallFrame : public Serializable {
CallFrame() = default;
CallFrame(CallFrame &&) = default;
CallFrame(const CallFrame &) = delete;
explicit CallFrame(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
CallFrame &operator=(const CallFrame &) = delete;
CallFrame &operator=(CallFrame &&) = default;
debugger::CallFrameId callFrameId{};
std::string functionName;
@@ -282,8 +386,12 @@ struct debugger::CallFrame : public Serializable {
struct heapProfiler::SamplingHeapProfileNode : public Serializable {
SamplingHeapProfileNode() = default;
SamplingHeapProfileNode(SamplingHeapProfileNode &&) = default;
SamplingHeapProfileNode(const SamplingHeapProfileNode &) = delete;
explicit SamplingHeapProfileNode(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
SamplingHeapProfileNode &operator=(const SamplingHeapProfileNode &) = delete;
SamplingHeapProfileNode &operator=(SamplingHeapProfileNode &&) = default;
runtime::CallFrame callFrame{};
double selfSize{};
@@ -293,8 +401,13 @@ struct heapProfiler::SamplingHeapProfileNode : public Serializable {
struct heapProfiler::SamplingHeapProfileSample : public Serializable {
SamplingHeapProfileSample() = default;
SamplingHeapProfileSample(SamplingHeapProfileSample &&) = default;
SamplingHeapProfileSample(const SamplingHeapProfileSample &) = delete;
explicit SamplingHeapProfileSample(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
SamplingHeapProfileSample &operator=(const SamplingHeapProfileSample &) =
delete;
SamplingHeapProfileSample &operator=(SamplingHeapProfileSample &&) = default;
double size{};
int nodeId{};
@@ -303,8 +416,12 @@ struct heapProfiler::SamplingHeapProfileSample : public Serializable {
struct heapProfiler::SamplingHeapProfile : public Serializable {
SamplingHeapProfile() = default;
SamplingHeapProfile(SamplingHeapProfile &&) = default;
SamplingHeapProfile(const SamplingHeapProfile &) = delete;
explicit SamplingHeapProfile(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
SamplingHeapProfile &operator=(const SamplingHeapProfile &) = delete;
SamplingHeapProfile &operator=(SamplingHeapProfile &&) = default;
heapProfiler::SamplingHeapProfileNode head{};
std::vector<heapProfiler::SamplingHeapProfileSample> samples;
@@ -312,8 +429,12 @@ struct heapProfiler::SamplingHeapProfile : public Serializable {
struct profiler::PositionTickInfo : public Serializable {
PositionTickInfo() = default;
PositionTickInfo(PositionTickInfo &&) = default;
PositionTickInfo(const PositionTickInfo &) = delete;
explicit PositionTickInfo(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
PositionTickInfo &operator=(const PositionTickInfo &) = delete;
PositionTickInfo &operator=(PositionTickInfo &&) = default;
int line{};
int ticks{};
@@ -321,8 +442,12 @@ struct profiler::PositionTickInfo : public Serializable {
struct profiler::ProfileNode : public Serializable {
ProfileNode() = default;
ProfileNode(ProfileNode &&) = default;
ProfileNode(const ProfileNode &) = delete;
explicit ProfileNode(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
ProfileNode &operator=(const ProfileNode &) = delete;
ProfileNode &operator=(ProfileNode &&) = default;
int id{};
runtime::CallFrame callFrame{};
@@ -334,8 +459,12 @@ struct profiler::ProfileNode : public Serializable {
struct profiler::Profile : public Serializable {
Profile() = default;
Profile(Profile &&) = default;
Profile(const Profile &) = delete;
explicit Profile(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
Profile &operator=(const Profile &) = delete;
Profile &operator=(Profile &&) = default;
std::vector<profiler::ProfileNode> nodes;
double startTime{};
@@ -346,8 +475,12 @@ struct profiler::Profile : public Serializable {
struct runtime::CallArgument : public Serializable {
CallArgument() = default;
CallArgument(CallArgument &&) = default;
CallArgument(const CallArgument &) = delete;
explicit CallArgument(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
CallArgument &operator=(const CallArgument &) = delete;
CallArgument &operator=(CallArgument &&) = default;
std::optional<folly::dynamic> value;
std::optional<runtime::UnserializableValue> unserializableValue;
@@ -356,8 +489,14 @@ struct runtime::CallArgument : public Serializable {
struct runtime::ExecutionContextDescription : public Serializable {
ExecutionContextDescription() = default;
ExecutionContextDescription(ExecutionContextDescription &&) = default;
ExecutionContextDescription(const ExecutionContextDescription &) = delete;
explicit ExecutionContextDescription(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
ExecutionContextDescription &operator=(const ExecutionContextDescription &) =
delete;
ExecutionContextDescription &operator=(ExecutionContextDescription &&) =
default;
runtime::ExecutionContextId id{};
std::string origin;
@@ -367,8 +506,12 @@ struct runtime::ExecutionContextDescription : public Serializable {
struct runtime::PropertyDescriptor : public Serializable {
PropertyDescriptor() = default;
PropertyDescriptor(PropertyDescriptor &&) = default;
PropertyDescriptor(const PropertyDescriptor &) = delete;
explicit PropertyDescriptor(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
PropertyDescriptor &operator=(const PropertyDescriptor &) = delete;
PropertyDescriptor &operator=(PropertyDescriptor &&) = default;
std::string name;
std::optional<runtime::RemoteObject> value;
@@ -384,8 +527,14 @@ struct runtime::PropertyDescriptor : public Serializable {
struct runtime::InternalPropertyDescriptor : public Serializable {
InternalPropertyDescriptor() = default;
InternalPropertyDescriptor(InternalPropertyDescriptor &&) = default;
InternalPropertyDescriptor(const InternalPropertyDescriptor &) = delete;
explicit InternalPropertyDescriptor(const folly::dynamic &obj);
folly::dynamic toDynamic() const override;
InternalPropertyDescriptor &operator=(const InternalPropertyDescriptor &) =
delete;
InternalPropertyDescriptor &operator=(InternalPropertyDescriptor &&) =
default;
std::string name;
std::optional<runtime::RemoteObject> value;
@@ -431,6 +580,7 @@ struct debugger::EvaluateOnCallFrameRequest : public Request {
std::optional<bool> includeCommandLineAPI;
std::optional<bool> silent;
std::optional<bool> returnByValue;
std::optional<bool> generatePreview;
std::optional<bool> throwOnSideEffect;
};
@@ -579,6 +729,8 @@ struct heapProfiler::StartSamplingRequest : public Request {
void accept(RequestHandler &handler) const override;
std::optional<double> samplingInterval;
std::optional<bool> includeObjectsCollectedByMajorGC;
std::optional<bool> includeObjectsCollectedByMinorGC;
};
struct heapProfiler::StartTrackingHeapObjectsRequest : public Request {
@@ -651,6 +803,7 @@ struct runtime::CallFunctionOnRequest : public Request {
std::optional<std::vector<runtime::CallArgument>> arguments;
std::optional<bool> silent;
std::optional<bool> returnByValue;
std::optional<bool> generatePreview;
std::optional<bool> userGesture;
std::optional<bool> awaitPromise;
std::optional<runtime::ExecutionContextId> executionContextId;
@@ -683,6 +836,7 @@ struct runtime::EvaluateRequest : public Request {
std::optional<bool> silent;
std::optional<runtime::ExecutionContextId> contextId;
std::optional<bool> returnByValue;
std::optional<bool> generatePreview;
std::optional<bool> userGesture;
std::optional<bool> awaitPromise;
};
@@ -704,6 +858,7 @@ struct runtime::GetPropertiesRequest : public Request {
runtime::RemoteObjectId objectId{};
std::optional<bool> ownProperties;
std::optional<bool> generatePreview;
};
struct runtime::GlobalLexicalScopeNamesRequest : public Request {
@@ -105,6 +105,19 @@ void assign(std::unique_ptr<T> &lhs, const dynamic &obj, const U &key) {
}
}
template <typename T, typename U, typename D>
void assign(
std::unique_ptr<T, std::function<void(D *)>> &lhs,
const dynamic &obj,
const U &key) {
auto it = obj.find(key);
if (it != obj.items().end()) {
lhs = std::make_unique<T>(valueFromDynamic<T>(it->second));
} else {
lhs.reset();
}
}
/// valueToDynamic
inline dynamic valueToDynamic(const Serializable &value) {
@@ -153,6 +166,23 @@ void put(dynamic &obj, const K &key, const std::unique_ptr<V> &ptr) {
}
}
template <typename K, typename V, typename D>
void put(
dynamic &obj,
const K &key,
const std::unique_ptr<V, std::function<void(D *)>> &ptr) {
if (ptr.get()) {
obj[key] = valueToDynamic(*ptr);
} else {
obj.erase(key);
}
}
template <typename T>
void deleter(T *p) {
delete p;
}
} // namespace message
} // namespace chrome
} // namespace inspector
@@ -159,7 +159,7 @@ void expectCallFrames(
int i = 0;
for (const FrameInfo &info : infos) {
m::debugger::CallFrame frame = frames[i];
const m::debugger::CallFrame &frame = frames[i];
EXPECT_EQ(frame.callFrameId, folly::to<std::string>(i));
EXPECT_EQ(frame.functionName, info.functionName);
@@ -1570,7 +1570,7 @@ TEST(ConnectionTests, testGetPropertiesOnlyOwnProperties) {
// scope.
auto pausedNote =
expectPaused(conn, "other", {{"foo", 7, 2}, {"global", 9, 1}});
auto scopeObject = pausedNote.callFrames.at(0).scopeChain.at(0).object;
const auto &scopeObject = pausedNote.callFrames.at(0).scopeChain.at(0).object;
auto scopeChildren = expectProps(
conn,
msgId++,
@@ -1930,12 +1930,12 @@ TEST(ConnectionTests, testScopeVariables) {
// [1] (line 7) hit debugger statement
auto pausedNote =
expectPaused(conn, "other", {{"func", 7, 2}, {"global", 12, 1}});
auto scopeChain = pausedNote.callFrames.at(0).scopeChain;
const auto &scopeChain = pausedNote.callFrames.at(0).scopeChain;
EXPECT_EQ(scopeChain.size(), 2);
// [2] inspect local scope
EXPECT_EQ(scopeChain.at(0).type, "local");
auto localScopeObject = scopeChain.at(0).object;
const auto &localScopeObject = scopeChain.at(0).object;
auto localScopeObjectChildren = expectProps(
conn,
msgId++,
@@ -1958,7 +1958,7 @@ TEST(ConnectionTests, testScopeVariables) {
// As a workaround we create a Map of properties and check that
// those global properties that we have defined are in the map.
EXPECT_EQ(scopeChain.at(1).type, "global");
auto globalScopeObject = scopeChain.at(1).object;
const auto &globalScopeObject = scopeChain.at(1).object;
m::runtime::GetPropertiesRequest req;
req.id = msgId++;
req.objectId = globalScopeObject.objectId.value();
@@ -1966,8 +1966,9 @@ TEST(ConnectionTests, testScopeVariables) {
auto resp = expectResponse<m::runtime::GetPropertiesResponse>(conn, req.id);
std::unordered_map<std::string, std::optional<m::runtime::RemoteObject>>
globalProperties;
for (auto propertyDescriptor : resp.result) {
globalProperties[propertyDescriptor.name] = propertyDescriptor.value;
for (auto &propertyDescriptor : resp.result) {
globalProperties[propertyDescriptor.name] =
std::move(propertyDescriptor.value);
}
EXPECT_GE(globalProperties.size(), 3);
@@ -2039,17 +2040,8 @@ TEST(ConnectionTests, testRuntimeCallFunctionOnObject) {
auto addMember = [&](const m::runtime::RemoteObjectId id,
const char *type,
const char *propName,
const m::runtime::CallArgument &ca,
m::runtime::CallArgument ca,
bool allowRedefinition = false) {
m::runtime::CallFunctionOnRequest req;
req.id = msgId++;
req.functionDeclaration =
std::string("function(e){const r=\"") + propName + "\"; this[r]=e,r}";
req.arguments = std::vector<m::runtime::CallArgument>{ca};
req.objectId = thisId;
conn.send(req.toJson());
expectResponse<m::runtime::CallFunctionOnResponse>(conn, req.id);
auto it = expectedPropInfos.emplace(propName, PropInfo(type));
EXPECT_TRUE(allowRedefinition || it.second)
@@ -2062,6 +2054,16 @@ TEST(ConnectionTests, testRuntimeCallFunctionOnObject) {
if (ca.unserializableValue) {
it.first->second.setUnserializableValue(*ca.unserializableValue);
}
m::runtime::CallFunctionOnRequest req;
req.id = msgId++;
req.functionDeclaration =
std::string("function(e){const r=\"") + propName + "\"; this[r]=e,r}";
req.arguments = std::vector<m::runtime::CallArgument>{};
req.arguments->push_back(std::move(ca));
req.objectId = thisId;
conn.send(req.toJson());
expectResponse<m::runtime::CallFunctionOnResponse>(conn, req.id);
};
addMember(thisId, "boolean", "b", makeValueCallArgument(true));
@@ -2136,8 +2138,8 @@ TEST(ConnectionTests, testRuntimeCallFunctionOnExecutionContext) {
auto resp = expectResponse<m::runtime::GetPropertiesResponse>(conn, req.id);
std::unordered_map<std::string, std::optional<m::runtime::RemoteObject>>
properties;
for (auto propertyDescriptor : resp.result) {
properties[propertyDescriptor.name] = propertyDescriptor.value;
for (auto &propertyDescriptor : resp.result) {
properties[propertyDescriptor.name] = std::move(propertyDescriptor.value);
}
return properties;
};
@@ -2159,7 +2161,7 @@ TEST(ConnectionTests, testRuntimeCallFunctionOnExecutionContext) {
// plus the Runtime.CallArgument to be sent to the inspector.
struct {
const char *propName;
const m::runtime::CallArgument callArg;
m::runtime::CallArgument callArg;
} tests[] = {
{"callFunctionOnTestMember1", makeValueCallArgument(10)},
{"callFunctionOnTestMember2", makeValueCallArgument("string")},
@@ -2176,19 +2178,22 @@ TEST(ConnectionTests, testRuntimeCallFunctionOnExecutionContext) {
}
auto addMember = [&msgId, &conn](
const char *propName,
const m::runtime::CallArgument &ca) {
const char *propName, m::runtime::CallArgument &ca) {
m::runtime::CallFunctionOnRequest req;
req.id = msgId++;
req.functionDeclaration =
std::string("function(e){const r=\"") + propName + "\"; this[r]=e,r}";
req.arguments = std::vector<m::runtime::CallArgument>{ca};
// Don't have an easy way to copy these, so...
req.arguments = std::vector<m::runtime::CallArgument>{};
req.arguments->push_back(std::move(ca));
req.executionContextId = 1;
conn.send(req.toJson());
expectResponse<m::runtime::CallFunctionOnResponse>(conn, req.id);
// n.b. we're only borrowing the CallArgument, so give it back...
ca = std::move(req.arguments->at(0));
};
for (const auto &test : tests) {
for (auto &test : tests) {
addMember(test.propName, test.callArg);
}
@@ -2334,7 +2339,7 @@ TEST(ConnectionTests, testThisObject) {
expectPaused(conn, "other", {{"foo", 7, 2}, {"global", 11, 1}});
// [2] inspect first call frame (foo)
auto localThisObj = pausedNote.callFrames.at(0).thisObj;
const auto &localThisObj = pausedNote.callFrames.at(0).thisObj;
expectProps(
conn,
msgId++,
@@ -2348,7 +2353,7 @@ TEST(ConnectionTests, testThisObject) {
// in our test code and we can't use expectProps() method here.
// As a workaround we create a Map of properties and check that
// those global properties that we have defined are in the map.
auto globalThisObj = pausedNote.callFrames.at(1).thisObj;
const auto &globalThisObj = pausedNote.callFrames.at(1).thisObj;
m::runtime::GetPropertiesRequest req;
req.id = msgId++;
req.objectId = globalThisObj.objectId.value();
@@ -2356,8 +2361,8 @@ TEST(ConnectionTests, testThisObject) {
auto resp = expectResponse<m::runtime::GetPropertiesResponse>(conn, req.id);
std::unordered_map<std::string, std::optional<m::runtime::RemoteObject>>
properties;
for (auto propertyDescriptor : resp.result) {
properties[propertyDescriptor.name] = propertyDescriptor.value;
for (auto &propertyDescriptor : resp.result) {
properties[propertyDescriptor.name] = std::move(propertyDescriptor.value);
}
// globalString should be of type "string" and have value "global-string".
@@ -118,7 +118,7 @@ TEST(MessageTests, testSerializeResponse) {
debugger::SetBreakpointByUrlResponse resp;
resp.id = 1;
resp.breakpointId = "myBreakpointId";
resp.locations = {location};
resp.locations.push_back(std::move(location));
dynamic result = resp.toDynamic();
dynamic expected = folly::parseJson(R"({
@@ -180,8 +180,8 @@ TEST(MessageTests, testSerializeNotification) {
scope.object.description = "myDesc";
scope.object.objectId = "id1";
scope.name = "myScope";
scope.startLocation = startLocation;
scope.endLocation = endLocation;
scope.startLocation = std::move(startLocation);
scope.endLocation = std::move(endLocation);
debugger::CallFrame frame;
frame.callFrameId = "callFrame1";
@@ -190,11 +190,11 @@ TEST(MessageTests, testSerializeNotification) {
frame.location.lineNumber = 3;
frame.location.columnNumber = 4;
frame.url = "foo.js";
frame.scopeChain = std::vector<debugger::Scope>{scope};
frame.scopeChain.push_back(std::move(scope));
frame.thisObj.type = "function";
debugger::PausedNotification note;
note.callFrames = std::vector<debugger::CallFrame>{frame};
note.callFrames.push_back(std::move(frame));
note.reason = "debugCommand";
note.data = dynamic::object("foo", "bar");
note.hitBreakpoints = std::vector<std::string>{"a", "b"};
@@ -19,7 +19,7 @@ yarn build
node bin/index.js \
--ignore-experimental \
--include-experimental=Runtime.getHeapUsage \
--include-experimental=Runtime.getProperties.generatePreview,Runtime.evaluate.generatePreview,Runtime.callFunctionOn.generatePreview,Debugger.evaluateOnCallFrame.generatePreview,Runtime.RemoteObject.preview,Runtime.RemoteObject.customPreview,Runtime.CustomPreview,Runtime.EntryPreview,Runtime.ObjectPreview,Runtime.PropertyPreview,Runtime.getHeapUsage \
--roots "${MSGTYPES_PATH}" \
"${HEADER_PATH}" "${CPP_PATH}"
+4 -4
View File
@@ -3896,10 +3896,10 @@ detect-newline@^3.0.0:
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
devtools-protocol@0.0.959523:
version "0.0.959523"
resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.959523.tgz#a7ce62c6b88876081fe5bec866f70e467bc021ba"
integrity sha512-taOcAND/oJA5FhJD2I3RA+I8RPdrpPJWwvMBPzTq7Sugev1xTOG3lgtlSfkh5xkjTYw0Ti2CRQq016goFHMoPQ==
devtools-protocol@0.0.1107588:
version "0.0.1107588"
resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1107588.tgz#f8cac707840b97cc30b029359341bcbbb0ad8ffa"
integrity sha512-yIR+pG9x65Xko7bErCUSQaDLrO/P1p3JUzEk7JCU4DowPcGHkTGUGQapcfcLc4qj0UaALwZ+cr0riFgiqpixcg==
diff-sequences@^29.2.0:
version "29.2.0"