Fabric: Fixes UB/broken font weight management on iOS

Summary:
Address Sanitizer told me that I have UB in `RCTUIFontWeightFromFloat` and it was right. After a short investigation, I find out that the original assumption that `UIFontWeight` values are practically numbers from 100 to 900 was incorrect. In my simulator, it's something like from `-1` to `+1` (irregularly!). So, the whole subsystem worked only by accident.
This diff fixes that; now we never assume which actual values `UIFontWeight` constants have.

I will publish code style fixes as a separate diff (otherwise it will be really hard to review).

Reviewed By: JoshuaGross

Differential Revision: D15756620

fbshipit-source-id: d7f888e85815d863487c6b68a960e39fd473e095
This commit is contained in:
Valentin Shergin
2019-06-12 21:13:46 -07:00
committed by Facebook Github Bot
parent 3bf068ac93
commit c34e554275
3 changed files with 39 additions and 38 deletions
@@ -32,6 +32,23 @@ using namespace facebook::react;
@end
inline static UIFontWeight RCTUIFontWeightFromInteger(NSInteger fontWeight) {
assert(fontWeight > 50);
assert(fontWeight < 950);
static UIFontWeight weights[] = {/* ~100 */ UIFontWeightUltraLight,
/* ~200 */ UIFontWeightThin,
/* ~300 */ UIFontWeightLight,
/* ~400 */ UIFontWeightRegular,
/* ~500 */ UIFontWeightMedium,
/* ~600 */ UIFontWeightSemibold,
/* ~700 */ UIFontWeightBold,
/* ~800 */ UIFontWeightHeavy,
/* ~900 */ UIFontWeightBlack};
// The expression is designed to convert something like 760 or 830 to 7.
return weights[(fontWeight + 50) / 100 - 1];
}
inline static UIFont *RCTEffectiveFontFromTextAttributes(
const TextAttributes &textAttributes) {
NSString *fontFamily =
@@ -46,9 +63,9 @@ inline static UIFont *RCTEffectiveFontFromTextAttributes(
: RCTFontStyleUndefined;
fontProperties.variant = textAttributes.fontVariant.hasValue()
? RCTFontVariantFromFontVariant(textAttributes.fontVariant.value())
: RCTFontVariantDefault;
: RCTFontVariantUndefined;
fontProperties.weight = textAttributes.fontWeight.hasValue()
? CGFloat(textAttributes.fontWeight.value())
? RCTUIFontWeightFromInteger((NSInteger)textAttributes.fontWeight.value())
: NAN;
fontProperties.sizeMultiplier = textAttributes.fontSizeMultiplier;
@@ -27,12 +27,12 @@ typedef NS_OPTIONS(NSInteger, RCTFontVariant) {
};
struct RCTFontProperties {
NSString *family;
CGFloat size;
UIFontWeight weight;
RCTFontStyle style;
RCTFontVariant variant;
CGFloat sizeMultiplier;
NSString *family = nil;
CGFloat size = NAN;
UIFontWeight weight = NAN;
RCTFontStyle style = RCTFontStyleUndefined;
RCTFontVariant variant = RCTFontVariantUndefined;
CGFloat sizeMultiplier = NAN;
};
NS_ASSUME_NONNULL_END
@@ -9,15 +9,18 @@
#import <cmath>
#import <mutex>
#import <algorithm>
#import <limits>
static RCTFontProperties RCTDefaultFontProperties() {
static RCTFontProperties defaultFontProperties;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
defaultFontProperties.size = 14;
defaultFontProperties.family =
[UIFont systemFontOfSize:defaultFontProperties.size].familyName;
defaultFontProperties.size = 14;
defaultFontProperties.weight = UIFontWeightRegular;
defaultFontProperties.style = RCTFontStyleNormal;
defaultFontProperties.variant = RCTFontVariantDefault;
defaultFontProperties.sizeMultiplier = 1.0;
@@ -27,24 +30,22 @@ static RCTFontProperties RCTDefaultFontProperties() {
}
static RCTFontProperties RCTResolveFontProperties(
RCTFontProperties fontProperties) {
RCTFontProperties defaultFontProperties = RCTDefaultFontProperties();
fontProperties.family = fontProperties.family.length &&
![fontProperties.family isEqualToString:@"System"]
RCTFontProperties fontProperties, RCTFontProperties baseFontProperties) {
fontProperties.family = fontProperties.family.length
? fontProperties.family
: defaultFontProperties.family;
: baseFontProperties.family;
fontProperties.size = !isnan(fontProperties.size)
? fontProperties.size
: defaultFontProperties.size;
: baseFontProperties.size;
fontProperties.weight = !isnan(fontProperties.weight)
? fontProperties.weight
: defaultFontProperties.weight;
: baseFontProperties.weight;
fontProperties.style = fontProperties.style != RCTFontStyleUndefined
? fontProperties.style
: defaultFontProperties.style;
: baseFontProperties.style;
fontProperties.variant = fontProperties.variant != RCTFontVariantUndefined
? fontProperties.variant
: defaultFontProperties.variant;
: baseFontProperties.variant;
return fontProperties;
}
@@ -71,23 +72,6 @@ static NSArray *RCTFontFeatures(RCTFontVariant fontVariant) {
return @[];
}
static UIFontWeight RCTUIFontWeightFromFloat(CGFloat fontWeight) {
// Note: Even if the underlying type of `UIFontWeight` is `CGFloat`
// and UIKit uses the same numerical notation, we have to use exact
// `UIFontWeight*` constants to make it work properly (because
// float values comparison is tricky).
static UIFontWeight weights[] = {/* ~100 */ UIFontWeightUltraLight,
/* ~200 */ UIFontWeightThin,
/* ~300 */ UIFontWeightLight,
/* ~400 */ UIFontWeightRegular,
/* ~500 */ UIFontWeightMedium,
/* ~600 */ UIFontWeightSemibold,
/* ~700 */ UIFontWeightBold,
/* ~800 */ UIFontWeightHeavy,
/* ~900 */ UIFontWeightBlack};
return weights[std::llround((fontWeight / 100) - 1)];
}
static UIFont *RCTDefaultFontWithFontProperties(
RCTFontProperties fontProperties) {
static NSCache *fontCache;
@@ -109,7 +93,7 @@ static UIFont *RCTDefaultFontWithFontProperties(
if (!font) {
font = [UIFont
systemFontOfSize:fontProperties.size
weight:RCTUIFontWeightFromFloat(fontProperties.weight)];
weight:fontProperties.weight];
if (fontProperties.variant == RCTFontStyleItalic) {
UIFontDescriptor *fontDescriptor = [font fontDescriptor];
@@ -135,7 +119,7 @@ static UIFont *RCTDefaultFontWithFontProperties(
UIFont *RCTFontWithFontProperties(RCTFontProperties fontProperties) {
RCTFontProperties defaultFontProperties = RCTDefaultFontProperties();
fontProperties = RCTResolveFontProperties(fontProperties);
fontProperties = RCTResolveFontProperties(fontProperties, defaultFontProperties);
assert(!isnan(fontProperties.sizeMultiplier));
CGFloat effectiveFontSize =
@@ -158,7 +142,7 @@ UIFont *RCTFontWithFontProperties(RCTFontProperties fontProperties) {
// Failback to system font.
font = [UIFont
systemFontOfSize:effectiveFontSize
weight:RCTUIFontWeightFromFloat(fontProperties.weight)];
weight:fontProperties.weight];
}
} else {
// Get the closest font that matches the given weight for the fontFamily