From e100c9f72d1506490216c0e387f0140c598d24dc Mon Sep 17 00:00:00 2001 From: Christoph Purrer Date: Sun, 6 Nov 2022 14:21:45 -0800 Subject: [PATCH] Scaffolding for the PerformanceObserver TurboModule Summary: Changelog: [Internal] Differential Revision: D41028555 fbshipit-source-id: 082e80e0ccbde9633e30e5de19129f85e11dc373 --- BUCK | 28 +++++++ .../NativePerformanceObserver.cpp | 41 ++++++++++ .../NativePerformanceObserver.h | 40 ++++++++++ .../NativePerformanceObserver.js} | 27 +++---- ...ePerformanceObserver_RawPerformanceEntry.h | 76 +++++++++++++++++++ .../WebPerformance/PerformanceObserver.js | 7 +- 6 files changed, 202 insertions(+), 17 deletions(-) create mode 100644 Libraries/WebPerformance/NativePerformanceObserver.cpp create mode 100644 Libraries/WebPerformance/NativePerformanceObserver.h rename Libraries/{NativeModules/specs/NativePerformanceObserverCxx.js => WebPerformance/NativePerformanceObserver.js} (52%) create mode 100644 Libraries/WebPerformance/NativePerformanceObserver_RawPerformanceEntry.h diff --git a/BUCK b/BUCK index f6992f1619298c..b29a350994e0f7 100644 --- a/BUCK +++ b/BUCK @@ -10,6 +10,9 @@ load( ) load( "//tools/build_defs/oss:rn_defs.bzl", + "ANDROID", + "APPLE", + "CXX", "HERMES_BYTECODE_VERSION", "IOS", "RCT_IMAGE_DATA_DECODER_SOCKET", @@ -1453,3 +1456,28 @@ rn_xplat_cxx_library2( "//fbobjc/VendorLib/react-native-maps:react-native-maps", ], ) + +rn_xplat_cxx_library2( + name = "RCTWebPerformance", + srcs = glob([ + "Libraries/WebPerformance/**/*.cpp", + ]), + header_namespace = "", + exported_headers = subdir_glob( + [("Libraries/WebPerformance", "*.h")], + prefix = "RCTWebPerformance", + ), + fbandroid_compiler_flags = [ + "-fexceptions", + "-frtti", + ], + labels = [ + "depslint_never_remove", + "pfh:ReactNative_CommonInfrastructurePlaceholder", + ], + platforms = (ANDROID, APPLE, CXX), + visibility = ["PUBLIC"], + deps = [ + ":FBReactNativeSpecJSI", + ], +) diff --git a/Libraries/WebPerformance/NativePerformanceObserver.cpp b/Libraries/WebPerformance/NativePerformanceObserver.cpp new file mode 100644 index 00000000000000..ca182dd55585f4 --- /dev/null +++ b/Libraries/WebPerformance/NativePerformanceObserver.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "NativePerformanceObserver.h" + +namespace facebook::react { + +void NativePerformanceObserver::startReporting( + jsi::Runtime &rt, + std::string entryType) {} + +void NativePerformanceObserver::stopReporting( + jsi::Runtime &rt, + std::string entryType) {} + +std::vector NativePerformanceObserver::getPendingEntries( + jsi::Runtime &rt) { + return std::vector{}; +} + +std::function NativePerformanceObserver::setOnPerformanceEntryCallback( + jsi::Runtime &rt, + std::optional> callback) { + callback_ = callback; + // Invoke function + // if (callback) { + // callback.value()(); + // } + // return cleanup function + return [weakThis = weak_from_this()]() { + if (auto strongThis = weakThis.lock()) { + strongThis->callback_ = std::nullopt; + } + }; +} + +} // namespace facebook::react diff --git a/Libraries/WebPerformance/NativePerformanceObserver.h b/Libraries/WebPerformance/NativePerformanceObserver.h new file mode 100644 index 00000000000000..11ecc6b8a69c1a --- /dev/null +++ b/Libraries/WebPerformance/NativePerformanceObserver.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "NativePerformanceObserver_RawPerformanceEntry.h" + +namespace facebook::react { + +class NativePerformanceObserver + : public NativePerformanceObserverCxxSpec, + std::enable_shared_from_this { + public: + NativePerformanceObserver(std::shared_ptr jsInvoker); + + void startReporting(jsi::Runtime &rt, std::string entryType); + + void stopReporting(jsi::Runtime &rt, std::string entryType); + + std::vector getPendingEntries(jsi::Runtime &rt); + + std::function setOnPerformanceEntryCallback( + jsi::Runtime &rt, + std::optional> callback); + + private: + std::optional> callback_; +}; + +} // namespace facebook::react diff --git a/Libraries/NativeModules/specs/NativePerformanceObserverCxx.js b/Libraries/WebPerformance/NativePerformanceObserver.js similarity index 52% rename from Libraries/NativeModules/specs/NativePerformanceObserverCxx.js rename to Libraries/WebPerformance/NativePerformanceObserver.js index 420b495e091d70..3eff04f9cf2675 100644 --- a/Libraries/NativeModules/specs/NativePerformanceObserverCxx.js +++ b/Libraries/WebPerformance/NativePerformanceObserver.js @@ -8,11 +8,9 @@ * @format */ -import type {TurboModule} from '../../TurboModule/RCTExport'; +import type {TurboModule} from '../TurboModule/RCTExport'; -import * as TurboModuleRegistry from '../../TurboModule/TurboModuleRegistry'; - -export type RawTimeStamp = number; +import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry'; export const RawPerformanceEntryTypeValues = { UNDEFINED: 0, @@ -23,22 +21,25 @@ export type RawPerformanceEntryType = number; export type RawPerformanceEntry = $ReadOnly<{ name: string, entryType: RawPerformanceEntryType, - startTime: RawTimeStamp, + startTime: number, duration: number, - // For "event" entries only: - processingStart?: RawTimeStamp, - processingEnd?: RawTimeStamp, - interactionId?: RawTimeStamp, + processingStart?: number, + processingEnd?: number, + interactionId?: number, }>; -export type RawPerformanceEntryList = $ReadOnlyArray; +export type OnPerformanceEntryCallbackUnsubscribe = () => void; export interface Spec extends TurboModule { +startReporting: (entryType: string) => void; +stopReporting: (entryType: string) => void; - +getPendingEntries: () => RawPerformanceEntryList; - +setOnPerformanceEntryCallback: (callback?: () => void) => void; + +getPendingEntries: () => $ReadOnlyArray; + +setOnPerformanceEntryCallback: ( + callback?: () => void, + ) => OnPerformanceEntryCallbackUnsubscribe; } -export default (TurboModuleRegistry.get('PerformanceObserver'): ?Spec); +export default (TurboModuleRegistry.get( + 'NativePerformanceObserverCxx', +): ?Spec); diff --git a/Libraries/WebPerformance/NativePerformanceObserver_RawPerformanceEntry.h b/Libraries/WebPerformance/NativePerformanceObserver_RawPerformanceEntry.h new file mode 100644 index 00000000000000..eeb45e40fe6ade --- /dev/null +++ b/Libraries/WebPerformance/NativePerformanceObserver_RawPerformanceEntry.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +namespace facebook::react { + +struct RawPerformanceEntry { + std::string name; + int32_t entryType; + float startTime; + float duration; + // For "event" entries only: + std::optional processingStart; + std::optional processingEnd; + std::optional interactionId; +}; + +template <> +struct Bridging { + static RawPerformanceEntry fromJs( + jsi::Runtime &rt, + const jsi::Object &value, + const std::shared_ptr &jsInvoker) { + RawPerformanceEntry result{ + bridging::fromJs( + rt, value.getProperty(rt, "name"), jsInvoker), + bridging::fromJs( + rt, value.getProperty(rt, "entryType"), jsInvoker), + bridging::fromJs( + rt, value.getProperty(rt, "startTime"), jsInvoker), + bridging::fromJs( + rt, value.getProperty(rt, "duration"), jsInvoker), + bridging::fromJs>( + rt, value.getProperty(rt, "processingStart"), jsInvoker), + bridging::fromJs>( + rt, value.getProperty(rt, "processingEnd"), jsInvoker), + bridging::fromJs>( + rt, value.getProperty(rt, "interactionId"), jsInvoker), + }; + return result; + } + + static jsi::Object toJs(jsi::Runtime &rt, const RawPerformanceEntry &value) { + auto result = facebook::jsi::Object(rt); + result.setProperty(rt, "name", bridging::toJs(rt, value.name)); + result.setProperty(rt, "entryType", bridging::toJs(rt, value.entryType)); + result.setProperty(rt, "startTime", bridging::toJs(rt, value.startTime)); + result.setProperty(rt, "duration", bridging::toJs(rt, value.duration)); + if (value.processingStart) { + result.setProperty( + rt, + "processingStart", + bridging::toJs(rt, value.processingStart.value())); + } + if (value.processingEnd) { + result.setProperty( + rt, "processingEnd", bridging::toJs(rt, value.processingEnd.value())); + } + if (value.interactionId) { + result.setProperty( + rt, "interactionId", bridging::toJs(rt, value.interactionId.value())); + } + return result; + } +}; + +} // namespace facebook::react diff --git a/Libraries/WebPerformance/PerformanceObserver.js b/Libraries/WebPerformance/PerformanceObserver.js index aa750b2166e4c5..25c5744c6d4354 100644 --- a/Libraries/WebPerformance/PerformanceObserver.js +++ b/Libraries/WebPerformance/PerformanceObserver.js @@ -10,12 +10,11 @@ import type { RawPerformanceEntry, - RawPerformanceEntryList, RawPerformanceEntryType, -} from '../NativeModules/specs/NativePerformanceObserverCxx'; +} from './NativePerformanceObserver'; -import NativePerformanceObserver from '../NativeModules/specs/NativePerformanceObserverCxx'; import warnOnce from '../Utilities/warnOnce'; +import NativePerformanceObserver from './NativePerformanceObserver'; export type HighResTimeStamp = number; // TODO: Extend once new types (such as event) are supported. @@ -210,7 +209,7 @@ function onPerformanceEntry() { if (!NativePerformanceObserver) { return; } - const rawEntries: RawPerformanceEntryList = + const rawEntries: $ReadOnlyArray = NativePerformanceObserver.getPendingEntries(); const entries = rawEntries.map(rawToPerformanceEntry); _observers.forEach(observer => {