diff --git a/Libraries/Core/InitializeCore.js b/Libraries/Core/InitializeCore.js index 8f12f11b86..e00120aa4b 100644 --- a/Libraries/Core/InitializeCore.js +++ b/Libraries/Core/InitializeCore.js @@ -29,6 +29,7 @@ const start = Date.now(); require('./setUpGlobals'); +require('./setUpPerformance'); require('./setUpSystrace'); require('./setUpErrorHandling'); require('./polyfillPromise'); diff --git a/Libraries/Core/setUpPerformance.js b/Libraries/Core/setUpPerformance.js new file mode 100644 index 0000000000..6fd0a1e42b --- /dev/null +++ b/Libraries/Core/setUpPerformance.js @@ -0,0 +1,26 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +'use strict'; + +if (!global.performance) { + global.performance = {}; +} + +/** + * Returns a double, measured in milliseconds. + * https://developer.mozilla.org/en-US/docs/Web/API/Performance/now + */ +if (typeof global.performance.now !== 'function') { + global.performance.now = function() { + const performanceNow = global.nativePerformanceNow || Date.now; + return performanceNow(); + }; +} diff --git a/Libraries/Performance/Systrace.js b/Libraries/Performance/Systrace.js index dcc8555c9a..e1db39bbfc 100644 --- a/Libraries/Performance/Systrace.js +++ b/Libraries/Performance/Systrace.js @@ -87,11 +87,25 @@ const userTimingPolyfill = __DEV__ } : null; +function installPerformanceHooks(polyfill) { + if (polyfill) { + if (global.performance === undefined) { + global.performance = {}; + } + + Object.keys(polyfill).forEach(methodName => { + if (typeof global.performance[methodName] !== 'function') { + global.performance[methodName] = polyfill[methodName]; + } + }); + } +} + const Systrace = { installReactHook() { if (_enabled) { if (__DEV__) { - global.performance = userTimingPolyfill; + installPerformanceHooks(userTimingPolyfill); } } _canInstallReactHook = true; @@ -108,8 +122,8 @@ const Systrace = { global.nativeTraceEndLegacy(TRACE_TAG_JS_VM_CALLS); } if (_canInstallReactHook) { - if (enabled && global.performance === undefined) { - global.performance = userTimingPolyfill; + if (enabled) { + installPerformanceHooks(userTimingPolyfill); } } } diff --git a/React/CxxBridge/JSCExecutorFactory.mm b/React/CxxBridge/JSCExecutorFactory.mm index 411a083dc3..d93a28077b 100644 --- a/React/CxxBridge/JSCExecutorFactory.mm +++ b/React/CxxBridge/JSCExecutorFactory.mm @@ -24,6 +24,13 @@ std::unique_ptr JSCExecutorFactory::createJSExecutor( _RCTLogJavaScriptInternal(static_cast(logLevel), [NSString stringWithUTF8String:message.c_str()]); }; react::bindNativeLogger(runtime, iosLoggingBinder); + + react::PerformanceNow iosPerformanceNowBinder = []() { + // CACurrentMediaTime() returns the current absolute time, in seconds + return CACurrentMediaTime() * 1000; + }; + react::bindNativePerformanceNow(runtime, iosPerformanceNowBinder); + // Wrap over the original runtimeInstaller if (runtimeInstaller) { runtimeInstaller(runtime); diff --git a/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/OnLoad.cpp b/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/OnLoad.cpp index 8b4dabd0f6..d6cbef092d 100644 --- a/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/OnLoad.cpp +++ b/ReactAndroid/src/main/java/com/facebook/hermes/reactexecutor/OnLoad.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -48,6 +49,10 @@ static void installBindings(jsi::Runtime &runtime) { static_cast( &reactAndroidLoggingHook); react::bindNativeLogger(runtime, androidLogger); + + react::PerformanceNow androidNativePerformanceNow = + static_cast(&reactAndroidNativePerformanceNowHook); + react::bindNativePerformanceNow(runtime, androidNativePerformanceNow); } class HermesExecutorHolder diff --git a/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/OnLoad.cpp b/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/OnLoad.cpp index f4e1783fee..e05dfec2ef 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/OnLoad.cpp +++ b/ReactAndroid/src/main/java/com/facebook/react/jscexecutor/OnLoad.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -30,6 +31,10 @@ class JSCExecutorFactory : public JSExecutorFactory { static_cast( &reactAndroidLoggingHook); react::bindNativeLogger(runtime, androidLogger); + + react::PerformanceNow androidNativePerformanceNow = + static_cast(&reactAndroidNativePerformanceNowHook); + react::bindNativePerformanceNow(runtime, androidNativePerformanceNow); }; return std::make_unique( jsc::makeJSCRuntime(), diff --git a/ReactAndroid/src/main/jni/react/jni/NativeTime.cpp b/ReactAndroid/src/main/jni/react/jni/NativeTime.cpp new file mode 100644 index 0000000000..05eb72b49b --- /dev/null +++ b/ReactAndroid/src/main/jni/react/jni/NativeTime.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "NativeTime.h" +#include + +namespace facebook { +namespace react { + +double reactAndroidNativePerformanceNowHook() { + auto time = std::chrono::steady_clock::now(); + auto duration = std::chrono::duration_cast( + time.time_since_epoch()) + .count(); + + constexpr double NANOSECONDS_IN_MILLISECOND = 1000000.0; + + return duration / NANOSECONDS_IN_MILLISECOND; +} + +} // namespace react +} // namespace facebook diff --git a/ReactAndroid/src/main/jni/react/jni/NativeTime.h b/ReactAndroid/src/main/jni/react/jni/NativeTime.h new file mode 100644 index 0000000000..b4d028dd88 --- /dev/null +++ b/ReactAndroid/src/main/jni/react/jni/NativeTime.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) Facebook, Inc. and its 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 + +namespace facebook { +namespace react { + +double reactAndroidNativePerformanceNowHook(); + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp b/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp index 0cf45f52df..3fb689543d 100644 --- a/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp +++ b/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp @@ -483,5 +483,20 @@ void bindNativeLogger(Runtime &runtime, Logger logger) { })); } +void bindNativePerformanceNow(Runtime &runtime, PerformanceNow performanceNow) { + runtime.global().setProperty( + runtime, + "nativePerformanceNow", + Function::createFromHostFunction( + runtime, + PropNameID::forAscii(runtime, "nativePerformanceNow"), + 0, + [performanceNow = std::move(performanceNow)]( + jsi::Runtime &runtime, + const jsi::Value &, + const jsi::Value *args, + size_t count) { return Value(performanceNow()); })); +} + } // namespace react } // namespace facebook diff --git a/ReactCommon/jsiexecutor/jsireact/JSIExecutor.h b/ReactCommon/jsiexecutor/jsireact/JSIExecutor.h index 48333c45c1..8245e3fd3f 100644 --- a/ReactCommon/jsiexecutor/jsireact/JSIExecutor.h +++ b/ReactCommon/jsiexecutor/jsireact/JSIExecutor.h @@ -135,5 +135,10 @@ class JSIExecutor : public JSExecutor { using Logger = std::function; void bindNativeLogger(jsi::Runtime &runtime, Logger logger); + +using PerformanceNow = std::function; +void bindNativePerformanceNow( + jsi::Runtime &runtime, + PerformanceNow performanceNow); } // namespace react } // namespace facebook