]> granicus.if.org Git - icu/commitdiff
ICU-20042 Improve OOM handling in PluralRules class. (#20)
authorJeff Genovy <29107334+jefgen@users.noreply.github.com>
Thu, 2 Aug 2018 06:33:03 +0000 (23:33 -0700)
committerShane Carr <shane@unicode.org>
Thu, 27 Sep 2018 21:27:37 +0000 (14:27 -0700)
- PluralRules class doesn't handle out-of-memory (OOM) errors in some code paths.
- The clone and assignment operator (operator=) methods of construction don't take an error code  parameter, meaning that if an OOM error occurs during the constructor, it will not reported back to the caller, and the caller has no way to know that the object is in a half-constructed state.
- Using an internal status variable for these above cases.
- Changes to the various PluralRules helper classes to check for OOM as well.
- Using nullptr instead NULL.
- Using LocalPointer instead of raw new/delete in some cases.
- Need to set mRules to nullptr, otherwise we can end up with double deletes in the failure case. (Thanks to Andy for the review).
- Using default member initializers for class members to avoid dangling pointers. Also allows for using default constructors too.

icu4c/source/i18n/plurrule.cpp
icu4c/source/i18n/plurrule_impl.h
icu4c/source/i18n/unicode/plurrule.h

index aedb924c6183b9f50cf86d50ba7b8440583c3bb6..4a4c21e7ac2aee4bff7e1373f4819ca1630e6ea1 100644 (file)
@@ -65,13 +65,15 @@ UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralKeywordEnumeration)
 
 PluralRules::PluralRules(UErrorCode& /*status*/)
 :   UObject(),
-    mRules(NULL)
+    mRules(nullptr),
+    mInternalStatus(U_ZERO_ERROR)
 {
 }
 
 PluralRules::PluralRules(const PluralRules& other)
 : UObject(other),
-    mRules(NULL)
+    mRules(nullptr),
+    mInternalStatus(U_ZERO_ERROR)
 {
     *this=other;
 }
