diff --git a/ecmascript/base/json_stringifier.cpp b/ecmascript/base/json_stringifier.cpp index 0d9eea484e8fd53fdf79bc711e57cdcb5b85656c..2e8f65586c7c01c8d69e0ae4a05bff2069b19384 100644 --- a/ecmascript/base/json_stringifier.cpp +++ b/ecmascript/base/json_stringifier.cpp @@ -218,8 +218,17 @@ JSTaggedValue JsonStringifier::GetSerializeValue(const JSHandle & handleValue_.Update(tagValue); // a. Let value be Call(ReplacerFunction, holder, «key, value»). const uint32_t argsLength = 2; // 2: «key, value» + JSHandle holder = factory_->CreateNullJSObject(); + if (stack_.size() <= 0) { + JSHandle holderKey(factory_->GetEmptyString()); + JSObject::CreateDataPropertyOrThrow(thread_, holder, holderKey, value); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + } else if (!object->IsUndefined()) { + holder = JSTaggedValue::ToObject(thread_, object); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); + } EcmaRuntimeCallInfo *info = - EcmaInterpreter::NewRuntimeCallInfo(thread_, replacer, object, undefined, argsLength); + EcmaInterpreter::NewRuntimeCallInfo(thread_, replacer, JSHandle::Cast(holder), undefined, argsLength); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread_); info->SetCallArg(key.GetTaggedValue(), handleValue_.GetTaggedValue()); tagValue = JSFunction::Call(info); diff --git a/ecmascript/dump.cpp b/ecmascript/dump.cpp index 24a18c32bc4c559ceaed913d397d2b78afcdb612..42d1379d78d1dd7afe2e623577cd609962dcc486 100644 --- a/ecmascript/dump.cpp +++ b/ecmascript/dump.cpp @@ -3144,8 +3144,13 @@ void JSNumberFormat::Dump(std::ostream &os) const GetMinimumSignificantDigits().Dump(os); os << "\n" << " - MaximumSignificantDigits: "; GetMaximumSignificantDigits().Dump(os); + os << "\n" << " - RoundingIncrement: "; + GetRoundingIncrement().Dump(os); + os << "\n" << " - RoundingPriority: " << static_cast(GetRoundingPriority()); + os << "\n" << " - RoundingMode: " << static_cast(GetRoundingMode()); os << "\n" << " - UseGrouping: " << static_cast(GetUseGrouping()); os << "\n" << " - RoundingType: " << static_cast(GetRoundingType()); + os << "\n" << " - TrailingZeroDisplay: " << static_cast(GetTrailingZeroDisplay()); os << "\n" << " - Notation: " << static_cast(GetNotation()); os << "\n" << " - CompactDisplay: " << static_cast(GetCompactDisplay()); os << "\n" << " - SignDisplay: " << static_cast(GetSignDisplay()); @@ -3191,6 +3196,11 @@ void JSPluralRules::Dump(std::ostream &os) const os << "\n"; os << " - MaximumFractionDigits: "; GetMaximumFractionDigits().Dump(os); + os << " - RoundingIncrement: "; + GetRoundingIncrement().Dump(os); + os << " - RoundingPriority: " << static_cast(GetRoundingPriority()); + os << " - RoundingMode: " << static_cast(GetRoundingMode()); + os << " - TrailingZeroDisplay: " << static_cast(GetTrailingZeroDisplay()); os << "\n"; os << " - MinimumSignificantDigits: "; GetMinimumSignificantDigits().Dump(os); @@ -5583,8 +5593,12 @@ void JSNumberFormat::DumpForSnapshot(std::vector &vec) const vec.emplace_back(CString("MinimumIntegerDigits"), GetMinimumIntegerDigits()); vec.emplace_back(CString("MinimumFractionDigits"), GetMinimumFractionDigits()); vec.emplace_back(CString("MaximumFractionDigits"), GetMaximumFractionDigits()); + vec.emplace_back(CString("RoundingPriority"), JSTaggedValue(static_cast(GetRoundingPriority()))); + vec.emplace_back(CString("RoundingMode"), JSTaggedValue(static_cast(GetRoundingMode()))); + vec.emplace_back(CString("TrailingZeroDisplay"), JSTaggedValue(static_cast(GetTrailingZeroDisplay()))); vec.emplace_back(CString("MinimumSignificantDigits"), GetMinimumSignificantDigits()); vec.emplace_back(CString("MaximumSignificantDigits"), GetMaximumSignificantDigits()); + vec.emplace_back(CString("RoundingIncrement"), GetRoundingIncrement()); vec.emplace_back(CString("UseGrouping"), JSTaggedValue(static_cast(GetUseGrouping()))); vec.emplace_back(CString("RoundingType"), JSTaggedValue(static_cast(GetRoundingType()))); vec.emplace_back(CString("Notation"), JSTaggedValue(static_cast(GetNotation()))); @@ -5623,7 +5637,11 @@ void JSPluralRules::DumpForSnapshot(std::vector &vec) const vec.emplace_back(CString("MaximumFractionDigits"), GetMaximumFractionDigits()); vec.emplace_back(CString("MinimumSignificantDigits"), GetMinimumSignificantDigits()); vec.emplace_back(CString("MaximumSignificantDigits"), GetMaximumSignificantDigits()); + vec.emplace_back(CString("RoundingIncrement"), GetRoundingIncrement()); + vec.emplace_back(CString("RoundingPriority"), JSTaggedValue(static_cast(GetRoundingPriority()))); + vec.emplace_back(CString("RoundingMode"), JSTaggedValue(static_cast(GetRoundingMode()))); vec.emplace_back(CString("RoundingType"), JSTaggedValue(static_cast(GetRoundingType()))); + vec.emplace_back(CString("TrailingZeroDisplay"), JSTaggedValue(static_cast(GetTrailingZeroDisplay()))); vec.emplace_back(CString("IcuPR"), GetIcuPR()); vec.emplace_back(CString("IcuNF"), GetIcuNF()); vec.emplace_back(CString("Type"), JSTaggedValue(static_cast(GetType()))); diff --git a/ecmascript/global_env_constants.h b/ecmascript/global_env_constants.h index 7792f9096a2b88797b8ed763106f6540251c059c..7de371d65cac883fde275b2e6c1efb22aa57290d 100644 --- a/ecmascript/global_env_constants.h +++ b/ecmascript/global_env_constants.h @@ -375,6 +375,9 @@ class ObjectFactory; V(HalfEvenString, HALF_EVEN_STRING_INDEX, "halfEven") \ V(StripIfIntegerString, STRIP_IF_INTEGER_INDEX, "stripIfInteger") \ V(RoundingModeString, ROUNDING_MODE_INDEX, "roundingMode") \ + V(RoundingTypeString, ROUNDING_TYPE_INDEX, "roundingType") \ + V(MorePrecisionString, MORE_PRECISION_INDEX, "morePrecision") \ + V(LessPrecisionString, LESS_PRECISION_INDEX, "lessPrecision") \ V(TrailingZeroDisplayString, TRAILING_ZERO_DISPLAY_INDEX, "trailingZeroDisplay") \ V(CodeString, CODE_INDEX, "code") \ V(NarrowSymbolString, NARROW_SYMBOL_INDEX, "narrowSymbol") \ @@ -392,6 +395,8 @@ class ObjectFactory; V(MaximumFractionDigitsString, MAXIMUM_FRACTIONDIGITS_INDEX, "maximumFractionDigits") \ V(MinimumSignificantDigitsString, MINIMUM_SIGNIFICANTDIGITS_INDEX, "minimumSignificantDigits") \ V(MaximumSignificantDigitsString, MAXIMUM_SIGNIFICANTDIGITS_INDEX, "maximumSignificantDigits") \ + V(RoundingIncrementString, ROUNDING_INCREMENT_INDEX, "roundingIncrement") \ + V(RoundingPriorityString, ROUNDING_PRIORITY_INDEX, "roundingPriority") \ V(InvalidDateString, INVALID_DATE_INDEX, "Invalid Date") \ V(UsageString, USAGE_INDEX, "usage") \ V(CompareString, COMPARE_INDEX, "compare") \ diff --git a/ecmascript/intl/global_intl_helper.cpp b/ecmascript/intl/global_intl_helper.cpp index f0348a7d53d9b5953d3c5b6223a6e1e3388af710..586ea1eb70e38bef59f6a5fb0181349e75e37072 100644 --- a/ecmascript/intl/global_intl_helper.cpp +++ b/ecmascript/intl/global_intl_helper.cpp @@ -127,6 +127,11 @@ void GlobalIntlHelper::InitNumberData(const GlobalEnvConstants *globalConst) collatorMap["maximumFractionDigits"] = globalConst->GetHandledMaximumFractionDigitsString(); collatorMap["minimumSignificantDigits"] = globalConst->GetHandledMinimumSignificantDigitsString(); collatorMap["maximumSignificantDigits"] = globalConst->GetHandledMaximumSignificantDigitsString(); + collatorMap["roundingIncrement"] = globalConst->GetHandledRoundingIncrementString(); + collatorMap["roundingPriority"] = globalConst->GetHandledRoundingPriorityString(); + collatorMap["roundingMode"] = globalConst->GetHandledRoundingModeString(); + collatorMap["roundingType"] = globalConst->GetHandledRoundingTypeString(); + collatorMap["trailingZeroDisplay"] = globalConst->GetHandledTrailingZeroDisplayString(); optionMaps.insert(make_pair(GlobalFormatterType::NumberFormatter, collatorMap)); } diff --git a/ecmascript/js_locale.h b/ecmascript/js_locale.h index 78f3cbd2fbe6e5f5437c0ed3147064642c5d9d84..35ce01e366560080a2d4d14968790d54831d9ede 100644 --- a/ecmascript/js_locale.h +++ b/ecmascript/js_locale.h @@ -65,8 +65,12 @@ enum class LocaleType : uint8_t { }; enum class TypeOption : uint8_t { CARDINAL = 0x01, ORDINAL, EXCEPTION }; -enum class RoundingType : uint8_t { FRACTIONDIGITS = 0x01, SIGNIFICANTDIGITS, COMPACTROUNDING, EXCEPTION }; + +enum class RoundingType : uint8_t { FRACTIONDIGITS = 0x01, SIGNIFICANTDIGITS, LESSPRECISION, MOREPRECISION, EXCEPTION }; enum class NotationOption : uint8_t { STANDARD = 0x01, SCIENTIFIC, ENGINEERING, COMPACT, EXCEPTION }; +enum class RoundingPriority : uint8_t { AUTO = 0x01, MOREPRECISION, LESSPRECISION, EXCEPTION }; +enum class RoundingMode : uint8_t { CEIL = 0x01, FLOOR, EXPAND, TRUNC, HALFCEIL, HALFFLOOR, HALFEXPAND, HALFTRUNC, HALFEVEN, EXCEPTION }; +enum class TrailingZeroDisplay : uint8_t { AUTO = 0x01, STRIPIFINTEGER, EXCEPTION }; constexpr uint32_t MAX_DIGITS = 21; constexpr uint32_t MAX_FRACTION_DIGITS = 20; @@ -547,6 +551,29 @@ public: static JSHandle ToString(JSThread *thread, const JSHandle &locale); + static bool IsValidRoundingIncrement(int value) { + switch (value) { + case 1: + case 2: + case 5: + case 10: + case 20: + case 25: + case 50: + case 100: + case 200: + case 250: + case 500: + case 1000: + case 2000: + case 2500: + case 5000: + return true; + default: + return false; + } + } + // 12.1.1 SetNumberFormatDigitOptions ( intlObj, options, mnfdDefault, mxfdDefault, notation ) template static void SetNumberFormatDigitOptions(JSThread *thread, const JSHandle &intlObj, @@ -568,6 +595,50 @@ public: // Set intlObj.[[MaximumSignificantDigits]] to 0. intlObj->SetMaximumSignificantDigits(thread, JSTaggedValue(0)); + JSHandle property = globalConst->GetHandledLocaleMatcherString(); + + // Let roundingIncrement be ? GetNumberOption(options, "roundingIncrement", 1, 5000, 1). + property = globalConst->GetHandledRoundingIncrementString(); + auto roundingIncrement = GetNumberOption(thread, JSHandle::Cast(options), property, 1, 5000, 1); + // If roundingIncrement is not in « 1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1000, 2000, 2500, 5000 », throw a RangeError exception. + if (!IsValidRoundingIncrement(roundingIncrement)) { + RETURN_IF_ABRUPT_COMPLETION(thread); + } + RETURN_IF_ABRUPT_COMPLETION(thread); + if (roundingIncrement != 1) { + mnfdDefault = mxfdDefault; + if (intlObj->GetRoundingType() != RoundingType::FRACTIONDIGITS) { + THROW_TYPE_ERROR(thread, "is not fractionDigits"); + } + } + intlObj->SetRoundingIncrement(thread, JSTaggedValue(roundingIncrement)); + + // Let roundingMode be ? GetOption(options, "roundingMode", string, « "ceil", "floor", "expand", "trunc", "halfCeil", "halfFloor", "halfExpand", "halfTrunc", "halfEven" », "halfExpand"). + property = globalConst->GetHandledRoundingModeString(); + auto roundingMode = JSLocale::GetOptionOfString( + thread, JSHandle::Cast(options), property, + {RoundingMode::CEIL, RoundingMode::FLOOR, RoundingMode::EXPAND, RoundingMode::TRUNC, RoundingMode::HALFCEIL, + RoundingMode::HALFFLOOR, RoundingMode::HALFEXPAND, RoundingMode::HALFTRUNC, RoundingMode::HALFEVEN}, + {"ceil", "floor", "expand", "trunc", "halfCeil", "halfFloor", "halfExpand", "halfTrunc", "halfEven"}, RoundingMode::HALFEXPAND); + RETURN_IF_ABRUPT_COMPLETION(thread); + intlObj->SetRoundingMode(roundingMode); + + // Let roundingPriority be ? GetOption(options, "roundingPriority", string, « "auto", "morePrecision", "lessPrecision" », "auto"). + property = globalConst->GetHandledRoundingPriorityString(); + auto roundingPriority = JSLocale::GetOptionOfString(thread, JSHandle::Cast(options), property, {RoundingPriority::AUTO, + RoundingPriority::MOREPRECISION, RoundingPriority::LESSPRECISION}, {"auto", "morePrecision", "lessPrecision"}, RoundingPriority::AUTO); + RETURN_IF_ABRUPT_COMPLETION(thread); + intlObj->SetRoundingPriority(roundingPriority); + + // Let trailingZeroDisplay be ? GetOption(options, "trailingZeroDisplay", string, « "auto", "stripIfInteger" », "auto"). + property = globalConst->GetHandledTrailingZeroDisplayString(); + auto trailingZeroDisplay = JSLocale::GetOptionOfString( + thread, JSHandle::Cast(options), property, + {TrailingZeroDisplay::AUTO, TrailingZeroDisplay::STRIPIFINTEGER}, + {"auto", "stripIfInteger"}, TrailingZeroDisplay::AUTO); + RETURN_IF_ABRUPT_COMPLETION(thread); + intlObj->SetTrailingZeroDisplay(trailingZeroDisplay); + // 5. Let mnid be ? GetNumberOption(options, "minimumIntegerDigits,", 1, 21, 1). JSHandle mnidKey = globalConst->GetHandledMinimumIntegerDigitsString(); int mnid = GetNumberOption(thread, JSHandle::Cast(options), mnidKey, 1, MAX_DIGITS, 1); @@ -591,7 +662,7 @@ public: // 10. Set intlObj.[[MinimumIntegerDigits]] to mnid. // 11. If mnsd is not undefined or mxsd is not undefined, then - if (!mnsd->IsUndefined() || !mxsd->IsUndefined()) { + if (!mnsd->IsUndefined() || !mxsd->IsUndefined()) { // a. Set intlObj.[[RoundingType]] to significantDigits. intlObj->SetRoundingType(RoundingType::SIGNIFICANTDIGITS); // b. Let mnsd be ? DefaultNumberOption(mnsd, 1, 21, 1). @@ -635,7 +706,7 @@ public: } else if (notation == NotationOption::COMPACT) { // 13. Else if notation is "compact", then // a. Set intlObj.[[RoundingType]] to compactRounding. - intlObj->SetRoundingType(RoundingType::COMPACTROUNDING); + intlObj->SetRoundingType(RoundingType::LESSPRECISION); } else { // 14. else, // a.Set intlObj.[[RoundingType]] to fractionDigits. diff --git a/ecmascript/js_number_format.cpp b/ecmascript/js_number_format.cpp index 9b4771f4d1fca177e161bea97d22f082c06144ec..e70fd23bf9f4d85d13ae99cc87d3d41e4b083b21 100644 --- a/ecmascript/js_number_format.cpp +++ b/ecmascript/js_number_format.cpp @@ -200,37 +200,37 @@ JSHandle OptionToEcmaString(JSThread *thread, SignDisplayOption s return result; } -JSHandle OptionToEcmaString(JSThread *thread, RoundingModeOption roundingMode) +JSHandle OptionToEcmaString(JSThread *thread, RoundingMode roundingMode) { JSMutableHandle result(thread, JSTaggedValue::Undefined()); auto globalConst = thread->GlobalConstants(); switch (roundingMode) { - case RoundingModeOption::CEIL: + case RoundingMode::CEIL: result.Update(globalConst->GetHandledCeilString().GetTaggedValue()); break; - case RoundingModeOption::FLOOR: + case RoundingMode::FLOOR: result.Update(globalConst->GetHandledFloorString().GetTaggedValue()); break; - case RoundingModeOption::EXPAND: + case RoundingMode::EXPAND: result.Update(globalConst->GetHandledExpandString().GetTaggedValue()); break; - case RoundingModeOption::TRUNC: + case RoundingMode::TRUNC: result.Update(globalConst->GetHandledTruncString().GetTaggedValue()); break; - case RoundingModeOption::HALFCEIL: + case RoundingMode::HALFCEIL: result.Update(globalConst->GetHandledHalfCeilString().GetTaggedValue()); break; - case RoundingModeOption::HALFFLOOR: + case RoundingMode::HALFFLOOR: result.Update(globalConst->GetHandledHalfFloorString().GetTaggedValue()); break; - case RoundingModeOption::HALFEXPAND: + case RoundingMode::HALFEXPAND: result.Update(globalConst->GetHandledHalfExpandString().GetTaggedValue()); break; - case RoundingModeOption::HALFTRUNC: + case RoundingMode::HALFTRUNC: result.Update(globalConst->GetHandledHalfTruncString().GetTaggedValue()); break; - case RoundingModeOption::HALFEVEN: + case RoundingMode::HALFEVEN: result.Update(globalConst->GetHandledHalfEvenString().GetTaggedValue()); break; default: @@ -240,15 +240,36 @@ JSHandle OptionToEcmaString(JSThread *thread, RoundingModeOption return result; } -JSHandle OptionToEcmaString(JSThread *thread, TrailingZeroDisplayOption trailingZeroDisplay) +JSHandle OptionToEcmaString(JSThread *thread, RoundingPriority roundingPriority) +{ + JSMutableHandle result(thread, JSTaggedValue::Undefined()); + auto globalConst = thread->GlobalConstants(); + + switch (roundingPriority) { + case RoundingPriority::AUTO: + result.Update(globalConst->GetHandledAutoString().GetTaggedValue()); + break; + case RoundingPriority::MOREPRECISION: + result.Update(globalConst->GetHandledMorePrecisionString().GetTaggedValue()); + break; + case RoundingPriority::LESSPRECISION: + result.Update(globalConst->GetHandledLessPrecisionString().GetTaggedValue()); + break; + default: + break; + } + return result; +} + +JSHandle OptionToEcmaString(JSThread *thread, TrailingZeroDisplay trailingZeroDisplay) { JSMutableHandle result(thread, JSTaggedValue::Undefined()); auto globalConst = thread->GlobalConstants(); switch (trailingZeroDisplay) { - case TrailingZeroDisplayOption::AUTO: + case TrailingZeroDisplay::AUTO: result.Update(globalConst->GetHandledAutoString().GetTaggedValue()); break; - case TrailingZeroDisplayOption::STRTPIFINTEGER: + case TrailingZeroDisplay::STRIPIFINTEGER: result.Update(globalConst->GetHandledStripIfIntegerString().GetTaggedValue()); break; default: @@ -665,27 +686,27 @@ void JSNumberFormat::InitializeNumberFormat(JSThread *thread, const JSHandleGetHandledCompactDisplayString(); auto compactDisplay = JSLocale::GetOptionOfString( thread, optionsObject, property, {CompactDisplayOption::SHORT, CompactDisplayOption::LONG}, {"short", "long"}, CompactDisplayOption::SHORT); + RETURN_IF_ABRUPT_COMPLETION(thread); numberFormat->SetCompactDisplay(compactDisplay); // Trans NotationOption to ICU Noation and set to icuNumberFormatter - if (notation == NotationOption::COMPACT) { - switch (compactDisplay) { - case CompactDisplayOption::SHORT: + switch (notation) { + case NotationOption::COMPACT: + if (compactDisplay == CompactDisplayOption::SHORT) { icuNumberFormatter = icuNumberFormatter.notation(icu::number::Notation::compactShort()); - break; - case CompactDisplayOption::LONG: + } else if (compactDisplay == CompactDisplayOption::LONG) { icuNumberFormatter = icuNumberFormatter.notation(icu::number::Notation::compactLong()); - break; - default: - break; - } - } - switch (notation) { + } + defaultUseGrouping = UseGroupingOption::MIN2; + break; case NotationOption::STANDARD: icuNumberFormatter = icuNumberFormatter.notation(icu::number::Notation::simple()); break; @@ -701,7 +722,6 @@ void JSNumberFormat::InitializeNumberFormat(JSThread *thread, const JSHandleGetHandledUserGroupingString(); - UseGroupingOption defaultUseGrouping = UseGroupingOption::AUTO; auto useGrouping = JSLocale::GetStringOrBooleanOption( thread, optionsObject, property, {UseGroupingOption::MIN2, UseGroupingOption::AUTO, UseGroupingOption::ALWAYS}, @@ -731,41 +751,35 @@ void JSNumberFormat::InitializeNumberFormat(JSThread *thread, const JSHandleGetHandledRoundingModeString(); - auto roundingMode = JSLocale::GetOptionOfString( - thread, optionsObject, property, - {RoundingModeOption::CEIL, RoundingModeOption::FLOOR, RoundingModeOption::EXPAND, RoundingModeOption::TRUNC, RoundingModeOption::HALFCEIL, - RoundingModeOption::HALFFLOOR, RoundingModeOption::HALFEXPAND, RoundingModeOption::HALFTRUNC, RoundingModeOption::HALFEVEN}, - {"ceil", "floor", "expand", "trunc", "halfCeil", "halfFloor", "halfExpand", "halfTrunc", "halfEven"}, RoundingModeOption::HALFEXPAND); - RETURN_IF_ABRUPT_COMPLETION(thread); - numberFormat->SetRoundingMode(roundingMode); + + RoundingMode roundingMode = numberFormat->GetRoundingMode(); switch (roundingMode) { - case RoundingModeOption::CEIL: + case RoundingMode::CEIL: icuNumberFormatter = icuNumberFormatter.roundingMode(UNumberFormatRoundingMode::UNUM_ROUND_CEILING); break; - case RoundingModeOption::FLOOR: + case RoundingMode::FLOOR: icuNumberFormatter = icuNumberFormatter.roundingMode(UNumberFormatRoundingMode::UNUM_ROUND_FLOOR); break; - case RoundingModeOption::EXPAND: + case RoundingMode::EXPAND: icuNumberFormatter = icuNumberFormatter.roundingMode(UNumberFormatRoundingMode::UNUM_ROUND_UP); break; - case RoundingModeOption::TRUNC: + case RoundingMode::TRUNC: icuNumberFormatter = icuNumberFormatter.roundingMode(UNumberFormatRoundingMode::UNUM_ROUND_DOWN); break; - case RoundingModeOption::HALFCEIL: + case RoundingMode::HALFCEIL: icuNumberFormatter = icuNumberFormatter.roundingMode(UNumberFormatRoundingMode::UNUM_ROUND_HALF_CEILING); break; - case RoundingModeOption::HALFFLOOR: + case RoundingMode::HALFFLOOR: icuNumberFormatter = icuNumberFormatter.roundingMode(UNumberFormatRoundingMode::UNUM_ROUND_HALF_FLOOR); break; - case RoundingModeOption::HALFEXPAND: + case RoundingMode::HALFEXPAND: icuNumberFormatter = icuNumberFormatter.roundingMode(UNumberFormatRoundingMode::UNUM_ROUND_HALFUP); break; - case RoundingModeOption::HALFTRUNC: + case RoundingMode::HALFTRUNC: icuNumberFormatter = icuNumberFormatter.roundingMode(UNumberFormatRoundingMode::UNUM_ROUND_HALFDOWN); break; - case RoundingModeOption::HALFEVEN: + case RoundingMode::HALFEVEN: icuNumberFormatter = icuNumberFormatter.roundingMode(UNumberFormatRoundingMode::UNUM_ROUND_HALFEVEN); break; default: @@ -830,15 +844,6 @@ void JSNumberFormat::InitializeNumberFormat(JSThread *thread, const JSHandleGetHandledTrailingZeroDisplayString(); - auto trailingZeroDisplay = JSLocale::GetOptionOfString( - thread, optionsObject, property, - {TrailingZeroDisplayOption::AUTO, TrailingZeroDisplayOption::STRTPIFINTEGER}, - {"auto", "stripIfInteger"}, TrailingZeroDisplayOption::AUTO); - RETURN_IF_ABRUPT_COMPLETION(thread); - numberFormat->SetTrailingZeroDisplay(trailingZeroDisplay); - if (forIcuCache) { std::string cacheEntry = locales->IsUndefined() ? "" : EcmaStringAccessor(locales.GetTaggedValue()).ToStdString(); @@ -959,7 +964,9 @@ void GroupToParts(JSThread *thread, const icu::number::FormattedNumber &formatte int32_t fieldId = cfpo.getField(); int32_t start = cfpo.getStart(); int32_t limit = cfpo.getLimit(); - typeString.Update(globalConst->GetLiteralString()); + // typeString.Update(globalConst->GetLiteralString()); + typeString.Update(globalConst->GetPercentSignString()); + // If start greater than previousLimit, means a literal type exists before number fields // so add a literal type with value of formattedText.sub(0, start) // Special case when fieldId is UNUM_GROUPING_SEPARATOR_FIELD @@ -993,6 +1000,7 @@ void GroupToParts(JSThread *thread, const icu::number::FormattedNumber &formatte lastFieldGroup = false; } // Special case in ICU when style is unit and unit is percent + // if (styleOption == StyleOption::PERCENT && static_cast(fieldId) == UNUM_MEASURE_UNIT_FIELD) { if (styleOption == StyleOption::UNIT && static_cast(fieldId) == UNUM_PERCENT_FIELD) { typeString.Update(globalConst->GetUnitString()); } else { @@ -1007,7 +1015,8 @@ void GroupToParts(JSThread *thread, const icu::number::FormattedNumber &formatte // If iterated length is smaller than formattedText.length, means a literal type exists after number fields // so add a literal type with value of formattedText.sub(previousLimit, formattedText.length) if (formattedText.length() > previousLimit) { - typeString.Update(globalConst->GetLiteralString()); + // typeString.Update(globalConst->GetLiteralString()); + typeString.Update(globalConst->GetPercentSignString()); JSHandle substring = intl::LocaleHelper::UStringToString(thread, formattedText, previousLimit, formattedText.length()); JSLocale::PutElement(thread, index, receiver, typeString, JSHandle::Cast(substring)); @@ -1202,7 +1211,7 @@ void JSNumberFormat::ResolvedOptions(JSThread *thread, const JSHandleGetHandledMinimumSignificantDigitsString(); JSHandle minimumSignificantDigits(thread, numberFormat->GetMinimumSignificantDigits()); @@ -1252,16 +1261,42 @@ void JSNumberFormat::ResolvedOptions(JSThread *thread, const JSHandleGetHandledRoundingIncrementString(); + JSHandle roundingIncrement(thread, numberFormat->GetRoundingIncrement()); + JSObject::CreateDataPropertyOrThrow(thread, options, property, roundingIncrement); + RETURN_IF_ABRUPT_COMPLETION(thread); + // [[RoundingMode]] property = globalConst->GetHandledRoundingModeString(); - RoundingModeOption roundingMode= numberFormat->GetRoundingMode(); + RoundingMode roundingMode= numberFormat->GetRoundingMode(); JSHandle roundingModeString = OptionToEcmaString(thread, roundingMode); JSObject::CreateDataPropertyOrThrow(thread, options, property, roundingModeString); RETURN_IF_ABRUPT_COMPLETION(thread); - // TrailingZeroDisplay + // [[roundingPriority]] + property = globalConst->GetHandledRoundingPriorityString(); + RoundingPriority roundingPriority = numberFormat->GetRoundingPriority(); + JSHandle roundingPriorityString = OptionToEcmaString(thread, roundingPriority); + JSObject::CreateDataPropertyOrThrow(thread, options, property, roundingPriorityString); + RETURN_IF_ABRUPT_COMPLETION(thread); + + // [[MinimumFractionDigits]] + if(roundingPriority == RoundingPriority::MOREPRECISION) { + property = globalConst->GetHandledMinimumFractionDigitsString(); + JSHandle minimumFractionDigits(thread, numberFormat->GetMinimumFractionDigits()); + JSObject::CreateDataPropertyOrThrow(thread, options, property, minimumFractionDigits); + RETURN_IF_ABRUPT_COMPLETION(thread); + // [[MaximumFractionDigits]] + property = globalConst->GetHandledMaximumFractionDigitsString(); + JSHandle maximumFractionDigits(thread, numberFormat->GetMaximumFractionDigits()); + JSObject::CreateDataPropertyOrThrow(thread, options, property, maximumFractionDigits); + RETURN_IF_ABRUPT_COMPLETION(thread); + } + + // [[TrailingZeroDisplay]] property = globalConst->GetHandledTrailingZeroDisplayString(); - TrailingZeroDisplayOption trailingZeroDisplay = numberFormat->GetTrailingZeroDisplay(); + TrailingZeroDisplay trailingZeroDisplay = numberFormat->GetTrailingZeroDisplay(); JSHandle trailingZeroDisplayString = OptionToEcmaString(thread, trailingZeroDisplay); JSObject::CreateDataPropertyOrThrow(thread, options, property, trailingZeroDisplayString); RETURN_IF_ABRUPT_COMPLETION(thread); diff --git a/ecmascript/js_number_format.h b/ecmascript/js_number_format.h index 7dd673c26fcced4d62fb878827e871e77c2464d2..c19dd1b592e108ec17f13827a219c3930d60fb8b 100644 --- a/ecmascript/js_number_format.h +++ b/ecmascript/js_number_format.h @@ -33,10 +33,6 @@ enum class UseGroupingOption : uint8_t { OFF = 0x01, MIN2 , AUTO, ALWAYS, EXCEPT enum class SignDisplayOption : uint8_t { AUTO = 0x01, ALWAYS, NEVER, EXCEPTZERO, NEGATIVE, EXCEPTION }; -enum class RoundingModeOption : uint8_t { CEIL = 0x01, FLOOR, EXPAND, TRUNC, HALFCEIL, HALFFLOOR, HALFEXPAND, HALFTRUNC, HALFEVEN, EXCEPTION }; - -enum class TrailingZeroDisplayOption : uint8_t { AUTO = 0x01, STRTPIFINTEGER, EXCEPTION }; - enum class CurrencyDisplayOption : uint8_t { CODE = 0x01, SYMBOL, NARROWSYMBOL, NAME, EXCEPTION }; enum class CurrencySignOption : uint8_t { STANDARD = 0x01, ACCOUNTING, EXCEPTION }; @@ -51,9 +47,9 @@ struct FractionDigitsOption { static const std::set SANCTIONED_UNIT({ "acre", "bit", "byte", "celsius", "centimeter", "day", "degree", "fahrenheit", "fluid-ounce", "foot", "gallon", "gigabit", "gigabyte", "gram", "hectare", "hour", "inch", "kilobit", "kilobyte", "kilogram", - "kilometer", "liter", "megabit", "megabyte", "meter", "mile", + "kilometer", "liter", "megabit", "megabyte", "meter", "microsecond", "mile", "mile-scandinavian", "millimeter", "milliliter", "millisecond", - "minute", "month", "ounce", "percent", "petabyte", "pound", "second", + "minute", "month", "nanosecond", "ounce", "percent", "petabyte", "pound", "second", "stone", "terabit", "terabyte", "week", "yard", "year" }); class JSNumberFormat : public JSObject { @@ -69,9 +65,9 @@ public: ACCESSORS(MinimumFractionDigits, MINIMUM_FRACTION_DIGITS_OFFSET, MAXIMUM_FRACTION_DIGITS_OFFSET) ACCESSORS(MaximumFractionDigits, MAXIMUM_FRACTION_DIGITS_OFFSET, MINIMUM_SIGNIFICANT_DIGITS_OFFSET) ACCESSORS(MinimumSignificantDigits, MINIMUM_SIGNIFICANT_DIGITS_OFFSET, MAXIMUM_SIGNIFICANT_DIGITS_OFFSET) - ACCESSORS(MaximumSignificantDigits, MAXIMUM_SIGNIFICANT_DIGITS_OFFSET, USER_GROUPING_OFFSET) - // ACCESSORS(UseGrouping, USER_GROUPING_OFFSET, BOUND_FORMAT_OFFSET) - ACCESSORS(BoundFormat, USER_GROUPING_OFFSET, ICU_FIELD_OFFSET) + ACCESSORS(MaximumSignificantDigits, MAXIMUM_SIGNIFICANT_DIGITS_OFFSET, ROUNDING_INCREMENT_OFFSET) + ACCESSORS(RoundingIncrement, ROUNDING_INCREMENT_OFFSET, BOUND_FORMAT_OFFSET) + ACCESSORS(BoundFormat, BOUND_FORMAT_OFFSET, ICU_FIELD_OFFSET) ACCESSORS(IcuField, ICU_FIELD_OFFSET, BIT_FIELD_OFFSET) ACCESSORS_BIT_FIELD(BitField, BIT_FIELD_OFFSET, LAST_OFFSET) DEFINE_ALIGN_SIZE(LAST_OFFSET); @@ -87,6 +83,7 @@ public: static constexpr size_t TRAILING_ZERO_DISPLAY_BITS = 2; static constexpr size_t COMPACT_DISPLAY_BITS = 2; static constexpr size_t NOTATION_BITS = 3; + static constexpr size_t ROUNDING_PRIORITY_BITS = 2; static constexpr size_t ROUNDING_TYPE_BITS = 3; FIRST_BIT_FIELD(BitField, Style, StyleOption, STYLE_BITS) NEXT_BIT_FIELD(BitField, CurrencySign, CurrencySignOption, CURRENCY_SIGN_BITS, Style) @@ -94,12 +91,12 @@ public: NEXT_BIT_FIELD(BitField, UnitDisplay, UnitDisplayOption, UNIT_DISPLAY_BITS, CurrencyDisplay) NEXT_BIT_FIELD(BitField, UseGrouping, UseGroupingOption, USE_GROUPING_BITS, UnitDisplay) NEXT_BIT_FIELD(BitField, SignDisplay, SignDisplayOption, SIGN_DISPLAY_BITS, UseGrouping) - NEXT_BIT_FIELD(BitField, RoundingMode, RoundingModeOption, ROUNDING_MODE_BITS, SignDisplay) - NEXT_BIT_FIELD(BitField, TrailingZeroDisplay, TrailingZeroDisplayOption, TRAILING_ZERO_DISPLAY_BITS, RoundingMode) + NEXT_BIT_FIELD(BitField, RoundingMode, RoundingMode, ROUNDING_MODE_BITS, SignDisplay) + NEXT_BIT_FIELD(BitField, TrailingZeroDisplay, TrailingZeroDisplay, TRAILING_ZERO_DISPLAY_BITS, RoundingMode) NEXT_BIT_FIELD(BitField, CompactDisplay, CompactDisplayOption, COMPACT_DISPLAY_BITS, TrailingZeroDisplay) NEXT_BIT_FIELD(BitField, Notation, NotationOption, NOTATION_BITS, CompactDisplay) - NEXT_BIT_FIELD(BitField, RoundingType, RoundingType, ROUNDING_TYPE_BITS, Notation) - + NEXT_BIT_FIELD(BitField, RoundingPriority, RoundingPriority, ROUNDING_PRIORITY_BITS, Notation) + NEXT_BIT_FIELD(BitField, RoundingType, RoundingType, ROUNDING_TYPE_BITS, RoundingPriority) DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LOCALE_OFFSET, BIT_FIELD_OFFSET) DECL_DUMP() @@ -166,10 +163,12 @@ public: int maximumSignificantDigits = formatter->GetMaximumSignificantDigits().GetInt(); int minimumFractionDigits = formatter->GetMinimumFractionDigits().GetInt(); int maximumFractionDigits = formatter->GetMaximumFractionDigits().GetInt(); + int roundingIncrement = formatter->GetRoundingIncrement().GetInt(); // If roundingtype is "compact-rounding" return ICU formatter RoundingType roundingType = formatter->GetRoundingType(); - if (roundingType == RoundingType::COMPACTROUNDING) { + TrailingZeroDisplay trailingZeroDisplay = formatter->GetTrailingZeroDisplay(); + if (roundingType == RoundingType::LESSPRECISION) { return icuNumberformatter; } // Else, Set ICU formatter FractionDigits and SignificantDigits @@ -183,6 +182,14 @@ public: precision = icu::number::Precision::minMaxSignificantDigits(minimumSignificantDigits, maximumSignificantDigits); } + + if (roundingIncrement != 1) { + precision = icu::number::Precision::incrementExact(roundingIncrement, -maximumFractionDigits).withMinFraction(minimumFractionDigits); + } + + if (trailingZeroDisplay == TrailingZeroDisplay::STRIPIFINTEGER) { + precision = precision.trailingZeroDisplay(UNUM_TRAILING_ZERO_HIDE_IF_WHOLE); + } return icuNumberformatter.precision(precision); } }; diff --git a/ecmascript/js_plural_rules.h b/ecmascript/js_plural_rules.h index ead2a4230c03e9a178ba343f9201489e3f54d87b..9d15525759aad24ade4d2b99cb5d3894700cc493 100644 --- a/ecmascript/js_plural_rules.h +++ b/ecmascript/js_plural_rules.h @@ -37,7 +37,8 @@ public: ACCESSORS(MinimumFractionDigits, MINIMUM_FRACTION_DIGITS_OFFSET, MAXIMUM_FRACTION_DIGITS_OFFSET) ACCESSORS(MaximumFractionDigits, MAXIMUM_FRACTION_DIGITS_OFFSET, MININUM_SIGNIFICANT_DIGITS_OFFSET) ACCESSORS(MinimumSignificantDigits, MININUM_SIGNIFICANT_DIGITS_OFFSET, MAXINUM_SIGNIFICANT_DIGITS_OFFSET) - ACCESSORS(MaximumSignificantDigits, MAXINUM_SIGNIFICANT_DIGITS_OFFSET, ICU_PLURAL_RULES_OFFSET) + ACCESSORS(MaximumSignificantDigits, MAXINUM_SIGNIFICANT_DIGITS_OFFSET, ROUNDING_INCREMENT_OFFSET) + ACCESSORS(RoundingIncrement, ROUNDING_INCREMENT_OFFSET, ICU_PLURAL_RULES_OFFSET) ACCESSORS(IcuPR, ICU_PLURAL_RULES_OFFSET, ICU_NUMBER_FORMAT_OFFSET) ACCESSORS(IcuNF, ICU_NUMBER_FORMAT_OFFSET, BIT_FIELD_OFFSET) ACCESSORS_BIT_FIELD(BitField, BIT_FIELD_OFFSET, LAST_OFFSET) @@ -45,9 +46,15 @@ public: // define BitField static constexpr size_t ROUNDING_TYPE_BITS = 3; + static constexpr size_t ROUNDING_PRIORITY_BITS = 2; + static constexpr size_t ROUNDING_MODE_BITS = 4; + static constexpr size_t TRAILING_ZERO_DISPLAY_BITS = 2; static constexpr size_t TYPE_BITS = 2; FIRST_BIT_FIELD(BitField, RoundingType, RoundingType, ROUNDING_TYPE_BITS) - NEXT_BIT_FIELD(BitField, Type, TypeOption, TYPE_BITS, RoundingType) + NEXT_BIT_FIELD(BitField, RoundingPriority, RoundingPriority, ROUNDING_PRIORITY_BITS, RoundingType) + NEXT_BIT_FIELD(BitField, RoundingMode, RoundingMode, ROUNDING_MODE_BITS, RoundingPriority) + NEXT_BIT_FIELD(BitField, TrailingZeroDisplay, TrailingZeroDisplay, TRAILING_ZERO_DISPLAY_BITS, RoundingMode) + NEXT_BIT_FIELD(BitField, Type, TypeOption, TYPE_BITS, TrailingZeroDisplay) DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LOCALE_OFFSET, BIT_FIELD_OFFSET) DECL_DUMP() diff --git a/ecmascript/object_factory.cpp b/ecmascript/object_factory.cpp index ee608516d362a9f61b81199b49c15d5a2a410e3f..92e1ea50381acdbfe014f62945c26c252c581942 100644 --- a/ecmascript/object_factory.cpp +++ b/ecmascript/object_factory.cpp @@ -1064,6 +1064,7 @@ void ObjectFactory::InitializeJSObject(const JSHandle &obj, const JSHa JSNumberFormat::Cast(*obj)->SetMaximumFractionDigits(thread_, JSTaggedValue::Undefined()); JSNumberFormat::Cast(*obj)->SetMinimumSignificantDigits(thread_, JSTaggedValue::Undefined()); JSNumberFormat::Cast(*obj)->SetMaximumSignificantDigits(thread_, JSTaggedValue::Undefined()); + JSNumberFormat::Cast(*obj)->SetRoundingIncrement(thread_, JSTaggedValue::Undefined()); JSNumberFormat::Cast(*obj)->SetUseGrouping(UseGroupingOption::EXCEPTION); JSNumberFormat::Cast(*obj)->SetBoundFormat(thread_, JSTaggedValue::Undefined()); JSNumberFormat::Cast(*obj)->SetIcuField(thread_, JSTaggedValue::Undefined()); @@ -1074,7 +1075,10 @@ void ObjectFactory::InitializeJSObject(const JSHandle &obj, const JSHa JSNumberFormat::Cast(*obj)->SetSignDisplay(SignDisplayOption::EXCEPTION); JSNumberFormat::Cast(*obj)->SetCompactDisplay(CompactDisplayOption::EXCEPTION); JSNumberFormat::Cast(*obj)->SetNotation(NotationOption::EXCEPTION); + JSNumberFormat::Cast(*obj)->SetRoundingMode(RoundingMode::EXCEPTION); + JSNumberFormat::Cast(*obj)->SetRoundingPriority(RoundingPriority::EXCEPTION); JSNumberFormat::Cast(*obj)->SetRoundingType(RoundingType::EXCEPTION); + JSNumberFormat::Cast(*obj)->SetTrailingZeroDisplay(TrailingZeroDisplay::EXCEPTION); break; } case JSType::JS_RELATIVE_TIME_FORMAT: { @@ -1104,8 +1108,12 @@ void ObjectFactory::InitializeJSObject(const JSHandle &obj, const JSHa JSPluralRules::Cast(*obj)->SetMaximumFractionDigits(thread_, JSTaggedValue::Undefined()); JSPluralRules::Cast(*obj)->SetMinimumSignificantDigits(thread_, JSTaggedValue::Undefined()); JSPluralRules::Cast(*obj)->SetMaximumSignificantDigits(thread_, JSTaggedValue::Undefined()); + JSPluralRules::Cast(*obj)->SetRoundingIncrement(thread_, JSTaggedValue::Undefined()); JSPluralRules::Cast(*obj)->SetIcuPR(thread_, JSTaggedValue::Undefined()); JSPluralRules::Cast(*obj)->SetIcuNF(thread_, JSTaggedValue::Undefined()); + JSPluralRules::Cast(*obj)->SetRoundingMode(RoundingMode::EXCEPTION); + JSPluralRules::Cast(*obj)->SetRoundingPriority(RoundingPriority::EXCEPTION); + JSPluralRules::Cast(*obj)->SetTrailingZeroDisplay(TrailingZeroDisplay::EXCEPTION); JSPluralRules::Cast(*obj)->SetRoundingType(RoundingType::EXCEPTION); JSPluralRules::Cast(*obj)->SetType(TypeOption::EXCEPTION); break;