Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add JSLogger class and duplicate SET tags #4675

Merged
merged 19 commits into from
Jul 11, 2023
Merged
41 changes: 41 additions & 0 deletions Common/cpp/LayoutAnimations/LayoutAnimationsManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
#include "CollectionUtils.h"
#include "Shareables.h"

#ifdef DEBUG
#include <utility>
#include "JSLogger.h"
#endif

namespace reanimated {

Expand Down Expand Up @@ -31,6 +34,11 @@ void LayoutAnimationsManager::clearLayoutAnimationConfig(int tag) {
enteringAnimations_.erase(tag);
exitingAnimations_.erase(tag);
layoutAnimations_.erase(tag);
#ifdef DEBUG
const auto &pair = viewsScreenSharedTagMap_[tag];
screenSharedTagSet_.erase(pair);
viewsScreenSharedTagMap_.erase(tag);
#endif
tjzel marked this conversation as resolved.
Show resolved Hide resolved

sharedTransitionAnimations_.erase(tag);
auto const &groupName = viewTagToSharedTag_[tag];
Expand Down Expand Up @@ -112,6 +120,39 @@ int LayoutAnimationsManager::findPrecedingViewTagForTransition(int tag) {
return -1;
}

#ifdef DEBUG
std::string LayoutAnimationsManager::getScreenSharedTagPairString(
const int screenTag,
const std::string &sharedTag) {
return std::to_string(screenTag) + ":" + sharedTag;
}

bool LayoutAnimationsManager::hasDuplicateSharedTag(
const int viewTag,
const int screenTag) {
if (!viewTagToSharedTag_.count(viewTag)) {
return false;
}
auto sharedTag = viewTagToSharedTag_[viewTag];
tjzel marked this conversation as resolved.
Show resolved Hide resolved
auto pair = getScreenSharedTagPairString(screenTag, sharedTag);
tjzel marked this conversation as resolved.
Show resolved Hide resolved
bool hasDuplicate = screenSharedTagSet_.count(pair);
if (hasDuplicate) {
tjzel marked this conversation as resolved.
Show resolved Hide resolved
jsLogger_->warnOnJS(
tjzel marked this conversation as resolved.
Show resolved Hide resolved
"[Reanimated] Duplicate shared tag \"" + sharedTag +
"\" on the same screen");
return true;
}
viewsScreenSharedTagMap_[viewTag] = pair;
screenSharedTagSet_.insert(pair);
return false;
}

void LayoutAnimationsManager::initializeJSLogger(
tjzel marked this conversation as resolved.
Show resolved Hide resolved
const std::shared_ptr<JSLogger> &jsLogger) {
jsLogger_ = jsLogger;
}
#endif // DEBUG

std::unordered_map<int, std::shared_ptr<Shareable>>
&LayoutAnimationsManager::getConfigsForType(LayoutAnimationType type) {
switch (type) {
Expand Down
23 changes: 22 additions & 1 deletion Common/cpp/LayoutAnimations/LayoutAnimationsManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
#include "LayoutAnimationType.h"
#include "Shareables.h"

#ifdef DEBUG
#include <unordered_set>
#include "JSLogger.h"
#endif

#include <jsi/jsi.h>
#include <stdio.h>
#include <functional>
Expand Down Expand Up @@ -38,11 +43,27 @@ class LayoutAnimationsManager {
bool cancelled /* = true */,
bool removeView /* = true */);
int findPrecedingViewTagForTransition(int tag);
#ifdef DEBUG
std::string getScreenSharedTagPairString(
tjzel marked this conversation as resolved.
Show resolved Hide resolved
const int screenTag,
const std::string &sharedTag);
bool hasDuplicateSharedTag(const int viewTag, const int screenTag);
void initializeJSLogger(const std::shared_ptr<JSLogger> &jsLogger);
tjzel marked this conversation as resolved.
Show resolved Hide resolved
#endif
tjzel marked this conversation as resolved.
Show resolved Hide resolved

private:
std::unordered_map<int, std::shared_ptr<Shareable>> &getConfigsForType(
LayoutAnimationType type);

#ifdef DEBUG
std::shared_ptr<JSLogger> jsLogger_;
// This set's function is to detect duplicate sharedTags on a single screen
// it contains strings in form: "reactScreenTag:sharedTag"
std::unordered_set<std::string> screenSharedTagSet_;
// And this map is to remove collected pairs on SET removal
std::unordered_map<int, std::string> viewsScreenSharedTagMap_;
#endif

std::unordered_map<int, std::shared_ptr<Shareable>> enteringAnimations_;
std::unordered_map<int, std::shared_ptr<Shareable>> exitingAnimations_;
std::unordered_map<int, std::shared_ptr<Shareable>> layoutAnimations_;
Expand All @@ -52,7 +73,7 @@ class LayoutAnimationsManager {
std::unordered_map<int, std::string> viewTagToSharedTag_;
mutable std::mutex
animationsMutex_; // Protects `enteringAnimations_`, `exitingAnimations_`,
// `layoutAnimations_` and `viewSharedValues_`.
// `layoutAnimations_` and `viewSharedValues_`.
};

} // namespace reanimated
10 changes: 10 additions & 0 deletions Common/cpp/NativeModules/NativeReanimatedModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
#include "Shareables.h"
#include "WorkletEventHandler.h"

#ifdef DEBUG
#include "JSLogger.h"
#endif

using namespace facebook;

namespace reanimated {
Expand Down Expand Up @@ -164,6 +168,12 @@ void NativeReanimatedModule::installCoreFunctions(
std::make_unique<CoreFunction>(runtimeHelper.get(), callGuard);
runtimeHelper->valueUnpacker =
std::make_unique<CoreFunction>(runtimeHelper.get(), valueUnpacker);
#ifdef DEBUG
// We initialize jsLogger_ here because we need runtimeHelper
// to be initialized already
jsLogger_ = std::make_shared<JSLogger>(runtimeHelper);
layoutAnimationsManager_.initializeJSLogger(jsLogger_);
#endif
}

NativeReanimatedModule::~NativeReanimatedModule() {
Expand Down
1 change: 1 addition & 0 deletions Common/cpp/NativeModules/NativeReanimatedModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ class NativeReanimatedModule : public NativeReanimatedModuleSpec {
KeyboardEventUnsubscribeFunction unsubscribeFromKeyboardEventsFunction;

#ifdef DEBUG
std::shared_ptr<JSLogger> jsLogger_;
SingleInstanceChecker<NativeReanimatedModule> singleInstanceChecker_;
#endif
};
Expand Down
21 changes: 21 additions & 0 deletions Common/cpp/Tools/JSLogger.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifdef DEBUG
tjzel marked this conversation as resolved.
Show resolved Hide resolved

#include "JSLogger.h"
#include <memory>

namespace reanimated {
tjzel marked this conversation as resolved.
Show resolved Hide resolved
JSLogger::JSLogger(const std::shared_ptr<JSRuntimeHelper> &runtimeHelper)
: runtimeHelper_(runtimeHelper) {}

void JSLogger::warnOnJS(const std::string &warning) const {
runtimeHelper_->scheduleOnJS(
[warning = warning, &runtimeHelper_ = runtimeHelper_]() {
tjzel marked this conversation as resolved.
Show resolved Hide resolved
jsi::Runtime &rt = *(runtimeHelper_->rnRuntime());
auto console = rt.global().getPropertyAsObject(rt, "console");
auto warn = console.getPropertyAsFunction(rt, "warn");
warn.call(rt, jsi::String::createFromUtf8(rt, warning));
});
tjzel marked this conversation as resolved.
Show resolved Hide resolved
}
} // namespace reanimated
tjzel marked this conversation as resolved.
Show resolved Hide resolved

#endif // DEBUG
24 changes: 24 additions & 0 deletions Common/cpp/Tools/JSLogger.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifdef DEBUG

#pragma once

#include "LayoutAnimationType.h"
#include "Shareables.h"

#include <memory>
#include <string>

namespace reanimated {

class JSLogger {
public:
explicit JSLogger(const std::shared_ptr<JSRuntimeHelper> &runtimeHelper);
void warnOnJS(const std::string &warning) const;

private:
const std::shared_ptr<JSRuntimeHelper> runtimeHelper_;
};

} // namespace reanimated

#endif // DEBUG
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ public void clearAnimationConfig(int tag) {}

@Override
public void cancelAnimation(int tag, int type, boolean cancelled, boolean removeView) {}

@Override
public boolean hasDuplicateSharedTag(int viewTag, int screenTag) { return false; }
};
}
}
15 changes: 15 additions & 0 deletions android/src/main/cpp/LayoutAnimations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@ void LayoutAnimations::setHasAnimationBlock(
this->hasAnimationBlock_ = hasAnimationBlock;
}

#ifdef DEBUG
void LayoutAnimations::setHasDuplicateSharedTag(
HasDuplicateSharedTag hasDuplicateSharedTag) {
hasDuplicateSharedTag_ = hasDuplicateSharedTag;
}

bool LayoutAnimations::hasDuplicateSharedTag(int viewTag, int screenTag) {
return hasDuplicateSharedTag_(viewTag, screenTag);
}
#endif

bool LayoutAnimations::hasAnimationForTag(int tag, int type) {
return hasAnimationBlock_(tag, type);
}
Expand Down Expand Up @@ -110,6 +121,10 @@ void LayoutAnimations::registerNatives() {
makeNativeMethod(
"findPrecedingViewTagForTransition",
LayoutAnimations::findPrecedingViewTagForTransition),
#ifdef DEBUG
makeNativeMethod(
"hasDuplicateSharedTag", LayoutAnimations::hasDuplicateSharedTag),
#endif
});
}
}; // namespace reanimated
10 changes: 10 additions & 0 deletions android/src/main/cpp/LayoutAnimations.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ class LayoutAnimations : public jni::HybridClass<LayoutAnimations> {
using AnimationStartingBlock =
std::function<void(int, int, alias_ref<JMap<jstring, jstring>>)>;
using HasAnimationBlock = std::function<bool(int, int)>;
#ifdef DEBUG
using HasDuplicateSharedTag = std::function<bool(int, int)>;
#endif
using ClearAnimationConfigBlock = std::function<void(int)>;
using CancelAnimationBlock =
std::function<void(int, int, jboolean, jboolean)>;
Expand All @@ -36,6 +39,10 @@ class LayoutAnimations : public jni::HybridClass<LayoutAnimations> {

void setAnimationStartingBlock(AnimationStartingBlock animationStartingBlock);
void setHasAnimationBlock(HasAnimationBlock hasAnimationBlock);
#ifdef DEBUG
void setHasDuplicateSharedTag(HasDuplicateSharedTag hasDuplicateSharedTag);
bool hasDuplicateSharedTag(int viewTag, int screenTag);
#endif
void setClearAnimationConfigBlock(
ClearAnimationConfigBlock clearAnimationConfigBlock);
void setCancelAnimationForTag(CancelAnimationBlock cancelAnimationBlock);
Expand Down Expand Up @@ -65,6 +72,9 @@ class LayoutAnimations : public jni::HybridClass<LayoutAnimations> {
CancelAnimationBlock cancelAnimationBlock_;
FindPrecedingViewTagForTransitionBlock
findPrecedingViewTagForTransitionBlock_;
#ifdef DEBUG
HasDuplicateSharedTag hasDuplicateSharedTag_;
#endif

explicit LayoutAnimations(
jni::alias_ref<LayoutAnimations::jhybridobject> jThis);
Expand Down
13 changes: 13 additions & 0 deletions android/src/main/cpp/NativeProxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,19 @@ void NativeProxy::setupLayoutAnimations() {
tag, static_cast<LayoutAnimationType>(type));
});

#ifdef DEBUG
layoutAnimations_->cthis()->setHasDuplicateSharedTag(
[weakNativeReanimatedModule](int viewTag, int screenTag) {
auto nativeReanimatedModule = weakNativeReanimatedModule.lock();
if (nativeReanimatedModule == nullptr) {
return false;
}

return nativeReanimatedModule->layoutAnimationsManager()
.hasDuplicateSharedTag(viewTag, screenTag);
});
#endif

layoutAnimations_->cthis()->setClearAnimationConfigBlock(
[weakNativeReanimatedModule](int tag) {
auto nativeReanimatedModule = weakNativeReanimatedModule.lock();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import com.facebook.react.BuildConfig;
import com.facebook.react.bridge.JavaOnlyMap;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
Expand Down Expand Up @@ -178,6 +179,27 @@ public void maybeRegisterSharedView(View view) {
if (hasAnimationForTag(view.getId(), LayoutAnimations.Types.SHARED_ELEMENT_TRANSITION)) {
mSharedTransitionManager.notifyAboutNewView(view);
}
if (BuildConfig.DEBUG) {
hasDuplicateSharedTag(view);
}
}

private boolean hasDuplicateSharedTag(View view) {
int viewTag = view.getId();

ViewParent parent = view.getParent();
while (parent != null) {
if (parent.getClass().getSimpleName().equals("Screen")) {
break;
}
parent = (ViewParent) parent.getParent();
}
tjzel marked this conversation as resolved.
Show resolved Hide resolved

if (parent != null) {
int screenTag = ((View) parent).getId();
return mNativeMethodsHolder.hasDuplicateSharedTag(viewTag, screenTag);
}
return false;
}

public void progressLayoutAnimation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public LayoutAnimations(ReactApplicationContext context) {

public native boolean hasAnimationForTag(int tag, int type);

public native boolean hasDuplicateSharedTag(int viewTag, int screenTag);

public native void clearAnimationConfigForTag(int tag);

public native void cancelAnimationForTag(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ public interface NativeMethodsHolder {
boolean isLayoutAnimationEnabled();

int findPrecedingViewTagForTransition(int tag);

boolean hasDuplicateSharedTag(int viewTag, int screenTag);
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,14 @@ public int findPrecedingViewTagForTransition(int tag) {
}
return -1;
}

public boolean hasDuplicateSharedTag(int viewTag, int screenTag) {
LayoutAnimations layoutAnimations = weakLayoutAnimations.get();
if (layoutAnimations != null) {
return layoutAnimations.hasDuplicateSharedTag(viewTag, screenTag);
}
return false;
}
};
}
}
Loading