@@ -86,54 +88,67 @@ SharedPluralRules::~SharedPluralRules() {
 
 PluralRules*
 PluralRules::clone() const {
-    return new PluralRules(*this);
+    PluralRules* newObj = new PluralRules(*this);
+    // Since clone doesn't have a 'status' parameter, the best we can do is return nullptr if
+    // the newly created object was not fully constructed properly (an error occurred).
+    if (newObj != nullptr && U_FAILURE(newObj->mInternalStatus)) {
+        delete newObj;
+        newObj = nullptr;
+    }
+    return newObj;
 }
 
 PluralRules&
 PluralRules::operator=(const PluralRules& other) {
     if (this != &other) {
         delete mRules;
-        if (other.mRules==NULL) {
-            mRules = NULL;
+        mRules = nullptr;
+        mInternalStatus = other.mInternalStatus;
+        if (U_FAILURE(mInternalStatus)) {
+            // bail out early if the object we were copying from was already 'invalid'.
+            return *this;
         }
-        else {
+        if (other.mRules != nullptr) {
             mRules = new RuleChain(*other.mRules);
+            if (mRules == nullptr) {
+                mInternalStatus = U_MEMORY_ALLOCATION_ERROR;
+            }
+            else if (U_FAILURE(mRules->fInternalStatus)) {
+                // If the RuleChain wasn't fully copied, then set our status to failure as well.
+                mInternalStatus = mRules->fInternalStatus;
+            }
         }
     }
-
     return *this;
 }
 
 StringEnumeration* PluralRules::getAvailableLocales(UErrorCode &status) {
-    StringEnumeration *result = new PluralAvailableLocalesEnumeration(status);
-    if (result == NULL && U_SUCCESS(status)) {
-        status = U_MEMORY_ALLOCATION_ERROR;
+    if (U_FAILURE(status)) {
+        return nullptr;
     }
+    LocalPointer<StringEnumeration> result(new PluralAvailableLocalesEnumeration(status), status);
     if (U_FAILURE(status)) {
-        delete result;
-        result = NULL;
+        return nullptr;
     }
-    return result;
+    return result.orphan();
 }
 
 
 PluralRules* U_EXPORT2
 PluralRules::createRules(const UnicodeString& description, UErrorCode& status) {
     if (U_FAILURE(status)) {
-        return NULL;
+        return nullptr;
     }
-
     PluralRuleParser parser;
-    PluralRules *newRules = new PluralRules(status);
-    if (U_SUCCESS(status) && newRules == NULL) {
-        status = U_MEMORY_ALLOCATION_ERROR;
+    LocalPointer<PluralRules> newRules(new PluralRules(status), status);
+    if (U_FAILURE(status)) {
+        return nullptr;
     }
-    parser.parse(description, newRules, status);
+    parser.parse(description, newRules.getAlias(), status);
     if (U_FAILURE(status)) {
-        delete newRules;
-        newRules = NULL;
+        newRules.adoptInstead(nullptr);
     }
-    return newRules;
+    return newRules.orphan();
 }
 
 
@@ -149,19 +164,17 @@ template<> U_I18N_API
 const SharedPluralRules *LocaleCacheKey<SharedPluralRules>::createObject(
         const void * /*unused*/, UErrorCode &status) const {
     const char *localeId = fLoc.getName();
-    PluralRules *pr = PluralRules::internalForLocale(
-            localeId, UPLURAL_TYPE_CARDINAL, status);
+    LocalPointer<PluralRules> pr(PluralRules::internalForLocale(localeId, UPLURAL_TYPE_CARDINAL, status), status);
     if (U_FAILURE(status)) {
-        return NULL;
+        return nullptr;
     }
-    SharedPluralRules *result = new SharedPluralRules(pr);
-    if (result == NULL) {
-        status = U_MEMORY_ALLOCATION_ERROR;
-        delete pr;
-        return NULL;
+    LocalPointer<SharedPluralRules> result(new SharedPluralRules(pr.getAlias()), status);
+    if (U_FAILURE(status)) {
+        return nullptr;
     }
+    pr.orphan(); // result was successfully created so it nows pr.
     result->addRef();
-    return result;
+    return result.orphan();
 }
 
 /* end plural rules cache */
@@ -171,13 +184,13 @@ const SharedPluralRules* U_EXPORT2
 PluralRules::createSharedInstance(
         const Locale& locale, UPluralType type, UErrorCode& status) {
     if (U_FAILURE(status)) {
-        return NULL;
+        return nullptr;
     }
     if (type != UPLURAL_TYPE_CARDINAL) {
         status = U_UNSUPPORTED_ERROR;
-        return NULL;
+        return nullptr;
     }
-    const SharedPluralRules *result = NULL;
+    const SharedPluralRules *result = nullptr;
     UnifiedCache::getByLocale(locale, result, status);
     return result;
 }
@@ -195,11 +208,11 @@ PluralRules::forLocale(const Locale& locale, UPluralType type, UErrorCode& statu
     const SharedPluralRules *shared = createSharedInstance(
             locale, type, status);
     if (U_FAILURE(status)) {
-        return NULL;
+        return nullptr;
     }
     PluralRules *result = (*shared)->clone();
     shared->removeRef();
-    if (result == NULL) {
+    if (result == nullptr) {
         status = U_MEMORY_ALLOCATION_ERROR;
     }
     return result;
@@ -208,20 +221,23 @@ PluralRules::forLocale(const Locale& locale, UPluralType type, UErrorCode& statu
 PluralRules* U_EXPORT2
 PluralRules::internalForLocale(const Locale& locale, UPluralType type, UErrorCode& status) {
     if (U_FAILURE(status)) {
-        return NULL;
+        return nullptr;
     }
     if (type >= UPLURAL_TYPE_COUNT) {
         status = U_ILLEGAL_ARGUMENT_ERROR;
-        return NULL;
+        return nullptr;
     }
-    PluralRules *newObj = new PluralRules(status);
-    if (newObj==NULL || U_FAILURE(status)) {
-        delete newObj;
-        return NULL;
+    LocalPointer<PluralRules> newObj(new PluralRules(status), status);
+    if (U_FAILURE(status)) {
+        return nullptr;
     }
     UnicodeString locRule = newObj->getRuleFromResource(locale, type, status);
-    // TODO: which errors, if any, should be returned?
+    // TODO: which other errors, if any, should be returned?
     if (locRule.length() == 0) {
+        // If an out-of-memory error occurred, then stop and report the failure.
+        if (status == U_MEMORY_ALLOCATION_ERROR) {
+            return nullptr;
+        }
         // Locales with no specific rules (all numbers have the "other" category
         //   will return a U_MISSING_RESOURCE_ERROR at this point. This is not
         //   an error.
@@ -229,13 +245,13 @@ PluralRules::internalForLocale(const Locale& locale, UPluralType type, UErrorCod
         status = U_ZERO_ERROR;
     }
     PluralRuleParser parser;
-    parser.parse(locRule, newObj, status);
+    parser.parse(locRule, newObj.getAlias(), status);
         //  TODO: should rule parse errors be returned, or
         //        should we silently use default rules?
         //        Original impl used default rules.
         //        Ask the question to ICU Core.
 
-    return newObj;
+    return newObj.orphan();
 }
 
 UnicodeString
@@ -250,7 +266,7 @@ PluralRules::select(double number) const {
 
 UnicodeString
 PluralRules::select(const IFixedDecimal &number) const {
-    if (mRules == NULL) {
+    if (mRules == nullptr) {
         return UnicodeString(TRUE, PLURAL_DEFAULT_RULE, -1);
     }
     else {
@@ -262,14 +278,18 @@ PluralRules::select(const IFixedDecimal &number) const {
 
 StringEnumeration*
 PluralRules::getKeywords(UErrorCode& status) const {
-    if (U_FAILURE(status))  return NULL;
-    StringEnumeration* nameEnumerator = new PluralKeywordEnumeration(mRules, status);
     if (U_FAILURE(status)) {
-      delete nameEnumerator;
-      return NULL;
+        return nullptr;
     }
-
-    return nameEnumerator;
+    if (U_FAILURE(mInternalStatus)) {
+        status = mInternalStatus;
+        return nullptr;
+    }
+    LocalPointer<StringEnumeration> nameEnumerator(new PluralKeywordEnumeration(mRules, status), status);
+    if (U_FAILURE(status)) {
+        return nullptr;
+    }
+    return nameEnumerator.orphan();
 }
 
 double
@@ -367,8 +387,15 @@ getSamplesFromString(const UnicodeString &samples, double *dest,
 int32_t
 PluralRules::getSamples(const UnicodeString &keyword, double *dest,
                         int32_t destCapacity, UErrorCode& status) {
+    if (destCapacity == 0 || U_FAILURE(status)) {
+        return 0;
+    }
+    if (U_FAILURE(mInternalStatus)) {
+        status = mInternalStatus;
+        return 0;
+    }
     RuleChain *rc = rulesForKeyword(keyword);
-    if (rc == NULL || destCapacity == 0 || U_FAILURE(status)) {
+    if (rc == nullptr) {
         return 0;
     }
     int32_t numSamples = getSamplesFromString(rc->fIntegerSamples, dest, destCapacity, status);
@@ -381,7 +408,7 @@ PluralRules::getSamples(const UnicodeString &keyword, double *dest,
 
 RuleChain *PluralRules::rulesForKeyword(const UnicodeString &keyword) const {
     RuleChain *rc;
-    for (rc = mRules; rc != NULL; rc = rc->fNext) {
+    for (rc = mRules; rc != nullptr; rc = rc->fNext) {
         if (rc->fKeyword == keyword) {
             break;
         }
@@ -395,7 +422,7 @@ PluralRules::isKeyword(const UnicodeString& keyword) const {
     if (0 == keyword.compare(PLURAL_KEYWORD_OTHER, 5)) {
         return true;
     }
-    return rulesForKeyword(keyword) != NULL;
+    return rulesForKeyword(keyword) != nullptr;
 }
 
 UnicodeString
@@ -421,13 +448,13 @@ PluralRules::operator==(const PluralRules& other) const  {
         return FALSE;
     }
     myKeywordList->reset(status);
-    while ((ptrKeyword=myKeywordList->snext(status))!=NULL) {
+    while ((ptrKeyword=myKeywordList->snext(status))!=nullptr) {
         if (!other.isKeyword(*ptrKeyword)) {
             return FALSE;
         }
     }
     otherKeywordList->reset(status);
-    while ((ptrKeyword=otherKeywordList->snext(status))!=NULL) {
+    while ((ptrKeyword=otherKeywordList->snext(status))!=nullptr) {
         if (!this->isKeyword(*ptrKeyword)) {
             return FALSE;
         }
@@ -460,29 +487,33 @@ PluralRuleParser::parse(const UnicodeString& ruleData, PluralRules *prules, UErr
         }
         switch (type) {
         case tAnd:
-            U_ASSERT(curAndConstraint != NULL);
-            curAndConstraint = curAndConstraint->add();
+            U_ASSERT(curAndConstraint != nullptr);
+            curAndConstraint = curAndConstraint->add(status);
             break;
         case tOr:
             {
-                U_ASSERT(currentChain != NULL);
+                U_ASSERT(currentChain != nullptr);
                 OrConstraint *orNode=currentChain->ruleHeader;
-                while (orNode->next != NULL) {
+                while (orNode->next != nullptr) {
                     orNode = orNode->next;
                 }
                 orNode->next= new OrConstraint();
+                if (orNode->next == nullptr) {
+                    status = U_MEMORY_ALLOCATION_ERROR;
+                    break;
+                }
                 orNode=orNode->next;
-                orNode->next=NULL;
-                curAndConstraint = orNode->add();
+                orNode->next=nullptr;
+                curAndConstraint = orNode->add(status);
             }
             break;
         case tIs:
-            U_ASSERT(curAndConstraint != NULL);
+            U_ASSERT(curAndConstraint != nullptr);
             U_ASSERT(curAndConstraint->value == -1);
-            U_ASSERT(curAndConstraint->rangeList == NULL);
+            U_ASSERT(curAndConstraint->rangeList == nullptr);
             break;
         case tNot:
-            U_ASSERT(curAndConstraint != NULL);
+            U_ASSERT(curAndConstraint != nullptr);
             curAndConstraint->negated=TRUE;
             break;
 
@@ -492,23 +523,29 @@ PluralRuleParser::parse(const UnicodeString& ruleData, PluralRules *prules, UErr
         case tIn:
         case tWithin:
         case tEqual:
-            U_ASSERT(curAndConstraint != NULL);
-            curAndConstraint->rangeList = new UVector32(status);
-            curAndConstraint->rangeList->addElement(-1, status);  // range Low
-            curAndConstraint->rangeList->addElement(-1, status);  // range Hi
-            rangeLowIdx = 0;
-            rangeHiIdx  = 1;
-            curAndConstraint->value=PLURAL_RANGE_HIGH;
-            curAndConstraint->integerOnly = (type != tWithin);
+            {
+                U_ASSERT(curAndConstraint != nullptr);
+                LocalPointer<UVector32> newRangeList(new UVector32(status), status);
+                if (U_FAILURE(status)) {
+                    break;
+                }
+                curAndConstraint->rangeList = newRangeList.orphan();
+                curAndConstraint->rangeList->addElement(-1, status);  // range Low
+                curAndConstraint->rangeList->addElement(-1, status);  // range Hi
+                rangeLowIdx = 0;
+                rangeHiIdx  = 1;
+                curAndConstraint->value=PLURAL_RANGE_HIGH;
+                curAndConstraint->integerOnly = (type != tWithin);
+            }
             break;
         case tNumber:
-            U_ASSERT(curAndConstraint != NULL);
+            U_ASSERT(curAndConstraint != nullptr);
             if ( (curAndConstraint->op==AndConstraint::MOD)&&
                  (curAndConstraint->opNum == -1 ) ) {
                 curAndConstraint->opNum=getNumberValue(token);
             }
             else {
-                if (curAndConstraint->rangeList == NULL) {
+                if (curAndConstraint->rangeList == nullptr) {
                     // this is for an 'is' rule
                     curAndConstraint->value = getNumberValue(token);
                 } else {
@@ -534,7 +571,7 @@ PluralRuleParser::parse(const UnicodeString& ruleData, PluralRules *prules, UErr
         case tComma:
             // TODO: rule syntax checking is inadequate, can happen with badly formed rules.
             //       Catch cases like "n mod 10, is 1" here instead.
-            if (curAndConstraint == NULL || curAndConstraint->rangeList == NULL) {
+            if (curAndConstraint == nullptr || curAndConstraint->rangeList == nullptr) {
                 status = U_UNEXPECTED_TOKEN;
                 break;
             }
@@ -545,7 +582,7 @@ PluralRuleParser::parse(const UnicodeString& ruleData, PluralRules *prules, UErr
             curAndConstraint->rangeList->addElement(-1, status);  // range Hi
             break;
         case tMod:
-            U_ASSERT(curAndConstraint != NULL);
+            U_ASSERT(curAndConstraint != nullptr);
             curAndConstraint->op=AndConstraint::MOD;
             break;
         case tVariableN:
@@ -553,24 +590,24 @@ PluralRuleParser::parse(const UnicodeString& ruleData, PluralRules *prules, UErr
         case tVariableF:
         case tVariableT:
         case tVariableV:
-            U_ASSERT(curAndConstraint != NULL);
+            U_ASSERT(curAndConstraint != nullptr);
             curAndConstraint->digitsType = type;
             break;
         case tKeyword:
             {
             RuleChain *newChain = new RuleChain;
-            if (newChain == NULL) {
+            if (newChain == nullptr) {
                 status = U_MEMORY_ALLOCATION_ERROR;
                 break;
             }
             newChain->fKeyword = token;
-            if (prules->mRules == NULL) {
+            if (prules->mRules == nullptr) {
                 prules->mRules = newChain;
             } else {
                 // The new rule chain goes at the end of the linked list of rule chains,
                 //   unless there is an "other" keyword & chain. "other" must remain last.
                 RuleChain *insertAfter = prules->mRules;
-                while (insertAfter->fNext!=NULL &&
+                while (insertAfter->fNext!=nullptr &&
                        insertAfter->fNext->fKeyword.compare(PLURAL_KEYWORD_OTHER, 5) != 0 ){
                     insertAfter=insertAfter->fNext;
                 }
@@ -578,8 +615,12 @@ PluralRuleParser::parse(const UnicodeString& ruleData, PluralRules *prules, UErr
                 insertAfter->fNext = newChain;
             }
             OrConstraint *orNode = new OrConstraint();
+            if (orNode == nullptr) {
+                status = U_MEMORY_ALLOCATION_ERROR;
+                break;
+            }
             newChain->ruleHeader = orNode;
-            curAndConstraint = orNode->add();
+            curAndConstraint = orNode->add(status);
             currentChain = newChain;
             }
             break;
@@ -629,7 +670,7 @@ PluralRules::getRuleFromResource(const Locale& locale, UPluralType type, UErrorC
     if (U_FAILURE(errCode)) {
         return emptyStr;
     }
-    LocalUResourceBundlePointer rb(ures_openDirect(NULL, "plurals", &errCode));
+    LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "plurals", &errCode));
     if(U_FAILURE(errCode)) {
         return emptyStr;
     }
@@ -646,7 +687,7 @@ PluralRules::getRuleFromResource(const Locale& locale, UPluralType type, UErrorC
         errCode = U_ILLEGAL_ARGUMENT_ERROR;
         return emptyStr;
     }
-    LocalUResourceBundlePointer locRes(ures_getByKey(rb.getAlias(), typeKey, NULL, &errCode));
+    LocalUResourceBundlePointer locRes(ures_getByKey(rb.getAlias(), typeKey, nullptr, &errCode));
     if(U_FAILURE(errCode)) {
         return emptyStr;
     }
@@ -654,7 +695,7 @@ PluralRules::getRuleFromResource(const Locale& locale, UPluralType type, UErrorC
     const char *curLocaleName=locale.getName();
     const UChar* s = ures_getStringByKey(locRes.getAlias(), curLocaleName, &resLen, &errCode);
 
-    if (s == NULL) {
+    if (s == nullptr) {
         // Check parent locales.
         UErrorCode status = U_ZERO_ERROR;
         char parentLocaleName[ULOC_FULLNAME_CAPACITY];
@@ -665,14 +706,14 @@ PluralRules::getRuleFromResource(const Locale& locale, UPluralType type, UErrorC
                                        ULOC_FULLNAME_CAPACITY, &status) > 0) {
             resLen=0;
             s = ures_getStringByKey(locRes.getAlias(), parentLocaleName, &resLen, &status);
-            if (s != NULL) {
+            if (s != nullptr) {
                 errCode = U_ZERO_ERROR;
                 break;
             }
             status = U_ZERO_ERROR;
         }
     }
-    if (s==NULL) {
+    if (s==nullptr) {
         return emptyStr;
     }
 
@@ -680,18 +721,18 @@ PluralRules::getRuleFromResource(const Locale& locale, UPluralType type, UErrorC
     u_UCharsToChars(s, setKey, resLen + 1);
     // printf("\n PluralRule: %s\n", setKey);
 
-    LocalUResourceBundlePointer ruleRes(ures_getByKey(rb.getAlias(), "rules", NULL, &errCode));
+    LocalUResourceBundlePointer ruleRes(ures_getByKey(rb.getAlias(), "rules", nullptr, &errCode));
     if(U_FAILURE(errCode)) {
         return emptyStr;
     }
-    LocalUResourceBundlePointer setRes(ures_getByKey(ruleRes.getAlias(), setKey, NULL, &errCode));
+    LocalUResourceBundlePointer setRes(ures_getByKey(ruleRes.getAlias(), setKey, nullptr, &errCode));
     if (U_FAILURE(errCode)) {
         return emptyStr;
     }
 
     int32_t numberKeys = ures_getSize(setRes.getAlias());
     UnicodeString result;
-    const char *key=NULL;
+    const char *key=nullptr;
     for(int32_t i=0; i<numberKeys; ++i) {   // Keys are zero, one, few, ...
         UnicodeString rules = ures_getNextUnicodeString(setRes.getAlias(), &key, &errCode);
         UnicodeString uKey(key, -1, US_INV);
@@ -707,54 +748,46 @@ PluralRules::getRuleFromResource(const Locale& locale, UPluralType type, UErrorC
 UnicodeString
 PluralRules::getRules() const {
     UnicodeString rules;
-    if (mRules != NULL) {
+    if (mRules != nullptr) {
         mRules->dumpRules(rules);
     }
     return rules;
 }
 
-
-AndConstraint::AndConstraint() {
-    op = AndConstraint::NONE;
-    opNum=-1;
-    value = -1;
-    rangeList = NULL;
-    negated = FALSE;
-    integerOnly = FALSE;
-    digitsType = none;
-    next=NULL;
-}
-
-
 AndConstraint::AndConstraint(const AndConstraint& other) {
+    this->fInternalStatus = other.fInternalStatus;
+    if (U_FAILURE(fInternalStatus)) {
+        return; // stop early if the object we are copying from is invalid.
+    }
     this->op = other.op;
     this->opNum=other.opNum;
     this->value=other.value;
-    this->rangeList=NULL;
-    if (other.rangeList != NULL) {
-        UErrorCode status = U_ZERO_ERROR;
-        this->rangeList = new UVector32(status);
-        this->rangeList->assign(*other.rangeList, status);
+    if (other.rangeList != nullptr) {
+        LocalPointer<UVector32> newRangeList(new UVector32(fInternalStatus), fInternalStatus);
+        if (U_FAILURE(fInternalStatus)) {
+            return;
+        }
+        this->rangeList = newRangeList.orphan();
+        this->rangeList->assign(*other.rangeList, fInternalStatus);
     }
     this->integerOnly=other.integerOnly;
     this->negated=other.negated;
     this->digitsType = other.digitsType;
-    if (other.next==NULL) {
-        this->next=NULL;
-    }
-    else {
+    if (other.next != nullptr) {
         this->next = new AndConstraint(*other.next);
+        if (this->next == nullptr) {
+            fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
+        }
     }
 }
 
 AndConstraint::~AndConstraint() {
     delete rangeList;
-    if (next!=NULL) {
-        delete next;
-    }
+    rangeList = nullptr;
+    delete next;
+    next = nullptr;
 }
 
-
 UBool
 AndConstraint::isFulfilled(const IFixedDecimal &number) {
     UBool result = TRUE;
@@ -776,7 +809,7 @@ AndConstraint::isFulfilled(const IFixedDecimal &number) {
         if (op == MOD) {
             n = fmod(n, opNum);
         }
-        if (rangeList == NULL) {
+        if (rangeList == nullptr) {
             result = value == -1 ||    // empty rule
                      n == value;       //  'is' rule
             break;
@@ -796,53 +829,67 @@ AndConstraint::isFulfilled(const IFixedDecimal &number) {
     return result;
 }
 
-
 AndConstraint*
-AndConstraint::add()
-{
+AndConstraint::add(UErrorCode& status) {
+    if (U_FAILURE(fInternalStatus)) {
+        status = fInternalStatus;
+        return nullptr;
+    }
     this->next = new AndConstraint();
+    if (this->next == nullptr) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+    }
     return this->next;
 }
 
-OrConstraint::OrConstraint() {
-    childNode=NULL;
-    next=NULL;
-}
 
 OrConstraint::OrConstraint(const OrConstraint& other) {
-    if ( other.childNode == NULL ) {
-        this->childNode = NULL;
+    this->fInternalStatus = other.fInternalStatus;
+    if (U_FAILURE(fInternalStatus)) {
+        return; // stop early if the object we are copying from is invalid.
     }
-    else {
+    if ( other.childNode != nullptr ) {
         this->childNode = new AndConstraint(*(other.childNode));
+        if (this->childNode == nullptr) {
+            fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
+            return;
+        }
     }
-    if (other.next == NULL ) {
-        this->next = NULL;
-    }
-    else {
+    if (other.next != nullptr ) {
         this->next = new OrConstraint(*(other.next));
+        if (this->next == nullptr) {
+            fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
+            return;
+        }
+        if (U_FAILURE(this->next->fInternalStatus)) {
+            this->fInternalStatus = this->next->fInternalStatus;
+        }
     }
 }
 
 OrConstraint::~OrConstraint() {
-    if (childNode!=NULL) {
-        delete childNode;
-    }
-    if (next!=NULL) {
-        delete next;
-    }
+    delete childNode;
+    childNode = nullptr;
+    delete next;
+    next = nullptr;
 }
 
 AndConstraint*
-OrConstraint::add()
-{
+OrConstraint::add(UErrorCode& status) {
+    if (U_FAILURE(fInternalStatus)) {
+        status = fInternalStatus;
+        return nullptr;
+    }
     OrConstraint *curOrConstraint=this;
     {
-        while (curOrConstraint->next!=NULL) {
+        while (curOrConstraint->next!=nullptr) {
             curOrConstraint = curOrConstraint->next;
         }
-        U_ASSERT(curOrConstraint->childNode == NULL);
+        U_ASSERT(curOrConstraint->childNode == nullptr);
         curOrConstraint->childNode = new AndConstraint();
+        if (curOrConstraint->childNode == nullptr) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+        }
     }
     return curOrConstraint->childNode;
 }
@@ -852,10 +899,10 @@ OrConstraint::isFulfilled(const IFixedDecimal &number) {
     OrConstraint* orRule=this;
     UBool result=FALSE;
 
-    while (orRule!=NULL && !result) {
+    while (orRule!=nullptr && !result) {
         result=TRUE;
         AndConstraint* andRule = orRule->childNode;
-        while (andRule!=NULL && result) {
+        while (andRule!=nullptr && result) {
             result = andRule->isFulfilled(number);
             andRule=andRule->next;
         }
@@ -866,19 +913,33 @@ OrConstraint::isFulfilled(const IFixedDecimal &number) {
 }
 
 
-RuleChain::RuleChain(): fKeyword(), fNext(NULL), ruleHeader(NULL), fDecimalSamples(), fIntegerSamples(),
-                        fDecimalSamplesUnbounded(FALSE), fIntegerSamplesUnbounded(FALSE) {
-}
-
 RuleChain::RuleChain(const RuleChain& other) :
-        fKeyword(other.fKeyword), fNext(NULL), ruleHeader(NULL), fDecimalSamples(other.fDecimalSamples),
+        fKeyword(other.fKeyword), fDecimalSamples(other.fDecimalSamples),
         fIntegerSamples(other.fIntegerSamples), fDecimalSamplesUnbounded(other.fDecimalSamplesUnbounded),
-        fIntegerSamplesUnbounded(other.fIntegerSamplesUnbounded) {
-    if (other.ruleHeader != NULL) {
+        fIntegerSamplesUnbounded(other.fIntegerSamplesUnbounded), fInternalStatus(other.fInternalStatus) {
+    if (U_FAILURE(this->fInternalStatus)) {
+        return; // stop early if the object we are copying from is invalid. 
+    }
+    if (other.ruleHeader != nullptr) {
         this->ruleHeader = new OrConstraint(*(other.ruleHeader));
+        if (this->ruleHeader == nullptr) {
+            this->fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
+        }
+        else if (U_FAILURE(this->ruleHeader->fInternalStatus)) {
+            // If the OrConstraint wasn't fully copied, then set our status to failure as well.
+            this->fInternalStatus = this->ruleHeader->fInternalStatus;
+            return; // exit early.
+        }
     }
-    if (other.fNext != NULL ) {
+    if (other.fNext != nullptr ) {
         this->fNext = new RuleChain(*other.fNext);
+        if (this->fNext == nullptr) {
+            this->fInternalStatus = U_MEMORY_ALLOCATION_ERROR;
+        }
+        else if (U_FAILURE(this->fNext->fInternalStatus)) {
+            // If the RuleChain wasn't fully copied, then set our status to failure as well.
+            this->fInternalStatus = this->fNext->fInternalStatus;
+        }
     }
 }
 
@@ -887,11 +948,10 @@ RuleChain::~RuleChain() {
     delete ruleHeader;
 }
 
-
 UnicodeString
 RuleChain::select(const IFixedDecimal &number) const {
     if (!number.isNaN() && !number.isInfinite()) {
-        for (const RuleChain *rules = this; rules != NULL; rules = rules->fNext) {
+        for (const RuleChain *rules = this; rules != nullptr; rules = rules->fNext) {
              if (rules->ruleHeader->isFulfilled(number)) {
                  return rules->fKeyword;
              }
@@ -923,17 +983,17 @@ void
 RuleChain::dumpRules(UnicodeString& result) {
     UChar digitString[16];
 
-    if ( ruleHeader != NULL ) {
+    if ( ruleHeader != nullptr ) {
         result +=  fKeyword;
         result += COLON;
         result += SPACE;
         OrConstraint* orRule=ruleHeader;
-        while ( orRule != NULL ) {
+        while ( orRule != nullptr ) {
             AndConstraint* andRule=orRule->childNode;
-            while ( andRule != NULL ) {
-                if ((andRule->op==AndConstraint::NONE) &&  (andRule->rangeList==NULL) && (andRule->value == -1)) {
+            while ( andRule != nullptr ) {
+                if ((andRule->op==AndConstraint::NONE) &&  (andRule->rangeList==nullptr) && (andRule->value == -1)) {
                     // Empty Rules.
-                } else if ( (andRule->op==AndConstraint::NONE) && (andRule->rangeList==NULL) ) {
+                } else if ( (andRule->op==AndConstraint::NONE) && (andRule->rangeList==nullptr) ) {
                     result += tokenString(andRule->digitsType);
                     result += UNICODE_STRING_SIMPLE(" is ");
                     if (andRule->negated) {
@@ -950,7 +1010,7 @@ RuleChain::dumpRules(UnicodeString& result) {
                         uprv_itou(digitString,16, andRule->opNum,10,0);
                         result += UnicodeString(digitString);
                     }
-                    if (andRule->rangeList==NULL) {
+                    if (andRule->rangeList==nullptr) {
                         if (andRule->negated) {
                             result += UNICODE_STRING_SIMPLE(" is not ");
                             uprv_itou(digitString,16, andRule->value,10,0);
@@ -993,16 +1053,16 @@ RuleChain::dumpRules(UnicodeString& result) {
                         }
                     }
                 }
-                if ( (andRule=andRule->next) != NULL) {
+                if ( (andRule=andRule->next) != nullptr) {
                     result += UNICODE_STRING_SIMPLE(" and ");
                 }
             }
-            if ( (orRule = orRule->next) != NULL ) {
+            if ( (orRule = orRule->next) != nullptr ) {
                 result += UNICODE_STRING_SIMPLE(" or ");
             }
         }
     }
-    if ( fNext != NULL ) {
+    if ( fNext != nullptr ) {
         result += UNICODE_STRING_SIMPLE("; ");
         fNext->dumpRules(result);
     }
@@ -1011,6 +1071,9 @@ RuleChain::dumpRules(UnicodeString& result) {
 
 UErrorCode
 RuleChain::getKeywords(int32_t capacityOfKeywords, UnicodeString* keywords, int32_t& arraySize) const {
+    if (U_FAILURE(fInternalStatus)) {
+        return fInternalStatus;
+    }
     if ( arraySize < capacityOfKeywords-1 ) {
         keywords[arraySize++]=fKeyword;
     }
@@ -1018,7 +1081,7 @@ RuleChain::getKeywords(int32_t capacityOfKeywords, UnicodeString* keywords, int3
         return U_BUFFER_OVERFLOW_ERROR;
     }
 
-    if ( fNext != NULL ) {
+    if ( fNext != nullptr ) {
         return fNext->getKeywords(capacityOfKeywords, keywords, arraySize);
     }
     else {
@@ -1032,7 +1095,7 @@ RuleChain::isKeyword(const UnicodeString& keywordParam) const {
         return TRUE;
     }
 
-    if ( fNext != NULL ) {
+    if ( fNext != nullptr ) {
         return fNext->isKeyword(keywordParam);
     }
     else {
@@ -1043,7 +1106,7 @@ RuleChain::isKeyword(const UnicodeString& keywordParam) const {
 
 PluralRuleParser::PluralRuleParser() :
         ruleIndex(0), token(), type(none), prevType(none),
-        curAndConstraint(NULL), currentChain(NULL), rangeLowIdx(-1), rangeHiIdx(-1)
+        curAndConstraint(nullptr), currentChain(nullptr), rangeLowIdx(-1), rangeHiIdx(-1)
 {
 }
 
@@ -1341,21 +1404,36 @@ PluralKeywordEnumeration::PluralKeywordEnumeration(RuleChain *header, UErrorCode
         return;
     }
     fKeywordNames.setDeleter(uprv_deleteUObject);
-    UBool  addKeywordOther=TRUE;
-    RuleChain *node=header;
-    while(node!=NULL) {
-        fKeywordNames.addElement(new UnicodeString(node->fKeyword), status);
+    UBool  addKeywordOther = TRUE;
+    RuleChain *node = header;
+    while (node != nullptr) {
+        auto newElem = new UnicodeString(node->fKeyword);
+        if (newElem == nullptr) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+            return;
+        }
+        fKeywordNames.addElement(newElem, status);
         if (U_FAILURE(status)) {
+            delete newElem;
             return;
         }
         if (0 == node->fKeyword.compare(PLURAL_KEYWORD_OTHER, 5)) {
-            addKeywordOther= FALSE;
+            addKeywordOther = FALSE;
         }
-        node=node->fNext;
+        node = node->fNext;
     }
 
     if (addKeywordOther) {
-        fKeywordNames.addElement(new UnicodeString(PLURAL_KEYWORD_OTHER), status);
+        auto newElem = new UnicodeString(PLURAL_KEYWORD_OTHER);
+        if (newElem == nullptr) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+            return;
+        }
+        fKeywordNames.addElement(newElem, status);
+        if (U_FAILURE(status)) {
+            delete newElem;
+            return;
+        }
     }
 }
 
@@ -1364,7 +1442,7 @@ PluralKeywordEnumeration::snext(UErrorCode& status) {
     if (U_SUCCESS(status) && pos < fKeywordNames.size()) {
         return (const UnicodeString*)fKeywordNames.elementAt(pos++);
     }
-    return NULL;
+    return nullptr;
 }
 
 void
@@ -1374,7 +1452,7 @@ PluralKeywordEnumeration::reset(UErrorCode& /*status*/) {
 
 int32_t
 PluralKeywordEnumeration::count(UErrorCode& /*status*/) const {
-       return fKeywordNames.size();
+    return fKeywordNames.size();
 }
 
 PluralKeywordEnumeration::~PluralKeywordEnumeration() {
@@ -1634,41 +1712,39 @@ int32_t FixedDecimal::getVisibleFractionDigitCount() const {
 
 
 PluralAvailableLocalesEnumeration::PluralAvailableLocalesEnumeration(UErrorCode &status) {
-    fLocales = NULL;
-    fRes = NULL;
     fOpenStatus = status;
     if (U_FAILURE(status)) {
         return;
     }
-    fOpenStatus = U_ZERO_ERROR;
-    LocalUResourceBundlePointer rb(ures_openDirect(NULL, "plurals", &fOpenStatus));
-    fLocales = ures_getByKey(rb.getAlias(), "locales", NULL, &fOpenStatus);
+    fOpenStatus = U_ZERO_ERROR; // clear any warnings.
+    LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "plurals", &fOpenStatus));
+    fLocales = ures_getByKey(rb.getAlias(), "locales", nullptr, &fOpenStatus);
 }
 
 PluralAvailableLocalesEnumeration::~PluralAvailableLocalesEnumeration() {
     ures_close(fLocales);
     ures_close(fRes);
-    fLocales = NULL;
-    fRes = NULL;
+    fLocales = nullptr;
+    fRes = nullptr;
 }
 
 const char *PluralAvailableLocalesEnumeration::next(int32_t *resultLength, UErrorCode &status) {
     if (U_FAILURE(status)) {
-        return NULL;
+        return nullptr;
     }
     if (U_FAILURE(fOpenStatus)) {
         status = fOpenStatus;
-        return NULL;
+        return nullptr;
     }
     fRes = ures_getNextResource(fLocales, fRes, &status);
-    if (fRes == NULL || U_FAILURE(status)) {
+    if (fRes == nullptr || U_FAILURE(status)) {
         if (status == U_INDEX_OUTOFBOUNDS_ERROR) {
             status = U_ZERO_ERROR;
         }
-        return NULL;
+        return nullptr;
     }
     const char *result = ures_getKey(fRes);
-    if (resultLength != NULL) {
+    if (resultLength != nullptr) {
         *resultLength = static_cast<int32_t>(uprv_strlen(result));
     }
     return result;
index 3ab445d5843c8b4336467cf65492fd0323e39234..3a919ea9f5d9bd77d7b91d9dc6becb8e988a63b6 100644 (file)
@@ -181,7 +181,6 @@ private:
        kRangeList,
        kSamples
     };
-
 };
 
 enum PluralOperand {
@@ -311,32 +310,36 @@ public:
         NONE,
         MOD
     } RuleOp;
-    RuleOp  op;
-    int32_t opNum;           // for mod expressions, the right operand of the mod.
-    int32_t     value;       // valid for 'is' rules only.
-    UVector32   *rangeList;  // for 'in', 'within' rules. Null otherwise.
-    UBool   negated;           // TRUE for negated rules.
-    UBool   integerOnly;     // TRUE for 'within' rules.
-    tokenType digitsType;    // n | i | v | f constraint.
-    AndConstraint *next;
-
-    AndConstraint();
+    RuleOp op = AndConstraint::NONE;
+    int32_t opNum = -1;             // for mod expressions, the right operand of the mod.
+    int32_t value = -1;             // valid for 'is' rules only.
+    UVector32 *rangeList = nullptr; // for 'in', 'within' rules. Null otherwise.
+    UBool negated = FALSE;          // TRUE for negated rules.
+    UBool integerOnly = FALSE;      // TRUE for 'within' rules.
+    tokenType digitsType = none;    // n | i | v | f constraint.
+    AndConstraint *next = nullptr;
+    // Internal error status, used for errors that occur during the copy constructor.
+    UErrorCode fInternalStatus = U_ZERO_ERROR;    
+
+    AndConstraint() = default;
     AndConstraint(const AndConstraint& other);
     virtual ~AndConstraint();
-    AndConstraint* add();
+    AndConstraint* add(UErrorCode& status);
     // UBool isFulfilled(double number);
     UBool isFulfilled(const IFixedDecimal &number);
 };
 
 class OrConstraint : public UMemory  {
 public:
-    AndConstraint *childNode;
-    OrConstraint *next;
-    OrConstraint();
+    AndConstraint *childNode = nullptr;
+    OrConstraint *next = nullptr;
+    // Internal error status, used for errors that occur during the copy constructor.
+    UErrorCode fInternalStatus = U_ZERO_ERROR;
 
+    OrConstraint() = default;
     OrConstraint(const OrConstraint& other);
     virtual ~OrConstraint();
-    AndConstraint* add();
+    AndConstraint* add(UErrorCode& status);
     // UBool isFulfilled(double number);
     UBool isFulfilled(const IFixedDecimal &number);
 };
@@ -344,15 +347,16 @@ public:
 class RuleChain : public UMemory  {
 public:
     UnicodeString   fKeyword;
-    RuleChain      *fNext;
-    OrConstraint   *ruleHeader;
+    RuleChain      *fNext = nullptr;
+    OrConstraint   *ruleHeader = nullptr;
     UnicodeString   fDecimalSamples;  // Samples strings from rule source
     UnicodeString   fIntegerSamples;  //   without @decimal or @integer, otherwise unprocessed.
-    UBool           fDecimalSamplesUnbounded;
-    UBool           fIntegerSamplesUnbounded;
-
+    UBool           fDecimalSamplesUnbounded = FALSE;
+    UBool           fIntegerSamplesUnbounded = FALSE;
+    // Internal error status, used for errors that occur during the copy constructor.
+    UErrorCode      fInternalStatus = U_ZERO_ERROR;
 
-    RuleChain();
+    RuleChain() = default;
     RuleChain(const RuleChain& other);
     virtual ~RuleChain();
 
@@ -386,8 +390,8 @@ class U_I18N_API PluralAvailableLocalesEnumeration: public StringEnumeration {
     virtual int32_t count(UErrorCode& status) const;
   private:
     UErrorCode      fOpenStatus;
-    UResourceBundle *fLocales;
-    UResourceBundle *fRes;
+    UResourceBundle *fLocales = nullptr;
+    UResourceBundle *fRes = nullptr;
 };
 
 U_NAMESPACE_END
index df451b2cd34f047cce8137ce55f0604218276d3f..daeed52bee632e3118cad04315b5f6d543df6850 100644 (file)
@@ -497,6 +497,12 @@ private:
     UnicodeString   getRuleFromResource(const Locale& locale, UPluralType type, UErrorCode& status);
     RuleChain      *rulesForKeyword(const UnicodeString &keyword) const;
 
+    /**
+    * An internal status variable used to indicate that the object is in an 'invalid' state.
+    * Used by copy constructor, the assignment operator and the clone method.
+    */
+    UErrorCode mInternalStatus;
+
     friend class PluralRuleParser;
 };