tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

messageformat2_evaluation.cpp (17594B)


      1 // © 2024 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 
      4 #include "unicode/utypes.h"
      5 
      6 #if !UCONFIG_NO_NORMALIZATION
      7 
      8 #if !UCONFIG_NO_FORMATTING
      9 
     10 #if !UCONFIG_NO_MF2
     11 
     12 #include "messageformat2_allocation.h"
     13 #include "messageformat2_evaluation.h"
     14 #include "messageformat2_function_registry_internal.h"
     15 #include "messageformat2_macros.h"
     16 #include "uvector.h" // U_ASSERT
     17 
     18 U_NAMESPACE_BEGIN
     19 
     20 // Auxiliary data structures used during formatting a message
     21 
     22 namespace message2 {
     23 
     24 using namespace data_model;
     25 
     26 // Functions
     27 // -------------
     28 
     29 ResolvedFunctionOption::ResolvedFunctionOption(ResolvedFunctionOption&& other) {
     30    name = std::move(other.name);
     31    value = std::move(other.value);
     32    sourceIsLiteral = other.sourceIsLiteral;
     33 }
     34 
     35 ResolvedFunctionOption::~ResolvedFunctionOption() {}
     36 
     37 
     38 const ResolvedFunctionOption* FunctionOptions::getResolvedFunctionOptions(int32_t& len) const {
     39    len = functionOptionsLen;
     40    U_ASSERT(len == 0 || options != nullptr);
     41    return options;
     42 }
     43 
     44 FunctionOptions::FunctionOptions(UVector&& optionsVector, UErrorCode& status) {
     45    CHECK_ERROR(status);
     46 
     47    functionOptionsLen = optionsVector.size();
     48    options = moveVectorToArray<ResolvedFunctionOption>(optionsVector, status);
     49 }
     50 
     51 // Returns false if option doesn't exist
     52 UBool FunctionOptions::wasSetFromLiteral(const UnicodeString& key) const {
     53    if (options == nullptr) {
     54        U_ASSERT(functionOptionsLen == 0);
     55    }
     56    for (int32_t i = 0; i < functionOptionsLen; i++) {
     57        const ResolvedFunctionOption& opt = options[i];
     58        if (opt.getName() == key) {
     59            return opt.isLiteral();
     60        }
     61    }
     62    return false;
     63 }
     64 
     65 UBool FunctionOptions::getFunctionOption(std::u16string_view key, Formattable& option) const {
     66    if (options == nullptr) {
     67        U_ASSERT(functionOptionsLen == 0);
     68    }
     69    for (int32_t i = 0; i < functionOptionsLen; i++) {
     70        const ResolvedFunctionOption& opt = options[i];
     71        if (opt.getName() == key) {
     72            option = opt.getValue();
     73            return true;
     74        }
     75    }
     76    return false;
     77 }
     78 
     79 UnicodeString FunctionOptions::getStringFunctionOption(std::u16string_view key) const {
     80    Formattable option;
     81    if (getFunctionOption(key, option)) {
     82        if (option.getType() == UFMT_STRING) {
     83            UErrorCode localErrorCode = U_ZERO_ERROR;
     84            UnicodeString val = option.getString(localErrorCode);
     85            U_ASSERT(U_SUCCESS(localErrorCode));
     86            return val;
     87        }
     88    }
     89    // For anything else, including non-string values, return "".
     90    // Alternately, could try to stringify the non-string option.
     91    // (Currently, no tests require that.)
     92    return {};
     93 }
     94 
     95 FunctionOptions& FunctionOptions::operator=(FunctionOptions&& other) noexcept {
     96    functionOptionsLen = other.functionOptionsLen;
     97    options = other.options;
     98    other.functionOptionsLen = 0;
     99    other.options = nullptr;
    100    return *this;
    101 }
    102 
    103 FunctionOptions::FunctionOptions(FunctionOptions&& other) {
    104    *this = std::move(other);
    105 }
    106 
    107 FunctionOptions::~FunctionOptions() {
    108    if (options != nullptr) {
    109        delete[] options;
    110        options = nullptr;
    111    }
    112 }
    113 
    114 static bool containsOption(const UVector& opts, const ResolvedFunctionOption& opt) {
    115    for (int32_t i = 0; i < opts.size(); i++) {
    116        if (static_cast<ResolvedFunctionOption*>(opts[i])->getName()
    117            == opt.getName()) {
    118            return true;
    119        }
    120    }
    121    return false;
    122 }
    123 
    124 // Options in `this` take precedence
    125 // `this` can't be used after mergeOptions is called
    126 FunctionOptions FunctionOptions::mergeOptions(FunctionOptions&& other,
    127                                              UErrorCode& status) {
    128    UVector mergedOptions(status);
    129    mergedOptions.setDeleter(uprv_deleteUObject);
    130 
    131    if (U_FAILURE(status)) {
    132        return {};
    133    }
    134 
    135    // Create a new vector consisting of the options from this `FunctionOptions`
    136    for (int32_t i = 0; i < functionOptionsLen; i++) {
    137        mergedOptions.adoptElement(create<ResolvedFunctionOption>(std::move(options[i]), status),
    138                                 status);
    139    }
    140 
    141    // Add each option from `other` that doesn't appear in this `FunctionOptions`
    142    for (int i = 0; i < other.functionOptionsLen; i++) {
    143        // Note: this is quadratic in the length of `options`
    144        if (!containsOption(mergedOptions, other.options[i])) {
    145            mergedOptions.adoptElement(create<ResolvedFunctionOption>(std::move(other.options[i]),
    146                                                                    status),
    147                                     status);
    148        }
    149    }
    150 
    151    delete[] options;
    152    options = nullptr;
    153    functionOptionsLen = 0;
    154 
    155    return FunctionOptions(std::move(mergedOptions), status);
    156 }
    157 
    158 // PrioritizedVariant
    159 // ------------------
    160 
    161 UBool PrioritizedVariant::operator<(const PrioritizedVariant& other) const {
    162  if (priority < other.priority) {
    163      return true;
    164  }
    165  return false;
    166 }
    167 
    168 PrioritizedVariant::~PrioritizedVariant() {}
    169 
    170    // ---------------- Environments and closures
    171 
    172    Environment* Environment::create(const VariableName& var, Closure&& c, Environment* parent, UErrorCode& errorCode) {
    173        NULL_ON_ERROR(errorCode);
    174        Environment* result = new NonEmptyEnvironment(var, std::move(c), parent);
    175        if (result == nullptr) {
    176            errorCode = U_MEMORY_ALLOCATION_ERROR;
    177            return nullptr;
    178        }
    179        return result;
    180    }
    181 
    182    Environment* Environment::create(UErrorCode& errorCode) {
    183        NULL_ON_ERROR(errorCode);
    184        Environment* result = new EmptyEnvironment();
    185        if (result == nullptr) {
    186            errorCode = U_MEMORY_ALLOCATION_ERROR;
    187            return nullptr;
    188        }
    189        return result;
    190    }
    191 
    192    const Closure& EmptyEnvironment::lookup(const VariableName& v) const {
    193        (void) v;
    194        U_ASSERT(false);
    195        UPRV_UNREACHABLE_EXIT;
    196    }
    197 
    198    const Closure& NonEmptyEnvironment::lookup(const VariableName& v) const {
    199        if (v == var) {
    200            return rhs;
    201        }
    202        return parent->lookup(v);
    203    }
    204 
    205    bool EmptyEnvironment::has(const VariableName& v) const {
    206        (void) v;
    207        return false;
    208    }
    209 
    210    bool NonEmptyEnvironment::has(const VariableName& v) const {
    211        if (v == var) {
    212            return true;
    213        }
    214        return parent->has(v);
    215    }
    216 
    217    Environment::~Environment() {}
    218    NonEmptyEnvironment::~NonEmptyEnvironment() {}
    219    EmptyEnvironment::~EmptyEnvironment() {}
    220 
    221    Closure::~Closure() {}
    222 
    223    // MessageContext methods
    224 
    225    void MessageContext::checkErrors(UErrorCode& status) const {
    226        CHECK_ERROR(status);
    227        errors.checkErrors(status);
    228    }
    229 
    230    const Formattable* MessageContext::getGlobal(const VariableName& v,
    231                                                 UErrorCode& errorCode) const {
    232       return arguments.getArgument(v, errorCode);
    233    }
    234 
    235    MessageContext::MessageContext(const MessageArguments& args,
    236                                   const StaticErrors& e,
    237                                   UErrorCode& status) : arguments(args), errors(e, status) {}
    238 
    239    MessageContext::~MessageContext() {}
    240 
    241    // InternalValue
    242    // -------------
    243 
    244    bool InternalValue::isFallback() const {
    245        return std::holds_alternative<FormattedPlaceholder>(argument)
    246            && std::get_if<FormattedPlaceholder>(&argument)->isFallback();
    247    }
    248 
    249    bool InternalValue::hasNullOperand() const {
    250        return std::holds_alternative<FormattedPlaceholder>(argument)
    251            && std::get_if<FormattedPlaceholder>(&argument)->isNullOperand();
    252    }
    253 
    254    FormattedPlaceholder InternalValue::takeArgument(UErrorCode& errorCode) {
    255        if (U_FAILURE(errorCode)) {
    256            return {};
    257        }
    258 
    259        if (std::holds_alternative<FormattedPlaceholder>(argument)) {
    260            return std::move(*std::get_if<FormattedPlaceholder>(&argument));
    261        }
    262        errorCode = U_ILLEGAL_ARGUMENT_ERROR;
    263        return {};
    264    }
    265 
    266    const UnicodeString& InternalValue::getFallback() const {
    267        if (std::holds_alternative<FormattedPlaceholder>(argument)) {
    268            return std::get_if<FormattedPlaceholder>(&argument)->getFallback();
    269        }
    270        return (*std::get_if<InternalValue*>(&argument))->getFallback();
    271    }
    272 
    273    const Selector* InternalValue::getSelector(UErrorCode& errorCode) const {
    274        if (U_FAILURE(errorCode)) {
    275            return nullptr;
    276        }
    277 
    278        if (selector == nullptr) {
    279            errorCode = U_ILLEGAL_ARGUMENT_ERROR;
    280        }
    281        return selector;
    282    }
    283 
    284    InternalValue::InternalValue(FormattedPlaceholder&& arg) {
    285        argument = std::move(arg);
    286        selector = nullptr;
    287        formatter = nullptr;
    288    }
    289 
    290    InternalValue::InternalValue(InternalValue* operand,
    291                                 FunctionOptions&& opts,
    292                                 const FunctionName& functionName,
    293                                 const Formatter* f,
    294                                 const Selector* s) {
    295        argument = operand;
    296        options = std::move(opts);
    297        name = functionName;
    298        selector = s;
    299        formatter = f;
    300        U_ASSERT(selector != nullptr || formatter != nullptr);
    301    }
    302 
    303    // `this` cannot be used after calling this method
    304    void InternalValue::forceSelection(DynamicErrors& errs,
    305                                       const UnicodeString* keys,
    306                                       int32_t keysLen,
    307                                       UnicodeString* prefs,
    308                                       int32_t& prefsLen,
    309                                       UErrorCode& errorCode) {
    310        if (U_FAILURE(errorCode)) {
    311            return;
    312        }
    313 
    314        if (!canSelect()) {
    315            errorCode = U_ILLEGAL_ARGUMENT_ERROR;
    316            return;
    317        }
    318        // Find the argument and complete set of options by traversing `argument`
    319        FunctionOptions opts;
    320        InternalValue* p = this;
    321        FunctionName selectorName = name;
    322 
    323        bool operandSelect = false;
    324        while (std::holds_alternative<InternalValue*>(p->argument)) {
    325            if (p->name != selectorName) {
    326                // Can only compose calls to the same selector
    327                errorCode = U_ILLEGAL_ARGUMENT_ERROR;
    328                return;
    329            }
    330            // Very special case to detect something like:
    331            // .local $sel = {1 :integer select=exact} .local $bad = {$sel :integer} .match $bad 1 {{ONE}} * {{operand select {$bad}}}
    332            // This can be done better once function composition is fully implemented.
    333            if (p != this &&
    334                !p->options.getStringFunctionOption(options::SELECT).isEmpty()
    335                && (selectorName == functions::NUMBER || selectorName == functions::INTEGER)) {
    336                // In this case, we want to call the selector normally but emit a
    337                // `bad-option` error, possibly with the outcome of normal-looking output (with relaxed
    338                // error handling) and an error (with strict error handling).
    339                operandSelect = true;
    340            }
    341            // First argument to mergeOptions takes precedence
    342            opts = opts.mergeOptions(std::move(p->options), errorCode);
    343            if (U_FAILURE(errorCode)) {
    344                return;
    345            }
    346            InternalValue* next = *std::get_if<InternalValue*>(&p->argument);
    347            p = next;
    348        }
    349        FormattedPlaceholder arg = std::move(*std::get_if<FormattedPlaceholder>(&p->argument));
    350 
    351        // This condition can't be checked in the selector.
    352        // Effectively, there are two different kinds of "bad option" errors:
    353        // one that can be recovered from (used for select=$var) and one that
    354        // can't (used for bad digit size options and other cases).
    355        // The checking of the recoverable error has to be done here; otherwise,
    356        // the "bad option" signaled by the selector implementation would cause
    357        // fallback output to be used when formatting the `*` pattern.
    358        bool badSelectOption = !checkSelectOption();
    359 
    360        selector->selectKey(std::move(arg), std::move(opts),
    361                            keys, keysLen,
    362                            prefs, prefsLen, errorCode);
    363        if (errorCode == U_MF_SELECTOR_ERROR) {
    364            errorCode = U_ZERO_ERROR;
    365            errs.setSelectorError(selectorName, errorCode);
    366        } else if (errorCode == U_MF_BAD_OPTION) {
    367            errorCode = U_ZERO_ERROR;
    368            errs.setBadOption(selectorName, errorCode);
    369        } else if (operandSelect || badSelectOption) {
    370            errs.setRecoverableBadOption(selectorName, errorCode);
    371            // In this case, only the `*` variant should match
    372            prefsLen = 0;
    373        }
    374    }
    375 
    376    bool InternalValue::checkSelectOption() const {
    377        if (name != UnicodeString("number") && name != UnicodeString("integer")) {
    378            return true;
    379        }
    380 
    381        // Per the spec, if the "select" option is present, it must have been
    382        // set from a literal
    383 
    384        Formattable opt;
    385        // Returns false if the `select` option is present and it was not set from a literal
    386 
    387        // OK if the option wasn't present
    388        if (!options.getFunctionOption(UnicodeString("select"), opt)) {
    389            return true;
    390        }
    391        // Otherwise, return true if the option was set from a literal
    392        return options.wasSetFromLiteral(UnicodeString("select"));
    393    }
    394 
    395    FormattedPlaceholder InternalValue::forceFormatting(DynamicErrors& errs, UErrorCode& errorCode) {
    396        if (U_FAILURE(errorCode)) {
    397            return {};
    398        }
    399 
    400        if (formatter == nullptr && selector == nullptr) {
    401            U_ASSERT(std::holds_alternative<FormattedPlaceholder>(argument));
    402            return std::move(*std::get_if<FormattedPlaceholder>(&argument));
    403        }
    404        if (formatter == nullptr) {
    405            errorCode = U_ILLEGAL_ARGUMENT_ERROR;
    406            return {};
    407        }
    408 
    409        FormattedPlaceholder arg;
    410 
    411        if (std::holds_alternative<FormattedPlaceholder>(argument)) {
    412            arg = std::move(*std::get_if<FormattedPlaceholder>(&argument));
    413        } else {
    414            arg = (*std::get_if<InternalValue*>(&argument))->forceFormatting(errs,
    415                                                                             errorCode);
    416        }
    417 
    418        if (U_FAILURE(errorCode)) {
    419            return {};
    420        }
    421 
    422        if (arg.isFallback()) {
    423            return arg;
    424        }
    425 
    426        // The fallback for a nullary function call is the function name
    427        UnicodeString fallback;
    428        if (arg.isNullOperand()) {
    429            fallback = u":";
    430            fallback += name;
    431        } else {
    432            fallback = arg.getFallback();
    433        }
    434 
    435        // Very special case for :number select=foo and :integer select=foo
    436        // This check can't be done inside the function implementation because
    437        // it doesn't have a way to both signal an error and return usable output,
    438        // and the spec stipulates that fallback output shouldn't be used in the
    439        // case of a bad `select` option to a formatting call.
    440        bool badSelect = !checkSelectOption();
    441 
    442        // Call the function with the argument
    443        FormattedPlaceholder result = formatter->format(std::move(arg), std::move(options), errorCode);
    444        if (U_SUCCESS(errorCode) && errorCode == U_USING_DEFAULT_WARNING) {
    445            // Ignore this warning
    446            errorCode = U_ZERO_ERROR;
    447        }
    448        if (U_FAILURE(errorCode)) {
    449            if (errorCode == U_MF_OPERAND_MISMATCH_ERROR) {
    450                errorCode = U_ZERO_ERROR;
    451                errs.setOperandMismatchError(name, errorCode);
    452            } else if (errorCode == U_MF_BAD_OPTION) {
    453                errorCode = U_ZERO_ERROR;
    454                errs.setBadOption(name, errorCode);
    455            } else {
    456                errorCode = U_ZERO_ERROR;
    457                // Convey any other error generated by the formatter
    458                // as a formatting error
    459                errs.setFormattingError(name, errorCode);
    460            }
    461        }
    462        // Ignore the output if any error occurred
    463        // We don't ignore the output in the case of a Bad Option Error,
    464        // because of the select=bad case where we want both an error
    465        // and non-fallback output.
    466        if (errs.hasFormattingError() || errs.hasBadOptionError()) {
    467            return FormattedPlaceholder(fallback);
    468        }
    469        if (badSelect) {
    470            // In this case, we want to set an error but not replace
    471            // the output with a fallback
    472            errs.setRecoverableBadOption(name, errorCode);
    473        }
    474        return result;
    475    }
    476 
    477    InternalValue& InternalValue::operator=(InternalValue&& other) noexcept {
    478        argument = std::move(other.argument);
    479        other.argument = nullptr;
    480        options = std::move(other.options);
    481        name = other.name;
    482        selector = other.selector;
    483        formatter = other.formatter;
    484        other.selector = nullptr;
    485        other.formatter = nullptr;
    486 
    487        return *this;
    488    }
    489 
    490    InternalValue::~InternalValue() {
    491        delete selector;
    492        selector = nullptr;
    493        delete formatter;
    494        formatter = nullptr;
    495        if (std::holds_alternative<InternalValue*>(argument)) {
    496            delete *std::get_if<InternalValue*>(&argument);
    497            argument = nullptr;
    498        }
    499    }
    500 
    501 } // namespace message2
    502 U_NAMESPACE_END
    503 
    504 #endif /* #if !UCONFIG_NO_MF2 */
    505 
    506 #endif /* #if !UCONFIG_NO_FORMATTING */
    507 
    508 #endif /* #if !UCONFIG_NO_NORMALIZATION */