in ReactAndroid/src/main/java/com/facebook/react/fabric/jni/FabricMountingManager.cpp [265:814]
void FabricMountingManager::executeMount(
MountingCoordinator::Shared const &mountingCoordinator) {
std::lock_guard<std::recursive_mutex> lock(commitMutex_);
SystraceSection s(
"FabricUIManagerBinding::schedulerDidFinishTransactionIntBuffer");
auto finishTransactionStartTime = telemetryTimePointNow();
auto mountingTransaction = mountingCoordinator->pullTransaction();
if (!mountingTransaction.has_value()) {
return;
}
auto env = Environment::current();
auto telemetry = mountingTransaction->getTelemetry();
auto surfaceId = mountingTransaction->getSurfaceId();
auto &mutations = mountingTransaction->getMutations();
auto revisionNumber = telemetry.getRevisionNumber();
std::vector<CppMountItem> cppCommonMountItems;
std::vector<CppMountItem> cppDeleteMountItems;
std::vector<CppMountItem> cppUpdatePropsMountItems;
std::vector<CppMountItem> cppUpdateStateMountItems;
std::vector<CppMountItem> cppUpdatePaddingMountItems;
std::vector<CppMountItem> cppUpdateLayoutMountItems;
std::vector<CppMountItem> cppUpdateOverflowInsetMountItems;
std::vector<CppMountItem> cppUpdateEventEmitterMountItems;
{
std::lock_guard allocatedViewsLock(allocatedViewsMutex_);
auto allocatedViewsIterator = allocatedViewRegistry_.find(surfaceId);
auto const &allocatedViewTags =
allocatedViewsIterator != allocatedViewRegistry_.end()
? allocatedViewsIterator->second
: butter::set<Tag>{};
if (allocatedViewsIterator == allocatedViewRegistry_.end()) {
LOG(ERROR) << "Executing commit after surface was stopped!";
}
bool noRevisionCheck =
disablePreallocateViews_ || disableRevisionCheckForPreallocation_;
for (const auto &mutation : mutations) {
const auto &parentShadowView = mutation.parentShadowView;
const auto &oldChildShadowView = mutation.oldChildShadowView;
const auto &newChildShadowView = mutation.newChildShadowView;
auto &mutationType = mutation.type;
auto &index = mutation.index;
bool isVirtual = mutation.mutatedViewIsVirtual();
switch (mutationType) {
case ShadowViewMutation::Create: {
bool revisionCheck =
noRevisionCheck || newChildShadowView.props->revision > 1;
bool allocationCheck =
!allocatedViewTags.contains(newChildShadowView.tag);
bool shouldCreateView =
shouldRememberAllocatedViews_ ? allocationCheck : revisionCheck;
if (shouldCreateView) {
cppCommonMountItems.push_back(
CppMountItem::CreateMountItem(newChildShadowView));
}
break;
}
case ShadowViewMutation::Remove: {
if (!isVirtual) {
cppCommonMountItems.push_back(CppMountItem::RemoveMountItem(
parentShadowView, oldChildShadowView, index));
}
break;
}
case ShadowViewMutation::Delete: {
cppDeleteMountItems.push_back(
CppMountItem::DeleteMountItem(oldChildShadowView));
break;
}
case ShadowViewMutation::Update: {
if (!isVirtual) {
if (oldChildShadowView.props != newChildShadowView.props) {
cppUpdatePropsMountItems.push_back(
CppMountItem::UpdatePropsMountItem(
oldChildShadowView, newChildShadowView));
}
if (oldChildShadowView.state != newChildShadowView.state) {
cppUpdateStateMountItems.push_back(
CppMountItem::UpdateStateMountItem(newChildShadowView));
}
// Padding: padding mountItems must be executed before layout props
// are updated in the view. This is necessary to ensure that events
// (resulting from layout changes) are dispatched with the correct
// padding information.
if (oldChildShadowView.layoutMetrics.contentInsets !=
newChildShadowView.layoutMetrics.contentInsets) {
cppUpdatePaddingMountItems.push_back(
CppMountItem::UpdatePaddingMountItem(newChildShadowView));
}
if (oldChildShadowView.layoutMetrics !=
newChildShadowView.layoutMetrics) {
cppUpdateLayoutMountItems.push_back(
CppMountItem::UpdateLayoutMountItem(
mutation.newChildShadowView));
}
// OverflowInset: This is the values indicating boundaries including
// children of the current view. The layout of current view may not
// change, and we separate this part from layout mount items to not
// pack too much data there.
if (useOverflowInset_ &&
(oldChildShadowView.layoutMetrics.overflowInset !=
newChildShadowView.layoutMetrics.overflowInset)) {
cppUpdateOverflowInsetMountItems.push_back(
CppMountItem::UpdateOverflowInsetMountItem(
newChildShadowView));
}
}
if (oldChildShadowView.eventEmitter !=
newChildShadowView.eventEmitter) {
cppUpdateEventEmitterMountItems.push_back(
CppMountItem::UpdateEventEmitterMountItem(
mutation.newChildShadowView));
}
break;
}
case ShadowViewMutation::Insert: {
if (!isVirtual) {
// Insert item
cppCommonMountItems.push_back(CppMountItem::InsertMountItem(
parentShadowView, newChildShadowView, index));
bool revisionCheck =
noRevisionCheck || newChildShadowView.props->revision > 1;
bool allocationCheck =
allocatedViewTags.find(newChildShadowView.tag) ==
allocatedViewTags.end();
bool shouldCreateView =
shouldRememberAllocatedViews_ ? allocationCheck : revisionCheck;
if (shouldCreateView) {
cppUpdatePropsMountItems.push_back(
CppMountItem::UpdatePropsMountItem({}, newChildShadowView));
}
// State
if (newChildShadowView.state) {
cppUpdateStateMountItems.push_back(
CppMountItem::UpdateStateMountItem(newChildShadowView));
}
// Padding: padding mountItems must be executed before layout props
// are updated in the view. This is necessary to ensure that events
// (resulting from layout changes) are dispatched with the correct
// padding information.
cppUpdatePaddingMountItems.push_back(
CppMountItem::UpdatePaddingMountItem(
mutation.newChildShadowView));
// Layout
cppUpdateLayoutMountItems.push_back(
CppMountItem::UpdateLayoutMountItem(
mutation.newChildShadowView));
// OverflowInset: This is the values indicating boundaries including
// children of the current view. The layout of current view may not
// change, and we separate this part from layout mount items to not
// pack too much data there.
if (useOverflowInset_) {
cppUpdateOverflowInsetMountItems.push_back(
CppMountItem::UpdateOverflowInsetMountItem(
newChildShadowView));
}
}
// EventEmitter
cppUpdateEventEmitterMountItems.push_back(
CppMountItem::UpdateEventEmitterMountItem(
mutation.newChildShadowView));
break;
}
default: {
break;
}
}
}
if (shouldRememberAllocatedViews_ &&
allocatedViewsIterator != allocatedViewRegistry_.end()) {
auto &views = allocatedViewsIterator->second;
for (auto const &mutation : mutations) {
switch (mutation.type) {
case ShadowViewMutation::Create:
views.insert(mutation.newChildShadowView.tag);
break;
case ShadowViewMutation::Delete:
views.erase(mutation.oldChildShadowView.tag);
break;
default:
break;
}
}
}
}
// We now have all the information we need, including ordering of mount items,
// to know exactly how much space must be allocated
int batchMountItemIntsSize = 0;
int batchMountItemObjectsSize = 0;
computeBufferSizes(
batchMountItemIntsSize,
batchMountItemObjectsSize,
cppCommonMountItems,
cppDeleteMountItems,
cppUpdatePropsMountItems,
cppUpdateStateMountItems,
cppUpdatePaddingMountItems,
cppUpdateLayoutMountItems,
cppUpdateOverflowInsetMountItems,
cppUpdateEventEmitterMountItems);
static auto createMountItemsIntBufferBatchContainer =
jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<alias_ref<JMountItem>(
jint, jintArray, jtypeArray<jobject>, jint)>(
"createIntBufferBatchMountItem");
static auto scheduleMountItem = jni::findClassStatic(UIManagerJavaDescriptor)
->getMethod<void(
JMountItem::javaobject,
jint,
jlong,
jlong,
jlong,
jlong,
jlong,
jlong,
jlong)>("scheduleMountItem");
if (batchMountItemIntsSize == 0) {
auto finishTransactionEndTime = telemetryTimePointNow();
scheduleMountItem(
javaUIManager_,
nullptr,
telemetry.getRevisionNumber(),
telemetryTimePointToMilliseconds(telemetry.getCommitStartTime()),
telemetryTimePointToMilliseconds(telemetry.getDiffStartTime()),
telemetryTimePointToMilliseconds(telemetry.getDiffEndTime()),
telemetryTimePointToMilliseconds(telemetry.getLayoutStartTime()),
telemetryTimePointToMilliseconds(telemetry.getLayoutEndTime()),
telemetryTimePointToMilliseconds(finishTransactionStartTime),
telemetryTimePointToMilliseconds(finishTransactionEndTime));
return;
}
// Allocate the intBuffer and object array, now that we know exact sizes
// necessary
jintArray intBufferArray = env->NewIntArray(batchMountItemIntsSize);
local_ref<JArrayClass<jobject>> objBufferArray =
JArrayClass<jobject>::newArray(batchMountItemObjectsSize);
// Fill in arrays
int intBufferPosition = 0;
int objBufferPosition = 0;
int prevMountItemType = -1;
jint temp[6];
for (int i = 0; i < cppCommonMountItems.size(); i++) {
const auto &mountItem = cppCommonMountItems[i];
const auto &mountItemType = mountItem.type;
// Get type here, and count forward how many items of this type are in a
// row. Write preamble to any common type here.
if (prevMountItemType != mountItemType) {
int numSameItemTypes = 1;
for (int j = i + 1; j < cppCommonMountItems.size() &&
cppCommonMountItems[j].type == mountItemType;
j++) {
numSameItemTypes++;
}
writeIntBufferTypePreamble(
mountItemType,
numSameItemTypes,
env,
intBufferArray,
intBufferPosition);
}
prevMountItemType = mountItemType;
// TODO: multi-create, multi-insert, etc
if (mountItemType == CppMountItem::Type::Create) {
local_ref<JString> componentName =
getPlatformComponentName(mountItem.newChildShadowView);
int isLayoutable =
mountItem.newChildShadowView.layoutMetrics != EmptyLayoutMetrics ? 1
: 0;
local_ref<JObject> props =
getProps(mountItem.oldChildShadowView, mountItem.newChildShadowView);
// Do not hold onto Java object from C
// We DO want to hold onto C object from Java, since we don't know the
// lifetime of the Java object
local_ref<StateWrapperImpl::JavaPart> javaStateWrapper = nullptr;
if (mountItem.newChildShadowView.state != nullptr) {
javaStateWrapper = StateWrapperImpl::newObjectJavaArgs();
StateWrapperImpl *cStateWrapper = cthis(javaStateWrapper);
cStateWrapper->state_ = mountItem.newChildShadowView.state;
}
// Do not hold a reference to javaEventEmitter from the C++ side.
SharedEventEmitter eventEmitter =
mountItem.newChildShadowView.eventEmitter;
auto javaEventEmitter = EventEmitterWrapper::newObjectJavaArgs();
EventEmitterWrapper *cEventEmitter = cthis(javaEventEmitter);
cEventEmitter->eventEmitter = eventEmitter;
temp[0] = mountItem.newChildShadowView.tag;
temp[1] = isLayoutable;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 2, temp);
intBufferPosition += 2;
(*objBufferArray)[objBufferPosition++] = componentName.get();
(*objBufferArray)[objBufferPosition++] = props.get();
(*objBufferArray)[objBufferPosition++] =
javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr;
(*objBufferArray)[objBufferPosition++] = javaEventEmitter.get();
} else if (mountItemType == CppMountItem::Type::Insert) {
temp[0] = mountItem.newChildShadowView.tag;
temp[1] = mountItem.parentShadowView.tag;
temp[2] = mountItem.index;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 3, temp);
intBufferPosition += 3;
} else if (mountItemType == CppMountItem::Remove) {
temp[0] = mountItem.oldChildShadowView.tag;
temp[1] = mountItem.parentShadowView.tag;
temp[2] = mountItem.index;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 3, temp);
intBufferPosition += 3;
} else {
LOG(ERROR) << "Unexpected CppMountItem type";
}
}
if (!cppUpdatePropsMountItems.empty()) {
writeIntBufferTypePreamble(
CppMountItem::Type::UpdateProps,
cppUpdatePropsMountItems.size(),
env,
intBufferArray,
intBufferPosition);
for (const auto &mountItem : cppUpdatePropsMountItems) {
temp[0] = mountItem.newChildShadowView.tag;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 1, temp);
intBufferPosition += 1;
(*objBufferArray)[objBufferPosition++] =
getProps(mountItem.oldChildShadowView, mountItem.newChildShadowView);
}
}
if (!cppUpdateStateMountItems.empty()) {
writeIntBufferTypePreamble(
CppMountItem::Type::UpdateState,
cppUpdateStateMountItems.size(),
env,
intBufferArray,
intBufferPosition);
for (const auto &mountItem : cppUpdateStateMountItems) {
temp[0] = mountItem.newChildShadowView.tag;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 1, temp);
intBufferPosition += 1;
auto state = mountItem.newChildShadowView.state;
// Do not hold onto Java object from C
// We DO want to hold onto C object from Java, since we don't know the
// lifetime of the Java object
local_ref<StateWrapperImpl::JavaPart> javaStateWrapper = nullptr;
if (state != nullptr) {
javaStateWrapper = StateWrapperImpl::newObjectJavaArgs();
StateWrapperImpl *cStateWrapper = cthis(javaStateWrapper);
cStateWrapper->state_ = state;
}
(*objBufferArray)[objBufferPosition++] =
(javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr);
}
}
if (!cppUpdatePaddingMountItems.empty()) {
writeIntBufferTypePreamble(
CppMountItem::Type::UpdatePadding,
cppUpdatePaddingMountItems.size(),
env,
intBufferArray,
intBufferPosition);
for (const auto &mountItem : cppUpdatePaddingMountItems) {
auto layoutMetrics = mountItem.newChildShadowView.layoutMetrics;
auto pointScaleFactor = layoutMetrics.pointScaleFactor;
auto contentInsets = layoutMetrics.contentInsets;
int left = floor(scale(contentInsets.left, pointScaleFactor));
int top = floor(scale(contentInsets.top, pointScaleFactor));
int right = floor(scale(contentInsets.right, pointScaleFactor));
int bottom = floor(scale(contentInsets.bottom, pointScaleFactor));
temp[0] = mountItem.newChildShadowView.tag;
temp[1] = left;
temp[2] = top;
temp[3] = right;
temp[4] = bottom;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 5, temp);
intBufferPosition += 5;
}
}
if (!cppUpdateLayoutMountItems.empty()) {
writeIntBufferTypePreamble(
CppMountItem::Type::UpdateLayout,
cppUpdateLayoutMountItems.size(),
env,
intBufferArray,
intBufferPosition);
for (const auto &mountItem : cppUpdateLayoutMountItems) {
auto layoutMetrics = mountItem.newChildShadowView.layoutMetrics;
auto pointScaleFactor = layoutMetrics.pointScaleFactor;
auto frame = layoutMetrics.frame;
int x = round(scale(frame.origin.x, pointScaleFactor));
int y = round(scale(frame.origin.y, pointScaleFactor));
int w = round(scale(frame.size.width, pointScaleFactor));
int h = round(scale(frame.size.height, pointScaleFactor));
int displayType =
toInt(mountItem.newChildShadowView.layoutMetrics.displayType);
temp[0] = mountItem.newChildShadowView.tag;
temp[1] = x;
temp[2] = y;
temp[3] = w;
temp[4] = h;
temp[5] = displayType;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 6, temp);
intBufferPosition += 6;
}
}
if (!cppUpdateOverflowInsetMountItems.empty()) {
writeIntBufferTypePreamble(
CppMountItem::Type::UpdateOverflowInset,
cppUpdateOverflowInsetMountItems.size(),
env,
intBufferArray,
intBufferPosition);
for (const auto &mountItem : cppUpdateOverflowInsetMountItems) {
auto layoutMetrics = mountItem.newChildShadowView.layoutMetrics;
auto pointScaleFactor = layoutMetrics.pointScaleFactor;
auto overflowInset = layoutMetrics.overflowInset;
int overflowInsetLeft =
round(scale(overflowInset.left, pointScaleFactor));
int overflowInsetTop = round(scale(overflowInset.top, pointScaleFactor));
int overflowInsetRight =
round(scale(overflowInset.right, pointScaleFactor));
int overflowInsetBottom =
round(scale(overflowInset.bottom, pointScaleFactor));
temp[0] = mountItem.newChildShadowView.tag;
temp[1] = overflowInsetLeft;
temp[2] = overflowInsetTop;
temp[3] = overflowInsetRight;
temp[4] = overflowInsetBottom;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 5, temp);
intBufferPosition += 5;
}
}
if (!cppUpdateEventEmitterMountItems.empty()) {
writeIntBufferTypePreamble(
CppMountItem::Type::UpdateEventEmitter,
cppUpdateEventEmitterMountItems.size(),
env,
intBufferArray,
intBufferPosition);
for (const auto &mountItem : cppUpdateEventEmitterMountItems) {
temp[0] = mountItem.newChildShadowView.tag;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 1, temp);
intBufferPosition += 1;
SharedEventEmitter eventEmitter =
mountItem.newChildShadowView.eventEmitter;
// Do not hold a reference to javaEventEmitter from the C++ side.
auto javaEventEmitter = EventEmitterWrapper::newObjectJavaArgs();
EventEmitterWrapper *cEventEmitter = cthis(javaEventEmitter);
cEventEmitter->eventEmitter = eventEmitter;
(*objBufferArray)[objBufferPosition++] = javaEventEmitter.get();
}
}
// Write deletes last - so that all prop updates, etc, for the tag in the same
// batch don't fail. Without additional machinery, moving deletes here
// requires that the differ never produces "DELETE...CREATE" in that order for
// the same tag. It's nice to be able to batch all similar operations together
// for space efficiency.
if (!cppDeleteMountItems.empty()) {
writeIntBufferTypePreamble(
CppMountItem::Type::Delete,
cppDeleteMountItems.size(),
env,
intBufferArray,
intBufferPosition);
for (const auto &mountItem : cppDeleteMountItems) {
temp[0] = mountItem.oldChildShadowView.tag;
env->SetIntArrayRegion(intBufferArray, intBufferPosition, 1, temp);
intBufferPosition += 1;
}
}
// If there are no items, we pass a nullptr instead of passing the object
// through the JNI
auto batch = createMountItemsIntBufferBatchContainer(
javaUIManager_,
surfaceId,
batchMountItemIntsSize == 0 ? nullptr : intBufferArray,
batchMountItemObjectsSize == 0 ? nullptr : objBufferArray.get(),
revisionNumber);
auto finishTransactionEndTime = telemetryTimePointNow();
scheduleMountItem(
javaUIManager_,
batch.get(),
telemetry.getRevisionNumber(),
telemetryTimePointToMilliseconds(telemetry.getCommitStartTime()),
telemetryTimePointToMilliseconds(telemetry.getDiffStartTime()),
telemetryTimePointToMilliseconds(telemetry.getDiffEndTime()),
telemetryTimePointToMilliseconds(telemetry.getLayoutStartTime()),
telemetryTimePointToMilliseconds(telemetry.getLayoutEndTime()),
telemetryTimePointToMilliseconds(finishTransactionStartTime),
telemetryTimePointToMilliseconds(finishTransactionEndTime));
env->DeleteLocalRef(intBufferArray);
}