diff --git a/packages/hermes-inspector-msggen/package.json b/packages/hermes-inspector-msggen/package.json index 78d8a77a611..f39c077005d 100644 --- a/packages/hermes-inspector-msggen/package.json +++ b/packages/hermes-inspector-msggen/package.json @@ -13,7 +13,7 @@ "test": "jest" }, "dependencies": { - "devtools-protocol": "0.0.959523", + "devtools-protocol": "0.0.1107588", "yargs": "^17.6.2" }, "devDependencies": { diff --git a/packages/hermes-inspector-msggen/src/Command.js b/packages/hermes-inspector-msggen/src/Command.js index b7b60d4aeac..feed094bb12 100644 --- a/packages/hermes-inspector-msggen/src/Command.js +++ b/packages/hermes-inspector-msggen/src/Command.js @@ -23,26 +23,38 @@ export class Command { domain: string, obj: any, ignoreExperimental: boolean, + includeExperimental: Set, ): ?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, + ) { 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, ); } diff --git a/packages/hermes-inspector-msggen/src/Event.js b/packages/hermes-inspector-msggen/src/Event.js index 9edfe7f8f2b..10e9ee97422 100644 --- a/packages/hermes-inspector-msggen/src/Event.js +++ b/packages/hermes-inspector-msggen/src/Event.js @@ -18,20 +18,34 @@ export class Event { experimental: ?boolean; parameters: Array; - static create(domain: string, obj: any, ignoreExperimental: boolean): ?Event { - return ignoreExperimental && obj.experimental + static create( + domain: string, + obj: any, + ignoreExperimental: boolean, + includeExperimental: Set, + ): ?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, + ) { this.domain = domain; this.name = obj.name; this.description = obj.description; this.parameters = Property.createArray( domain, + obj.name, obj.parameters || [], ignoreExperimental, + includeExperimental, ); } diff --git a/packages/hermes-inspector-msggen/src/Graph.js b/packages/hermes-inspector-msggen/src/Graph.js index 359e4673924..a3b22d3a206 100644 --- a/packages/hermes-inspector-msggen/src/Graph.js +++ b/packages/hermes-inspector-msggen/src/Graph.js @@ -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): Array { + traverse(rootIds: Array): { + nodes: Array, + cycles: Array, + } { // 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 = []; - postorder(root, output); + const nodes: Array = []; + const cycles: Array = []; + 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) { +function postorder(node: Node, nodes: Array, cycles: Array) { 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); } diff --git a/packages/hermes-inspector-msggen/src/HeaderWriter.js b/packages/hermes-inspector-msggen/src/HeaderWriter.js index 560c9580789..3e4beaa4ce5 100644 --- a/packages/hermes-inspector-msggen/src/HeaderWriter.js +++ b/packages/hermes-inspector-msggen/src/HeaderWriter.js @@ -62,6 +62,8 @@ export class HeaderWriter { namespace chrome { namespace message { +template +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) { diff --git a/packages/hermes-inspector-msggen/src/Property.js b/packages/hermes-inspector-msggen/src/Property.js index 56dcff6a72c..d50df01f503 100644 --- a/packages/hermes-inspector-msggen/src/Property.js +++ b/packages/hermes-inspector-msggen/src/Property.js @@ -34,12 +34,19 @@ export class Property { static createArray( domain: string, + parent: string, elements: Array, ignoreExperimental: boolean, + includeExperimental: Set, ): Array { 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>`; + } 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 + ? '' + : '{}'; } } diff --git a/packages/hermes-inspector-msggen/src/Type.js b/packages/hermes-inspector-msggen/src/Type.js index 18f9ac11cf5..eef2a4265a8 100644 --- a/packages/hermes-inspector-msggen/src/Type.js +++ b/packages/hermes-inspector-msggen/src/Type.js @@ -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, + ): ?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; - constructor(domain: string, obj: any, ignoreExperimental: boolean) { + constructor( + domain: string, + obj: any, + ignoreExperimental: boolean, + includeExperimental: Set, + ) { super(domain, obj); this.type = obj.type; this.properties = Property.createArray( domain, + obj.id, obj.properties || [], ignoreExperimental, + includeExperimental, ); } diff --git a/packages/hermes-inspector-msggen/src/index.js b/packages/hermes-inspector-msggen/src/index.js index f44d57300c9..dd6c488f605 100644 --- a/packages/hermes-inspector-msggen/src/index.js +++ b/packages/hermes-inspector-msggen/src/index.js @@ -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, ): 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 = 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); diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/Connection.cpp b/packages/react-native/ReactCommon/hermes/inspector/chrome/Connection.cpp index 2259d503a59..8bce4307841 100644 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/Connection.cpp +++ b/packages/react-native/ReactCommon/hermes/inspector/chrome/Connection.cpp @@ -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); diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp b/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp index 3ae62d24002..e36a47344aa 100644 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp +++ b/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypes.cpp @@ -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); diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypes.h b/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypes.h index 61a477221ad..26544df6e33 100644 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypes.h +++ b/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypes.h @@ -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 +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 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 value; + std::unique_ptr< + runtime::ObjectPreview, + std::function> + valuePreview{nullptr, deleter}; + std::optional 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> + key{nullptr, deleter}; + std::unique_ptr< + runtime::ObjectPreview, + std::function> + value{nullptr, deleter}; +}; + +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 subtype; + std::optional description; + bool overflow{}; + std::vector properties; + std::optional> 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 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 subtype; @@ -213,12 +295,18 @@ struct runtime::RemoteObject : public Serializable { std::optional unserializableValue; std::optional description; std::optional objectId; + std::optional preview; + std::optional 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 description; std::vector 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 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 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 value; std::optional 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 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 value; @@ -431,6 +580,7 @@ struct debugger::EvaluateOnCallFrameRequest : public Request { std::optional includeCommandLineAPI; std::optional silent; std::optional returnByValue; + std::optional generatePreview; std::optional throwOnSideEffect; }; @@ -579,6 +729,8 @@ struct heapProfiler::StartSamplingRequest : public Request { void accept(RequestHandler &handler) const override; std::optional samplingInterval; + std::optional includeObjectsCollectedByMajorGC; + std::optional includeObjectsCollectedByMinorGC; }; struct heapProfiler::StartTrackingHeapObjectsRequest : public Request { @@ -651,6 +803,7 @@ struct runtime::CallFunctionOnRequest : public Request { std::optional> arguments; std::optional silent; std::optional returnByValue; + std::optional generatePreview; std::optional userGesture; std::optional awaitPromise; std::optional executionContextId; @@ -683,6 +836,7 @@ struct runtime::EvaluateRequest : public Request { std::optional silent; std::optional contextId; std::optional returnByValue; + std::optional generatePreview; std::optional userGesture; std::optional awaitPromise; }; @@ -704,6 +858,7 @@ struct runtime::GetPropertiesRequest : public Request { runtime::RemoteObjectId objectId{}; std::optional ownProperties; + std::optional generatePreview; }; struct runtime::GlobalLexicalScopeNamesRequest : public Request { diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypesInlines.h b/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypesInlines.h index 54a2f6f7c99..5f403fe1329 100644 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypesInlines.h +++ b/packages/react-native/ReactCommon/hermes/inspector/chrome/MessageTypesInlines.h @@ -105,6 +105,19 @@ void assign(std::unique_ptr &lhs, const dynamic &obj, const U &key) { } } +template +void assign( + std::unique_ptr> &lhs, + const dynamic &obj, + const U &key) { + auto it = obj.find(key); + if (it != obj.items().end()) { + lhs = std::make_unique(valueFromDynamic(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 &ptr) { } } +template +void put( + dynamic &obj, + const K &key, + const std::unique_ptr> &ptr) { + if (ptr.get()) { + obj[key] = valueToDynamic(*ptr); + } else { + obj.erase(key); + } +} + +template +void deleter(T *p) { + delete p; +} + } // namespace message } // namespace chrome } // namespace inspector diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/ConnectionTests.cpp b/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/ConnectionTests.cpp index 1338154be37..ced992837aa 100644 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/ConnectionTests.cpp +++ b/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/ConnectionTests.cpp @@ -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(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(conn, req.id); std::unordered_map> 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{ca}; - req.objectId = thisId; - conn.send(req.toJson()); - expectResponse(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{}; + req.arguments->push_back(std::move(ca)); + req.objectId = thisId; + conn.send(req.toJson()); + expectResponse(conn, req.id); }; addMember(thisId, "boolean", "b", makeValueCallArgument(true)); @@ -2136,8 +2138,8 @@ TEST(ConnectionTests, testRuntimeCallFunctionOnExecutionContext) { auto resp = expectResponse(conn, req.id); std::unordered_map> 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{ca}; + // Don't have an easy way to copy these, so... + req.arguments = std::vector{}; + req.arguments->push_back(std::move(ca)); req.executionContextId = 1; conn.send(req.toJson()); expectResponse(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(conn, req.id); std::unordered_map> 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". diff --git a/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/MessageTests.cpp b/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/MessageTests.cpp index c8a94d6ebc8..fe3c0d9a9e9 100644 --- a/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/MessageTests.cpp +++ b/packages/react-native/ReactCommon/hermes/inspector/chrome/tests/MessageTests.cpp @@ -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{scope}; + frame.scopeChain.push_back(std::move(scope)); frame.thisObj.type = "function"; debugger::PausedNotification note; - note.callFrames = std::vector{frame}; + note.callFrames.push_back(std::move(frame)); note.reason = "debugCommand"; note.data = dynamic::object("foo", "bar"); note.hitBreakpoints = std::vector{"a", "b"}; diff --git a/packages/react-native/ReactCommon/hermes/inspector/tools/run_msggen b/packages/react-native/ReactCommon/hermes/inspector/tools/run_msggen index 3954775cfcd..45fbfd11963 100755 --- a/packages/react-native/ReactCommon/hermes/inspector/tools/run_msggen +++ b/packages/react-native/ReactCommon/hermes/inspector/tools/run_msggen @@ -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}" diff --git a/yarn.lock b/yarn.lock index 7d6bb88b79b..d055e9805f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"