v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
js-list-format.cc
Go to the documentation of this file.
1// Copyright 2018 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_INTL_SUPPORT
6#error Internationalization is expected to be enabled.
7#endif // V8_INTL_SUPPORT
8
10
11#include <memory>
12#include <vector>
13
15#include "src/heap/factory.h"
24#include "unicode/fieldpos.h"
25#include "unicode/fpositer.h"
26#include "unicode/listformatter.h"
27#include "unicode/ulistformatter.h"
28
29namespace v8 {
30namespace internal {
31
32namespace {
33
34UListFormatterWidth GetIcuWidth(JSListFormat::Style style) {
35 switch (style) {
37 return ULISTFMT_WIDTH_WIDE;
39 return ULISTFMT_WIDTH_SHORT;
41 return ULISTFMT_WIDTH_NARROW;
42 }
44}
45
46UListFormatterType GetIcuType(JSListFormat::Type type) {
47 switch (type) {
49 return ULISTFMT_TYPE_AND;
51 return ULISTFMT_TYPE_OR;
53 return ULISTFMT_TYPE_UNITS;
54 }
56}
57
58} // namespace
59
61 Isolate* isolate, DirectHandle<Map> map, DirectHandle<Object> locales,
62 DirectHandle<Object> input_options) {
63 // 3. Let requestedLocales be ? CanonicalizeLocaleList(locales).
64 Maybe<std::vector<std::string>> maybe_requested_locales =
65 Intl::CanonicalizeLocaleList(isolate, locales);
66 MAYBE_RETURN(maybe_requested_locales, DirectHandle<JSListFormat>());
67 std::vector<std::string> requested_locales =
68 maybe_requested_locales.FromJust();
69
71 const char* service = "Intl.ListFormat";
72 // 4. Let options be GetOptionsObject(_options_).
73 ASSIGN_RETURN_ON_EXCEPTION(isolate, options,
74 GetOptionsObject(isolate, input_options, service));
75
76 // Note: No need to create a record. It's not observable.
77 // 6. Let opt be a new Record.
78
79 // 7. Let matcher be ? GetOption(options, "localeMatcher", "string", «
80 // "lookup", "best fit" », "best fit").
81 Maybe<Intl::MatcherOption> maybe_locale_matcher =
82 Intl::GetLocaleMatcher(isolate, options, service);
83 MAYBE_RETURN(maybe_locale_matcher, MaybeDirectHandle<JSListFormat>());
84
85 // 8. Set opt.[[localeMatcher]] to matcher.
86 Intl::MatcherOption matcher = maybe_locale_matcher.FromJust();
87
88 // 10. Let r be ResolveLocale(%ListFormat%.[[AvailableLocales]],
89 // requestedLocales, opt, undefined, localeData).
90 Maybe<Intl::ResolvedLocale> maybe_resolve_locale =
92 requested_locales, matcher, {});
93 if (maybe_resolve_locale.IsNothing()) {
94 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError));
95 }
96 Intl::ResolvedLocale r = maybe_resolve_locale.FromJust();
97 DirectHandle<String> locale_str =
98 isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str());
99
100 // 12. Let t be GetOption(options, "type", "string", «"conjunction",
101 // "disjunction", "unit"», "conjunction").
103 isolate, options, "type", service, {"conjunction", "disjunction", "unit"},
106 Type type_enum = maybe_type.FromJust();
107
108 // 14. Let s be ? GetOption(options, "style", "string",
109 // «"long", "short", "narrow"», "long").
111 isolate, options, "style", service, {"long", "short", "narrow"},
114 Style style_enum = maybe_style.FromJust();
115
116 icu::Locale icu_locale = r.icu_locale;
117 UErrorCode status = U_ZERO_ERROR;
118 std::shared_ptr<icu::ListFormatter> formatter{
119 icu::ListFormatter::createInstance(icu_locale, GetIcuType(type_enum),
120 GetIcuWidth(style_enum), status)};
121 if (U_FAILURE(status) || formatter == nullptr) {
122 THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kIcuError));
123 }
124
126 Managed<icu::ListFormatter>::From(isolate, 0, std::move(formatter));
127
128 // Now all properties are ready, so we can allocate the result object.
129 DirectHandle<JSListFormat> list_format =
130 Cast<JSListFormat>(isolate->factory()->NewFastOrSlowJSObjectFromMap(map));
132 list_format->set_flags(0);
133 list_format->set_icu_formatter(*managed_formatter);
134
135 // 11. Set listFormat.[[Locale]] to r.[[Locale]].
136 list_format->set_locale(*locale_str);
137
138 // 13. Set listFormat.[[Type]] to t.
139 list_format->set_type(type_enum);
140
141 // 15. Set listFormat.[[Style]] to s.
142 list_format->set_style(style_enum);
143
144 return list_format;
145}
146
147// ecma402 #sec-intl.pluralrules.prototype.resolvedoptions
149 Isolate* isolate, DirectHandle<JSListFormat> format) {
150 Factory* factory = isolate->factory();
151 // 4. Let options be ! ObjectCreate(%ObjectPrototype%).
153 factory->NewJSObject(isolate->object_function());
154
155 // 5. For each row of Table 1, except the header row, do
156 // Table 1: Resolved Options of ListFormat Instances
157 // Internal Slot Property
158 // [[Locale]] "locale"
159 // [[Type]] "type"
160 // [[Style]] "style"
161 DirectHandle<String> locale(format->locale(), isolate);
162 JSObject::AddProperty(isolate, result, factory->locale_string(), locale,
163 NONE);
164 JSObject::AddProperty(isolate, result, factory->type_string(),
165 format->TypeAsString(isolate), NONE);
166 JSObject::AddProperty(isolate, result, factory->style_string(),
167 format->StyleAsString(isolate), NONE);
168 // 6. Return options.
169 return result;
170}
171
173 switch (style()) {
174 case Style::LONG:
175 return isolate->factory()->long_string();
176 case Style::SHORT:
177 return isolate->factory()->short_string();
178 case Style::NARROW:
179 return isolate->factory()->narrow_string();
180 }
181 UNREACHABLE();
182}
183
185 switch (type()) {
187 return isolate->factory()->conjunction_string();
189 return isolate->factory()->disjunction_string();
190 case Type::UNIT:
191 return isolate->factory()->unit_string();
192 }
193 UNREACHABLE();
194}
195
196namespace {
197
198// Extract String from FixedArray into array of UnicodeString
199Maybe<std::vector<icu::UnicodeString>> ToUnicodeStringArray(
200 Isolate* isolate, DirectHandle<FixedArray> array) {
201 int length = array->length();
202 std::vector<icu::UnicodeString> result;
203 for (int i = 0; i < length; i++) {
204 Handle<Object> item(array->get(i), isolate);
205 DCHECK(IsString(*item));
206 Handle<String> item_str = Cast<String>(item);
207 if (!item_str->IsFlat()) item_str = String::Flatten(isolate, item_str);
208 result.push_back(Intl::ToICUUnicodeString(isolate, item_str));
209 }
210 return Just(result);
211}
212
213template <typename T>
214MaybeDirectHandle<T> FormatListCommon(
215 Isolate* isolate, DirectHandle<JSListFormat> format,
216 DirectHandle<FixedArray> list,
217 const std::function<MaybeDirectHandle<T>(
218 Isolate*, const icu::FormattedValue&)>& formatToResult) {
219 DCHECK(!IsUndefined(*list));
220 Maybe<std::vector<icu::UnicodeString>> maybe_array =
221 ToUnicodeStringArray(isolate, list);
222 MAYBE_RETURN(maybe_array, DirectHandle<T>());
223 std::vector<icu::UnicodeString> array = maybe_array.FromJust();
224
225 icu::ListFormatter* formatter = format->icu_formatter()->raw();
226 DCHECK_NOT_NULL(formatter);
227
228 UErrorCode status = U_ZERO_ERROR;
229 icu::FormattedList formatted = formatter->formatStringsToValue(
230 array.data(), static_cast<int32_t>(array.size()), status);
231 if (U_FAILURE(status)) {
232 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError));
233 }
234 return formatToResult(isolate, formatted);
235}
236
237DirectHandle<String> IcuFieldIdToType(Isolate* isolate, int32_t field_id) {
238 switch (field_id) {
239 case ULISTFMT_LITERAL_FIELD:
240 return isolate->factory()->literal_string();
241 case ULISTFMT_ELEMENT_FIELD:
242 return isolate->factory()->element_string();
243 default:
244 UNREACHABLE();
245 }
246}
247
248// A helper function to convert the FormattedList to a
249// MaybeHandle<JSArray> for the implementation of formatToParts.
250MaybeDirectHandle<JSArray> FormattedListToJSArray(
251 Isolate* isolate, const icu::FormattedValue& formatted) {
252 DirectHandle<JSArray> array = isolate->factory()->NewJSArray(0);
253 icu::ConstrainedFieldPosition cfpos;
254 cfpos.constrainCategory(UFIELD_CATEGORY_LIST);
255 int index = 0;
256 UErrorCode status = U_ZERO_ERROR;
257 icu::UnicodeString string = formatted.toString(status);
258 DirectHandle<String> substring;
259 while (formatted.nextPosition(cfpos, status) && U_SUCCESS(status)) {
261 isolate, substring,
262 Intl::ToString(isolate, string, cfpos.getStart(), cfpos.getLimit()));
263 Intl::AddElement(isolate, array, index++,
264 IcuFieldIdToType(isolate, cfpos.getField()), substring);
265 }
266 if (U_FAILURE(status)) {
267 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError));
268 }
270 return array;
271}
272
273} // namespace
274
275// ecma402 #sec-formatlist
277 Isolate* isolate, DirectHandle<JSListFormat> format,
279 return FormatListCommon<String>(isolate, format, list,
281}
282
283// ecma42 #sec-formatlisttoparts
285 Isolate* isolate, DirectHandle<JSListFormat> format,
287 return FormatListCommon<JSArray>(isolate, format, list,
289}
290
291namespace {
292
293struct CheckListPattern {
294 static const char* key() { return "listPattern"; }
295 static const char* path() { return nullptr; }
296};
297
298} // namespace
299
300const std::set<std::string>& JSListFormat::GetAvailableLocales() {
302 available_locales = LAZY_INSTANCE_INITIALIZER;
303 return available_locales.Pointer()->Get();
304}
305
306} // namespace internal
307} // namespace v8
V8_INLINE T FromJust() const &
Definition v8-maybe.h:64
V8_INLINE bool IsNothing() const
Definition v8-maybe.h:35
Handle< JSObject > NewJSObject(DirectHandle< JSFunction > constructor, AllocationType allocation=AllocationType::kYoung, NewJSObjectType=NewJSObjectType::kNoAPIWrapper)
Definition factory.cc:2985
static void AddElement(Isolate *isolate, DirectHandle< JSArray > array, int index, DirectHandle< String > field_type_string, DirectHandle< String > value)
static V8_WARN_UNUSED_RESULT MaybeHandle< String > ToString(Isolate *isolate, const icu::UnicodeString &string)
static Maybe< std::vector< std::string > > CanonicalizeLocaleList(Isolate *isolate, DirectHandle< Object > locales, bool only_return_one_result=false)
static Maybe< ResolvedLocale > ResolveLocale(Isolate *isolate, const std::set< std::string > &available_locales, const std::vector< std::string > &requested_locales, MatcherOption options, const std::set< std::string > &relevant_extension_keys)
static icu::UnicodeString ToICUUnicodeString(Isolate *isolate, DirectHandle< String > string, int offset=0)
static V8_WARN_UNUSED_RESULT Maybe< MatcherOption > GetLocaleMatcher(Isolate *isolate, DirectHandle< JSReceiver > options, const char *method_name)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< String > FormattedToString(Isolate *isolate, const icu::FormattedValue &formatted)
static DirectHandle< JSObject > ResolvedOptions(Isolate *isolate, DirectHandle< JSListFormat > format_holder)
Handle< String > StyleAsString(Isolate *isolate) const
static MaybeDirectHandle< JSListFormat > New(Isolate *isolate, DirectHandle< Map > map, DirectHandle< Object > locales, DirectHandle< Object > options)
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< String > FormatList(Isolate *isolate, DirectHandle< JSListFormat > format_holder, DirectHandle< FixedArray > list)
Handle< String > TypeAsString(Isolate *isolate) const
static V8_EXPORT_PRIVATE const std::set< std::string > & GetAvailableLocales()
static V8_WARN_UNUSED_RESULT MaybeDirectHandle< JSArray > FormatListToParts(Isolate *isolate, DirectHandle< JSListFormat > format_holder, DirectHandle< FixedArray > list)
static V8_EXPORT_PRIVATE void AddProperty(Isolate *isolate, DirectHandle< JSObject > object, DirectHandle< Name > name, DirectHandle< Object > value, PropertyAttributes attributes)
static void ValidateElements(Tagged< JSObject > object)
static DirectHandle< Managed< CppType > > From(Isolate *isolate, size_t estimated_size, std::shared_ptr< CppType > shared_ptr, AllocationType allocation_type=AllocationType::kYoung)
Definition managed-inl.h:27
static V8_INLINE HandleType< String > Flatten(Isolate *isolate, HandleType< T > string, AllocationType allocation=AllocationType::kYoung)
void push_back(const T &value)
#define ASSIGN_RETURN_ON_EXCEPTION(isolate, dst, call)
Definition isolate.h:291
#define THROW_NEW_ERROR(isolate, call)
Definition isolate.h:307
#define MAYBE_RETURN(call, value)
Definition isolate.h:408
icu::number::FormattedNumber formatted
JSDurationFormat::FieldStyle style
DirectHandle< JSReceiver > options
ZoneVector< RpoNumber > & result
#define LAZY_INSTANCE_INITIALIZER
int r
Definition mul-fft.cc:298
MaybeDirectHandle< JSReceiver > GetOptionsObject(Isolate *isolate, DirectHandle< Object > options, const char *method_name)
MaybeDirectHandle< JSArray > FormattedListToJSArray(Isolate *isolate, const icu::FormattedValue &formatted, const std::vector< std::vector< Part > > *parts, JSDurationFormat::Separator separator)
Maybe< bool > GetStringOption(Isolate *isolate, DirectHandle< JSReceiver > options, const char *property, const std::vector< const char * > &values, const char *method_name, std::unique_ptr< char[]> *result)
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
Maybe< T > Just(const T &t)
Definition v8-maybe.h:117
uint32_t substring
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK(condition)
Definition logging.h:482