2143
const {key} = fiber;
2144
const displayName = getDisplayNameForFiber(fiber);
2145
const elementType = getElementTypeForFiber(fiber);
2146
- const debugOwner = fiber._debugOwner;
2147
-
2148
- // Ideally we should call getFiberIDThrows() for _debugOwner,
2149
- // since owners are almost always higher in the tree (and so have already been processed),
2150
- // but in some (rare) instances reported in open source, a descendant mounts before an owner.
2151
- // Since this is a DEV only field it's probably okay to also just lazily generate and ID here if needed.
2152
- // See https://github.com/facebook/react/issues/21445
2153
- let ownerID: number;
2154
- if (debugOwner != null) {
2155
- if (typeof debugOwner.tag === 'number') {
2156
- const ownerFiberInstance = getFiberInstanceUnsafe((debugOwner: any));
2157
- if (ownerFiberInstance !== null) {
2158
- ownerID = ownerFiberInstance.id;
2159
- } else {
2160
- ownerID = 0;
2161
- }
2162
- } else {
2163
- // TODO: Track Server Component Owners.
2164
- ownerID = 0;
2165
- }
2166
- } else {
2167
- ownerID = 0;
2168
- }
2146
+
2147
+ // Finding the owner instance might require traversing the whole parent path which
2148
+ // doesn't have great big O notation. Ideally we'd lazily fetch the owner when we
2149
+ // need it but we have some synchronous operations in the front end like Alt+Left
2150
+ // which selects the owner immediately. Typically most owners are only a few parents
2151
+ // away so maybe it's not so bad.
2152
+ const debugOwner = getUnfilteredOwner(fiber);
2153
+ const ownerInstance = findNearestOwnerInstance(
2154
+ parentInstance,
2155
+ debugOwner,
2156
+ );
2157
+ const ownerID = ownerInstance === null ? 0 : ownerInstance.id;
2158
const parentID = parentInstance ? parentInstance.id : 0;
2159
2160
const displayNameStringID = getStringID(displayName);
2220
displayName = env + '(' + displayName + ')';
2221
}
2222
const elementType = ElementTypeVirtual;
2234
- // TODO: Support Virtual Owners. To do this we need to find a matching
2235
- // virtual instance which is not a super cheap parent traversal and so
2236
- // we should ideally only do that lazily. We should maybe change the
2237
- // frontend to get it lazily.
2238
- const ownerID: number = 0;
2223
+
2224
+ // Finding the owner instance might require traversing the whole parent path which
2225
+ // doesn't have great big O notation. Ideally we'd lazily fetch the owner when we
2226
+ // need it but we have some synchronous operations in the front end like Alt+Left
2227
+ // which selects the owner immediately. Typically most owners are only a few parents
2228
+ // away so maybe it's not so bad.
2229
+ const debugOwner = getUnfilteredOwner(componentInfo);
2230
+ const ownerInstance = findNearestOwnerInstance(parentInstance, debugOwner);
2231
+ const ownerID = ownerInstance === null ? 0 : ownerInstance.id;
2232
const parentID = parentInstance ? parentInstance.id : 0;
2233
2234
const displayNameStringID = getStringID(displayName);
3347
}
3348
3349
function getUpdatersList(root: any): Array<SerializedElement> | null {
3357
- return root.memoizedUpdaters != null
3358
- ? Array.from(root.memoizedUpdaters)
3359
- .filter(fiber => getFiberIDUnsafe(fiber) !== null)
3360
- .map(fiberToSerializedElement)
3361
- : null;
3350
+ const updaters = root.memoizedUpdaters;
3351
+ if (updaters == null) {
3352
+ return null;
3353
+ }
3354
+ const result = [];
3355
+ // eslint-disable-next-line no-for-of-loops/no-for-of-loops
3356
+ for (const updater of updaters) {
3357
+ const inst = getFiberInstanceUnsafe(updater);
3358
+ if (inst !== null) {
3359
+ result.push(instanceToSerializedElement(inst));
3360
+ }
3361
+ }
3362
+ return result;
3363
}
3364
3365
function handleCommitFiberUnmount(fiber: any) {
3924
}
3925
}
3926
3926
- function fiberToSerializedElement(fiber: Fiber): SerializedElement {
3927
- return {
3928
- displayName: getDisplayNameForFiber(fiber) || 'Anonymous',
3929
- id: getFiberIDThrows(fiber),
3930
- key: fiber.key,
3931
- type: getElementTypeForFiber(fiber),
3932
- };
3927
+ function instanceToSerializedElement(
3928
+ instance: DevToolsInstance,
3929
+ ): SerializedElement {
3930
+ if (instance.kind === FIBER_INSTANCE) {
3931
+ const fiber = instance.data;
3932
+ return {
3933
+ displayName: getDisplayNameForFiber(fiber) || 'Anonymous',
3934
+ id: instance.id,
3935
+ key: fiber.key,
3936
+ type: getElementTypeForFiber(fiber),
3937
+ };
3938
+ } else {
3939
+ const componentInfo = instance.data;
3940
+ return {
3941
+ displayName: componentInfo.name || 'Anonymous',
3942
+ id: instance.id,
3943
+ key: componentInfo.key == null ? null : componentInfo.key,
3944
+ type: ElementTypeVirtual,
3945
+ };
3946
+ }
3947
}
3948
3949
function getOwnersList(id: number): Array<SerializedElement> | null {
3952
console.warn(`Could not find DevToolsInstance with id "${id}"`);
3953
return null;
3954
}
3941
- if (devtoolsInstance.kind !== FIBER_INSTANCE) {
3942
- // TODO: Handle VirtualInstance.
3943
- return null;
3955
+ const self = instanceToSerializedElement(devtoolsInstance);
3956
+ const owners = getOwnersListFromInstance(devtoolsInstance);
3957
+ // This is particular API is prefixed with the current instance too for some reason.
3958
+ if (owners === null) {
3959
+ return [self];
3960
}
3945
- const fiber =
3946
- findCurrentFiberUsingSlowPathByFiberInstance(devtoolsInstance);
3947
- if (fiber == null) {
3961
+ owners.unshift(self);
3962
+ owners.reverse();
3963
+ return owners;
3964
+ }
3965
+
3966
+ function getOwnersListFromInstance(
3967
+ instance: DevToolsInstance,
3968
+ ): Array<SerializedElement> | null {
3969
+ let owner = getUnfilteredOwner(instance.data);
3970
+ if (owner === null) {
3971
return null;
3972
}
3973
+ const owners: Array<SerializedElement> = [];
3974
+ let parentInstance: null | DevToolsInstance = instance.parent;
3975
+ while (parentInstance !== null && owner !== null) {
3976
+ const ownerInstance = findNearestOwnerInstance(parentInstance, owner);
3977
+ if (ownerInstance !== null) {
3978
+ owners.push(instanceToSerializedElement(ownerInstance));
3979
+ // Get the next owner and keep searching from the previous match.
3980
+ owner = getUnfilteredOwner(owner);
3981
+ parentInstance = ownerInstance.parent;
3982
+ } else {
3983
+ break;
3984
+ }
3985
+ }
3986
+ return owners;
3987
+ }
3988
3951
- const owners: Array<SerializedElement> = [fiberToSerializedElement(fiber)];
3952
-
3953
- let owner = fiber._debugOwner;
3954
- while (owner != null) {
3989
+ function getUnfilteredOwner(
3990
+ owner: ReactComponentInfo | Fiber | null | void,
3991
+ ): ReactComponentInfo | Fiber | null {
3992
+ if (owner == null) {
3993
+ return null;
3994
+ }
3995
+ if (typeof owner.tag === 'number') {
3996
+ const ownerFiber: Fiber = (owner: any); // Refined
3997
+ owner = ownerFiber._debugOwner;
3998
+ } else {
3999
+ const ownerInfo: ReactComponentInfo = (owner: any); // Refined
4000
+ owner = ownerInfo.owner;
4001
+ }
4002
+ while (owner) {
4003
if (typeof owner.tag === 'number') {
4004
const ownerFiber: Fiber = (owner: any); // Refined
4005
if (!shouldFilterFiber(ownerFiber)) {
3958
- owners.unshift(fiberToSerializedElement(ownerFiber));
4006
+ return ownerFiber;
4007
}
4008
owner = ownerFiber._debugOwner;
4009
} else {
3962
- // TODO: Track Server Component Owners.
3963
- break;
4010
+ const ownerInfo: ReactComponentInfo = (owner: any); // Refined
4011
+ if (!shouldFilterVirtual(ownerInfo)) {
4012
+ return ownerInfo;
4013
+ }
4014
+ owner = ownerInfo.owner;
4015
}
4016
}
4017
+ return null;
4018
+ }
4019
3967
- return owners;
4020
+ function findNearestOwnerInstance(
4021
+ parentInstance: null | DevToolsInstance,
4022
+ owner: void | null | ReactComponentInfo | Fiber,
4023
+ ): null | DevToolsInstance {
4024
+ if (owner == null) {
4025
+ return null;
4026
+ }
4027
+ // Search the parent path for any instance that matches this kind of owner.
4028
+ while (parentInstance !== null) {
4029
+ if (
4030
+ parentInstance.data === owner ||
4031
+ // Typically both owner and instance.data would refer to the current version of a Fiber
4032
+ // but it is possible for memoization to ignore the owner on the JSX. Then the new Fiber
4033
+ // isn't propagated down as the new owner. In that case we might match the alternate
4034
+ // instead. This is a bit hacky but the fastest check since type casting owner to a Fiber
4035
+ // needs a duck type check anyway.
4036
+ parentInstance.data === (owner: any).alternate
4037
+ ) {
4038
+ return parentInstance;
4039
+ }
4040
+ parentInstance = parentInstance.parent;
4041
+ }
4042
+ // It is technically possible to create an element and render it in a different parent
4043
+ // but this is a weird edge case and it is worth not having to scan the tree or keep
4044
+ // a register for every fiber/component info.
4045
+ return null;
4046
}
4047
4048
// Fast path props lookup for React Native style editor.
4125
}
4126
4127
const {
4050
- _debugOwner: debugOwner,
4128
stateNode,
4129
key,
4130
memoizedProps,
4251
context = {value: context};
4252
}
4253
4177
- let owners: null | Array<SerializedElement> = null;
4178
- let owner = debugOwner;
4179
- while (owner != null) {
4180
- if (typeof owner.tag === 'number') {
4181
- const ownerFiber: Fiber = (owner: any); // Refined
4182
- if (owners === null) {
4183
- owners = [];
4184
- }
4185
- owners.push(fiberToSerializedElement(ownerFiber));
4186
- owner = ownerFiber._debugOwner;
4187
- } else {
4188
- // TODO: Track Server Component Owners.
4189
- break;
4190
- }
4191
- }
4254
+ const owners: null | Array<SerializedElement> =
4255
+ getOwnersListFromInstance(fiberInstance);
4256
4257
const isTimedOutSuspense =
4258
tag === SuspenseComponent && memoizedState !== null;
4416
displayName = env + '(' + displayName + ')';
4417
}
4418
4355
- // TODO: Support Virtual Owners.
4356
- const owners: null | Array<SerializedElement> = null;
4419
+ const owners: null | Array<SerializedElement> =
4420
+ getOwnersListFromInstance(virtualInstance);
4421
4422
let rootType = null;
4423
let targetErrorBoundaryID = null;