Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/34869
Changelog: [Internal]
This merges all instances of `enablePropIteratorSetter` into a single one.
Both `AccesibilityProps` and `BaseTextProps` had their own instances if it, which is now redundant.
Reviewed By: cipolleschi
Differential Revision: D40062555
fbshipit-source-id: b6ccf5a9538612dd731a6f9c4eaceeebcb6d95be
Summary:
A follow up to D38708718 (https://github.com/facebook/react-native/commit/403fea25f65a38f4b4d8e0edcf89741b29e62059) review, this factors feature flags for Fabric core code into a separate file, `CoreFeatures`.
Keeping them together is arguably better for maintenance and makes code easier to reason about.
Changelog: [Internal]
Reviewed By: sammy-SC
Differential Revision: D40007784
fbshipit-source-id: 1885d5d6200575c6015f063d8b05813b18b47ffb
Summary:
Previously, ViewPropsMapBuffer conversions were hardcoded deep in Android infrastructrue. I've generalized this into a different mechanism to allow any Props struct to support MapBuffer props.
There are still some things that need to be cleaned up and this should be treated as experimental. One thing we likely want to do is remove the hardcoded IDs (fine for codegen'd code; less so for handwritten) and use compile-time-hashed IDs instead with human-readable string names.
Changelog: [Internal]
Reviewed By: mdvacca
Differential Revision: D38708719
fbshipit-source-id: 64603dee7f21828be31346c555d99862dab304ea
Summary:
Instead of having a special flag just for View MapBuffer props, we now use one flag to indicate that MapBuffer should be used for all props; each XShadowNode must set a special trait indicating if that ShadowNode supports MapBuffer props.
Changelog: [Internal]
Reviewed By: mdvacca
Differential Revision: D38708718
fbshipit-source-id: b398ec62a0db9c0ff23c0007c5503cf2838c4173
Summary:
changelog: [internal]
Add more clang tidy rules to prevent common class of bugs.
Reviewed By: javache
Differential Revision: D39245194
fbshipit-source-id: 5521c5c4653d7005b96ebba494e810ba5075afbc
Summary:
Changelog: [iOS][Internal] - Add iOS implementation of the button property of the PointerEvent object
This implements the `button` property on the PointerEvent object by storing the button which caused the down event in the `ActiveTouch` and reporting that button through `pointerdown` and `pointerup` events and -1 on all others. This diff also includes a small fix to the `pressure` property which was introduced due to `button` being correctly implemented.
Reviewed By: yungsters
Differential Revision: D38632543
fbshipit-source-id: 9dbbb23a9251f2e661faf37fdf206b9f0b26bc5f
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/34403
This change mirrors D38457812 (https://github.com/facebook/react-native/commit/063c2b4668b279ccbca639f98f7a0a5c4d7c5690) which added -Wpedantic to ReactCommon targets, but for the Android build used by OSS. This should ensure contributors see the same warnings locally as the internal build would produce.
Changelog:
[Android][Changed] - Enable -Wpedantic in OSS Android Targets
Reviewed By: cortinico
Differential Revision: D38632454
fbshipit-source-id: 19a036ee3f902eb9d47c568aef448af9d8562358
Summary:
Folly's molly target combines a number of targets that are supposed to be useable on mobile. Since we're trying to move away from folly, we should instead list explicitly which parts of folly we're using so we can remove them over time, and track which targets no longer have any folly dependencies.
Changelog: [Internal]
Reviewed By: NickGerleman
Differential Revision: D38352060
fbshipit-source-id: 09d0d84793692f97f4d49390c99c38b23441df54
Summary:
React Native is compiled downstream with MSVC, meaning the introduction of code depending on language extensions specific to gcc/clang may cause breakage.
We can enable `-Wpedantic` to flag any behavior not officially supported by the specified C++ standard. This will includes rules beyond what MSVC has trouble with, but seems to not have too many "noisy warnings".
This change enables -Wpedantic in BUCK targets within ReactCommon.
This makes the OSS C++ build for Android/iOS slightly more permissive than the internal build, A followup is to add the changes to OSS build logic as well, to avoid contributors seeing more errors upon internal submission. (checking with cortinico on how to do this for Android).
react-native-windows uses a higher warning level than `-Wall`, which is an additional cause of compilation failures, but is not addressed as part of this change.
Changelog:
[Internal][Changed] - Enable -Wpedantic for targets inside ReactCommon
Reviewed By: rshest
Differential Revision: D38457812
fbshipit-source-id: 014da1ac0b7ad8f78154e6e447ed58def6bd0d47
Summary:
This Pull Request aims at removing the making of reactnativeutilsjni as it is built from the same sources as reactnativejni. It also replaces references to reactnativeutilsjni with reactnativejni.
This should get rid of `reactnativeutilsjni.so` while reusing `reactnativejni.so` in it's place. This should give us some size improvements in the finally built apk.
## Changelog
<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->
[Android] [Changed] - Replaced reactnativeutilsjni with reactnativejni in the build process to reduce size
Pull Request resolved: https://github.com/facebook/react-native/pull/34339
Test Plan:
1. Ran the CMakelist.txt file using CMake and I could see that reactnativeutilsjni.dir is no longer generated with my changes.
2. Built the aar from this branch in Android Studio and build happened successfully.
I am not sure if we could run any more tests. Please let me know in case anymore testing is required and I can do accordingly
Reviewed By: cortinico
Differential Revision: D38400481
Pulled By: genkikondo
fbshipit-source-id: 592736e56441328389ae89135667c336ff8018e6
Summary:
Changelog:
[Android][Fixed] - Fix regression when setting shadow node properties.
Also simplified the corresponding macros to avoid using lambdas altogether, as they are not required.
Note that this **does not** modify any constexpr-related semantics of the existing code, as the main constexpr macro, `CONSTEXPR_RAW_PROPS_KEY_HASH` evaluation result is still contstexpr value, and the other ones already involved non-const parts (see my comments).
Reviewed By: NickGerleman
Differential Revision: D38356411
fbshipit-source-id: 22c330d3425c8aed36693f4652f1b257d2dc96be
Summary:
Fix macro errors for Windows. Current syntax breaks the build of the React Common project on Windows because the ({...}) syntax is not supported; must be replaced with lambda expressions.
Resolves https://github.com/facebook/react-native/issues/34090
## Changelog
<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->
[General] [Fixed] - Fix macro errors for Windows.
lyahdav JoshuaGross
Pull Request resolved: https://github.com/facebook/react-native/pull/34299
Test Plan: Build on react-native-windows repo. Tested in RNW app.
Reviewed By: javache
Differential Revision: D38272966
Pulled By: NickGerleman
fbshipit-source-id: e76eac11cde173ef49465d01d793c593017f2ab7
Summary:
Changelog: [iOS][Internal] - Add isPrimary property implementation to the PointerEvent object
This diff adds the `isPrimary` property to the PointerEvent object iOS implementation. In addition this adds a related change where we "reserve" the 0 touch identifier for mouse events and the 1 identifier for apple pencil events. This is an easy way to ensure that these pointers are always consistent no matter what happens. Since mouse & pencil pointers should always be considered the primary pointer, that allows us to focus the more advanced primary pointer differentiation purely on touch events.
The logic for this touch event primary pointer differentiation is essentially setting the first touch it recieves as a primary pointer, setting it on touch registration, and sets all subsequent touchs (while the first touch is down) as not the primary pointers. When that primary pointer is lifted, the class property keeping track of the primary pointer is reset and then the **next** pointer (secondary pointers which had already started before the previous primary pointer was lifted are not "upgraded" to primary) is marked as primary. A new platform test is also included in this diff in order to verify the aforementioned behavior.
Reviewed By: lunaleaps
Differential Revision: D37961707
fbshipit-source-id: ae8b78c5bfea6902fb73094fca1552e4e648ea44
Summary:
D37801394 (https://github.com/facebook/react-native/commit/51f49ca9982f24de08f5a5654a5210e547bb5b86) attempted to fix an issue of TextInput values being dropped when an uncontrolled component is restyled, and a defaultValue is present. I had missed quite a bit of functionality, where TextInput may have child Text elements, which the native side flattens into a single AttributedString. `lastNativeValue` includes a lossy version of the flattened string produced from the child fragments, so sending it along with the children led to duplicating of the current input on each edit, and things blow up.
With some experimentation, I found that the text-loss behavior only happens on Fabric, and is triggered by a state update rather than my original assumption of the view manager command in the `useLayoutEffect` hook. `AndroidTextInputShadowNode` will compare the current and previous flattened strings, to intentionally allow the native value to drift from the React tree if the React tree hasn't changed. This `AttributedString` comparison includes layout metrics as of D20151505 (https://github.com/facebook/react-native/commit/061f54e89086af1c80e5b0460ec715533f99bdb7) meaning a restyle may cause a state update, and clear the text.
I do not have full understanding of the flow of state updates to layout, or the underlying issue that led to the equality check including layout information (since TextMeasurementCache seems to explicitly compare LayoutMetrics). D18894538 (https://github.com/facebook/react-native/commit/254ebab1d2b6fac859ab1ae0c9503328fc99a6d0) used a solution of sending a no-op state update to avoid updating text for placeholders, when the Attributed strings are equal (though as of now this code is never reached, since we return earlier on AttributedString equality). I co-opted this mechanism, to avoid sending text updates if the text content and attributes of the AttributedString has not changed, disregarding any layout information. This is how the comparison worked at the time of the diff.
I also updated the fragment hashing function to include layout metrics, since it was added to be part of the equality check, and is itself hashable.
Changelog:
[Android][Fixed] - Fix `AttributedString` comparison logic for TextInput state updates
Reviewed By: sammy-SC
Differential Revision: D37902643
fbshipit-source-id: c0f8e3112feb19bd0ee62b37bdadeb237a9f725e
Summary:
Changelog: [iOS][Internal] - Add key modifier properties to the PointerEvent interface
This diff adds implementations of the `ctrlKey`, `shiftKey`, `altKey`, and `metaKey` properties on the PointerEvent interface for iOS.
Reviewed By: lunaleaps
Differential Revision: D37869377
fbshipit-source-id: b187bae93fbfc97b6ca1d8d9786ad85343484b3d
Summary:
changelog: [internal]
Original commit changeset: 290ae428a720
Original Phabricator Diff: D37881453 (https://github.com/facebook/react-native/commit/e98a835bfc7b01bccb1b7cdee94db248a0a90607)
in the original diff I made a mistake. The default value for RN is never, we override it in one of our subclasses.
Reviewed By: cortinico
Differential Revision: D37884715
fbshipit-source-id: 3c5b5ef5550120034e33ae9047292f38159dea3e
Summary:
Changelog: [iOS][Internal] - Implement offsetX/offsetY properties on the PointerEvent interface
Simple diff implementing the offsetX/offsetY properties on PointerEvent — thankfully the touch events already kept track of these so it was mostly a matter of forwarding that data to the pointer events.
Reviewed By: lunaleaps
Differential Revision: D37830139
fbshipit-source-id: 77f33a99393350d32cbe449e6a009bdeb2a12d08
Summary:
Changelog: [iOS][Internal] - Implement screenX/screenY properties on the PointerEvent interface
This diff implements the screenX/screenY properties which report the position of a pointer in the device's physical screen coordinates.
Reviewed By: lunaleaps
Differential Revision: D37794415
fbshipit-source-id: 6c39c3651812f99e66b93647579a2935598ef6f2
Summary:
Changelog: [iOS][Internal] - add x/y & pageX/Y implementations to PointerEvent interface
This diff adds the x/y properties, which are defined as aliases of clientX/clientY respectively. In addition this diff adds pageX/pageY which, while not definted as aliases of clientX/clientY, are effectively aliases in React Native since the root view is not scrollable (client and page points only differ on the web when a user has scrolled the document element).
Reviewed By: lunaleaps
Differential Revision: D37766818
fbshipit-source-id: c7ad3750460b1913889c6d1a55b4c1edc6918f5b
Summary:
Changelog: [iOS][internal] - Add twist property to PointerEvent interface
This adds the twist property to the PointerEvent implementation on iOS which similarly to tangentialPressure doesn't really apply so we default to a hard-coded value of 0.
Reviewed By: lunaleaps
Differential Revision: D37760511
fbshipit-source-id: f1fccfd6b5d07024cea83d86925a9bfc2e6cc8cf
Summary:
Changelog: [iOS][internal] - Add tangentialPressure property to PointerEvent interface
This diff adds the tangentialPressure property to the PointerEvent implementation on iOS. This one is pretty simple considering iOS doesn't have the concept of tangential pressure so we just hard code it to 0 as defined by the spec.
Reviewed By: lunaleaps
Differential Revision: D37759634
fbshipit-source-id: 7ca0e47267f5fde76ace2b96d05ea2e154cb4b8f
Summary:
Changelog: [iOS][Internal] - Add `buttons` implementation to the PointerEvent interface
This diff adds an implementation of the `buttons` property by leveraging `UIEvent`'s `buttonMask` property.
Reviewed By: lunaleaps
Differential Revision: D37430270
fbshipit-source-id: 69fd3aebcb403e665349a24283a04c0eb82ff3e2
Summary: Changelog: [Internal] - If any relevant view events (pointer, touch events, gesture responder, etc.) are declared on view, then the view must form stacking context. We need this change for pointer events specifically to determine whether we've entered/exited a view
Reviewed By: vincentriemer
Differential Revision: D37678352
fbshipit-source-id: 02641549ef608b1c9468ac693c7da629143212cb
Summary: Changelog: [Internal] - We can now remove the '2' suffix as we had an internal implementation that was not truly aligned with W3C pointers but used the same name. We have aligned the internal types to match w3c so we can now remove the suffix that differentiates them.
Reviewed By: vincentriemer
Differential Revision: D37545813
fbshipit-source-id: 6f2336ae9e314066c340161113268c1f28621a71
Summary:
See commentary at top of stack.
Changelog: [Added][Fabric] New API for efficient props construction
Reviewed By: javache
Differential Revision: D37051020
fbshipit-source-id: 643e433c0d0590cfcd17bc7a43d105bed6ff12ef
Summary:
See commentary at top of stack.
Changelog: [Added][Fabric] New API for efficient props construction
Reviewed By: javache
Differential Revision: D37050961
fbshipit-source-id: 170a09c08d7406b6aac51d7e78cf295a72fdcf91
Summary:
See commentary at top of stack.
Changelog: [Added][Fabric] New API for efficient props construction
Reviewed By: javache
Differential Revision: D37050376
fbshipit-source-id: 2bea35a6d604704cf430bd3b2914988227d1abf8
Summary:
Perf numbers for this stack are given in terms of before-stack and after-stack, but the changes are split up for ease of review, and also to show that this migration CAN happen per-component and is 100% opt-in. Most existing C++ components do not /need/ to change at all.
# Problem Statement
During certain renders (select critical scenarios in specific products), UIManagerBinding::createNode time takes over 50% of JS thread CPU time. This could be higher or lower depending on the specific product and interaction, but overall createNode takes a lot of CPU time. The question is: can we improve this? What is the minimal overhead needed?
The vast, vast majority of time is taken up by prop parsing (specifically, converting JS values across the JSI into concrete values on the C++ props structs). Other methods like appendChild, etc, do not take up a significant amount of time; so we conclude that createNode is special, and the JSI itself, or calling into C++, is not the problem. Props parsing is the perf problem.
Can we improve it? (Spoiler: yes)
# How does props parsing work today?
Today, props parsing works as follows:
1. The ConcreteComponentDescriptor will construct a RawPropsParser (one per component /type/, per application: so one for View, one for Image, one for Text... etc)
2. Once per component type per application, ConcreteComponentDescriptor will call "prepare" on the RawPropsParser with an empty, default-constructed ConcreteProps struct. This ConcreteProps struct will cause RawProps.at(field) for every single field.
3. Based on the RawProps::at calls in part 2, RawPropsParser constructs a Map from props string names (width, height, position, etc) to a position within a "value index" array.
4. The above is what happens before any actual props are parsed; and the RawPropsParser is now ready to parse actual Props.
5. When props are actually being parsed from a JSI dictionary, we now have two phases:
1. The RawPropsParser `preparse`s the RawProps, by iterating over the JSI map and filling in two additional data structures: a linear list of RawValues, and a mapping from the ValueIndex array (`keyIndexToValueIndex_`; see step 3) to a value's position in the values list (`value_` in RawPropsParser/RawProps);
2. The ConcretePropT constructor is called, which is the same as in step 2/3, which calls `fieldValue = rawProps.at("fieldName")` repeatedly.
3. For each `at` call, the RawProps will look up a prop name in the Map constructed in step 3, and either return an empty value, or map the key name to the `keyIndexToValueIndex_` array, which maps to a value in `values_`, which is then returned and further parsed.
So, a few things that become clear with the current architecture:
1. Complexity is a property of the number of /possible/ props that /can/ be parsed, not what is actually used in product code. This violates the "only pay for what you use" principal. If you have `<View opacity={0.5} />`, the ViewProps constructor will request ~170 properties, not 1!
2. There's a lot of pre-parsing which isn't free
3. The levels of indirection aren't free, and make cache misses more likely and pipelining is more challenging
4. The levels of indirection also require more memory - minor, but not free
# How can we improve it?
The goal is to improve props parsing with minimal or zero impact on backwards-compability. We should be able to migrate over components when it's clear there's a performance issue, without requiring everything gets migrated over at once. This both (1) helps us prove out the new architecture, (2) derisks the project, (3) gives us time, internally and externally, to perfect the APIs and gradually migrate everything over before deleting the old infrastructure code entirely.
Thus, the goal is to do something that introduces a zero-cost abstraction. This isn't entirely possible in practice, and in fact this method slightly regresses components that do not use the new architecture /at all/, while dramatically improving migrated components and causing the impact of the /old/ architecture to be minimal.
# Solution
1. We still keep the existing system in place entirely.
2. After Props are constructed (see ConcreteComponentDescriptor changes) we iterate over all the /values/ set from JS, and call PropsT::setProp. Incidentally, this allows us to easily reuse the same prop for multiple values for "free", which was expensive in the old system.
3. It's worth noting that this makes a Props struct "less immutable" than it was before, and essentially now we have a "builder pattern" for Props. (If we really wanted to, we could still require a single constructor for Props, and then actually use an intermediate PropsBuilder to accumulate values - but I don't think this overhead would be worth for the conceptual "immutability" win, and instead a "Construct/Set/Seal" model works fine, and we still have all the same guarantees of immutability after the parsing phase)
# Implementation Details
# How to properly construct a single Prop value
Minor detail: parsing a single prop is a 3-step process. We imagine two scenarios: (1) Creating a new ShadowNode/Props A from nothing/void, so the previous Props value is just the default constructor. (2) Cloning a ShadowNode A->B and therefore Props A must be copied to Props B before parsing.
We will denote this as a clone from A->B, where A may or may not be a previous node or a default-constructed Props node; and imagine in particular that we're setting the "opacity" value for PropsB.
We must first (1) copy a value over from the previous version of the Props struct, so B.opacity = A.opacity; (2) Determine if opacity has been set from JS. If so, and there is a value, B.opacity = parse(JSValue). (3) If JS has passed in a value for the prop, BUT the value is `null`, it means that JS is resetting or deleting the prop, so we must set it BACK to the default. In this case we set PropsB.opacity = DefaultConstructedProps.opacity.
We must take care in general to ensure that the correct behavior is achieved here, which should help to explain some of the code below.
## String comparisons vs hash comparisons
In the previous system, a RawPropsKey is three `const char*` strings, concatenated together repeatedly /at runtime/. In practice, the ONLY reason we have the prefix, name, suffix Key structure is for the templated prop parsing in ViewProps and YogaStyableProps - that's it. It's not used anywhere else. Further, the key {"margin", "Left", "Width"} is identical to the key {"marginLeftWidth", null, null} and we don't do anything fancy with matching prefixes before comparing the whole string, or similar. Before comparison, keys are concatenated into a single string and then we use `strcmp`. The performance of this isn't terrible, but it's nonzero overhead.
I think we can do better and it's sufficient to compare hashed string values; even better, we can construct most of these /at compile time/ using constexpr, and using `switch` statements guarantee no hash collisions within a single Props struct (it's possible there's a collision between Props.cpp and ViewProps.cpp, for example, since they're different switch statements). We may eventually want to be more robust against has collisions; I personally don't find the risk to be too great, hash collisions with these keys are exceedingly unlikely (or maybe I just like to live dangerously). Thus, at runtime, each setProp requires computing a single hash for the value coming from JS, and then int comparisons with a bunch of pre-compiled values.
If we want to be really paranoid, we could be robust to hash collisions by doing `switch COMPILED_HASH("opacity"): if (strcmp(strFromJs, "opacity") == 0)`. I'm happy to do this if there's enough concern.
## Macros
Yuck! I'm using lots of C preprocessor macros. In general I found this way, way easier in reducing code and (essentially) doing codegen for me vs templated code for the switch cases and hashing prop names at compile-time. Maybe there's a better way.
Changelog: [Added][Fabric] New API for efficient props construction
Reviewed By: javache
Differential Revision: D37050215
fbshipit-source-id: d2dcd351a93b9715cfeb5197eb0d6f9194ec6eb9
Summary:
A huge set of props use YGValue directly, say something really basic like `margin`/`position`/`padding`/`border`.
All of these according to CSS spec actually support `number | "em" | "px" | %` units, but we are going to throw and hard crash on `em` and `px`, which are unsupported in React Native.
Using `tryTo` instead of `to` (noexcept vs throwing method) for conversion, and treating things like `margin: 50px` same way as we would treat `margin: false` which is not really supported.
Changelog:
[General][Fixed] - Fixed a crash on deserialization of props when using 'px'/'em' units.
Reviewed By: bvanderhoof
Differential Revision: D37163250
fbshipit-source-id: 59cbe65a821052f6c7e9588b6d4a0ac14e344684
Summary:
Seems like an obvious typo! Whoops!
Not causing any known issues, but... this should be fixed.
Changelog: [Internal]
Reviewed By: genkikondo
Differential Revision: D36940476
fbshipit-source-id: d534ca3763b1f91e41c56953bf3d665e86db9e2b
Summary:
See also D36889794. This is a very similar idea, except the core problem is that BaseTextProps is accessing the same props as ViewProps; and for ParagraphProps parsing, it first defers to ViewProps' parser and then BaseTextProps. RawPropsParser is optimized to access the same props in the same order, *exactly once*, so if we access a prop out-of-order, or for a second time, that access and the next access are deoptimized. Paragraph/Text, in particular, were quite bad because we did this several times, and each out-of-order access requires scanning /all/ props.
This fixes the issue, at least partially, by (1) pulling all the duplicate accesses to the beginning of BaseTextProps, and (2) accessing them all in the same order as ViewProps, relatively (some props are skipped, but that matters less).
Practically what this means is that now, all of Props' accesses have a cost of O(1) for lookup, or a total of O(n) for all of them; each access is at the n+1 position in the internal RawPropsParser array, so each access is cheap. BaseTextProps' duplicate accesses, even though there are only 4 of them: (1) the first one scans the entire array until we reach the prop in question; (2) the next accesses require scans, but not whole-array scans, since they're in order. (3) The BaseTextProps accesses /after/ the duplicate accesses are all O(1).
tl;dr is that before we had something like O(n*6) cost for BaseTextProps parsing and now it is O(n*2).
Similar to my summary in the last diff: we may want to revisit the RawPropsParser API... but I want to tread gently there, and this gets us a large improvement without major, risky changes.
Empirically, based on a couple of systraces, average time for a single UIManager::createNode called from JS thread, before this stack: 17us. After: 667ns (3% as long). On average, for every 60 createNode calls, we will save 1ms on the UI thread. The savings will be greater for certain screens that use many Views or Text nodes, and lesser for screens that use fewer of these components.
Changelog: [Internal]
Reviewed By: mdvacca
Differential Revision: D36890072
fbshipit-source-id: 5d24b986c391d7bb158ed2f43d130a71960837d1
Summary:
Without getting into the weeds too much, RawPropParser "requires" that props be accessed in the same order every time a Props struct is parsed in order to most optimally fetch the values in a linear-ish fashion, basically ensuring that each rawProps.at() call is an O(1) operation, and overall getting all props for a particular component is O(n) in the number of props for a given struct. If props are called out of order, this breaks and worst-case we can end up with an O(n^2) operation.
Unfortunately, calling .at(x) twice with the same prop name triggers the deoptimized behavior. So as much as possible, always fetch exactly once and in the same order every time. In this case, we move initialization of two fields into the constructor body so that we can call .at() a single time instead of twice.
In the debug props of ViewProps I'm also reordering the fields to fetch them in the same order the constructor fetches them in, which will make this (debug-only) method slightly faster.
What's the impact of this? If you dig into the Tracery samples, the average/median RawPropsParser::at takes 1us or less. However, in /every single/ call to createNode for View components, there is at least one RawPropsParser::at call that takes 250+us. This was a huge red flag when analyzing traces, after which it was trivial (for View) to find the offending out-of-order calls. Since this is happening for every View and every type of component that derives from View, that's 1ms lost per every 4 View-type ShadowNodes created by ReactJS. After just 100 views created, that's 25ms. Etc.
There are other out-of-order calls lurking in the codebase that can be addressed separately. Impact scales with the size of the screen, the number of Views they render, etc.
Changelog: [Internal]
Reviewed By: mdvacca
Differential Revision: D36889794
fbshipit-source-id: 91e0a7ca39ed10778e60a0f0339a4b4dc8b14436