Line data Source code
1 : // Copyright 2013 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 :
9 : #include "src/objects/intl-objects.h"
10 :
11 : #include <memory>
12 :
13 : #include "src/api.h"
14 : #include "src/factory.h"
15 : #include "src/isolate.h"
16 : #include "src/objects-inl.h"
17 : #include "src/objects/intl-objects-inl.h"
18 :
19 : #include "unicode/brkiter.h"
20 : #include "unicode/bytestream.h"
21 : #include "unicode/calendar.h"
22 : #include "unicode/coll.h"
23 : #include "unicode/curramt.h"
24 : #include "unicode/dcfmtsym.h"
25 : #include "unicode/decimfmt.h"
26 : #include "unicode/dtfmtsym.h"
27 : #include "unicode/dtptngen.h"
28 : #include "unicode/gregocal.h"
29 : #include "unicode/locid.h"
30 : #include "unicode/numfmt.h"
31 : #include "unicode/numsys.h"
32 : #include "unicode/rbbi.h"
33 : #include "unicode/smpdtfmt.h"
34 : #include "unicode/timezone.h"
35 : #include "unicode/uchar.h"
36 : #include "unicode/ucol.h"
37 : #include "unicode/ucurr.h"
38 : #include "unicode/unum.h"
39 : #include "unicode/uvernum.h"
40 : #include "unicode/uversion.h"
41 :
42 : #if U_ICU_VERSION_MAJOR_NUM >= 59
43 : #include "unicode/char16ptr.h"
44 : #endif
45 :
46 : namespace v8 {
47 : namespace internal {
48 :
49 : namespace {
50 :
51 33564 : bool ExtractStringSetting(Isolate* isolate, Handle<JSObject> options,
52 : const char* key, icu::UnicodeString* setting) {
53 33564 : Handle<String> str = isolate->factory()->NewStringFromAsciiChecked(key);
54 : Handle<Object> object =
55 67128 : JSReceiver::GetProperty(options, str).ToHandleChecked();
56 33564 : if (object->IsString()) {
57 : v8::String::Utf8Value utf8_string(
58 18012 : v8::Utils::ToLocal(Handle<String>::cast(object)));
59 36024 : *setting = icu::UnicodeString::fromUTF8(*utf8_string);
60 18012 : return true;
61 : }
62 : return false;
63 : }
64 :
65 3670 : bool ExtractIntegerSetting(Isolate* isolate, Handle<JSObject> options,
66 : const char* key, int32_t* value) {
67 3670 : Handle<String> str = isolate->factory()->NewStringFromAsciiChecked(key);
68 : Handle<Object> object =
69 7340 : JSReceiver::GetProperty(options, str).ToHandleChecked();
70 3670 : if (object->IsNumber()) {
71 2286 : object->ToInt32(value);
72 2286 : return true;
73 : }
74 : return false;
75 : }
76 :
77 30926 : bool ExtractBooleanSetting(Isolate* isolate, Handle<JSObject> options,
78 : const char* key, bool* value) {
79 30926 : Handle<String> str = isolate->factory()->NewStringFromAsciiChecked(key);
80 : Handle<Object> object =
81 61852 : JSReceiver::GetProperty(options, str).ToHandleChecked();
82 30926 : if (object->IsBoolean()) {
83 15830 : *value = object->BooleanValue();
84 15830 : return true;
85 : }
86 : return false;
87 : }
88 :
89 1133 : icu::SimpleDateFormat* CreateICUDateFormat(Isolate* isolate,
90 : const icu::Locale& icu_locale,
91 : Handle<JSObject> options) {
92 : // Create time zone as specified by the user. We have to re-create time zone
93 : // since calendar takes ownership.
94 : icu::TimeZone* tz = NULL;
95 : icu::UnicodeString timezone;
96 1133 : if (ExtractStringSetting(isolate, options, "timeZone", &timezone)) {
97 355 : tz = icu::TimeZone::createTimeZone(timezone);
98 : } else {
99 778 : tz = icu::TimeZone::createDefault();
100 : }
101 :
102 : // Create a calendar using locale, and apply time zone to it.
103 1133 : UErrorCode status = U_ZERO_ERROR;
104 : icu::Calendar* calendar =
105 1133 : icu::Calendar::createInstance(tz, icu_locale, status);
106 :
107 2266 : if (calendar->getDynamicClassID() ==
108 1133 : icu::GregorianCalendar::getStaticClassID()) {
109 : icu::GregorianCalendar* gc = (icu::GregorianCalendar*)calendar;
110 1049 : UErrorCode status = U_ZERO_ERROR;
111 : // The beginning of ECMAScript time, namely -(2**53)
112 : const double start_of_time = -9007199254740992;
113 1049 : gc->setGregorianChange(start_of_time, status);
114 : DCHECK(U_SUCCESS(status));
115 : }
116 :
117 : // Make formatter from skeleton. Calendar and numbering system are added
118 : // to the locale as Unicode extension (if they were specified at all).
119 : icu::SimpleDateFormat* date_format = NULL;
120 1133 : icu::UnicodeString skeleton;
121 1133 : if (ExtractStringSetting(isolate, options, "skeleton", &skeleton)) {
122 : std::unique_ptr<icu::DateTimePatternGenerator> generator(
123 1133 : icu::DateTimePatternGenerator::createInstance(icu_locale, status));
124 1133 : icu::UnicodeString pattern;
125 1133 : if (U_SUCCESS(status))
126 2266 : pattern = generator->getBestPattern(skeleton, status);
127 :
128 1133 : date_format = new icu::SimpleDateFormat(pattern, icu_locale, status);
129 1133 : if (U_SUCCESS(status)) {
130 1133 : date_format->adoptCalendar(calendar);
131 : }
132 : }
133 :
134 1133 : if (U_FAILURE(status)) {
135 0 : delete calendar;
136 0 : delete date_format;
137 : date_format = nullptr;
138 : }
139 :
140 1133 : return date_format;
141 : }
142 :
143 2266 : void SetResolvedDateSettings(Isolate* isolate, const icu::Locale& icu_locale,
144 : icu::SimpleDateFormat* date_format,
145 : Handle<JSObject> resolved) {
146 : Factory* factory = isolate->factory();
147 1133 : UErrorCode status = U_ZERO_ERROR;
148 : icu::UnicodeString pattern;
149 1133 : date_format->toPattern(pattern);
150 : JSObject::SetProperty(
151 : resolved, factory->intl_pattern_symbol(),
152 : factory
153 : ->NewStringFromTwoByte(Vector<const uint16_t>(
154 : reinterpret_cast<const uint16_t*>(pattern.getBuffer()),
155 : pattern.length()))
156 : .ToHandleChecked(),
157 : SLOPPY)
158 2266 : .Assert();
159 :
160 : // Set time zone and calendar.
161 1133 : const icu::Calendar* calendar = date_format->getCalendar();
162 : // getType() returns legacy calendar type name instead of LDML/BCP47 calendar
163 : // key values. intl.js maps them to BCP47 values for key "ca".
164 : // TODO(jshin): Consider doing it here, instead.
165 1133 : const char* calendar_name = calendar->getType();
166 : JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("calendar"),
167 : factory->NewStringFromAsciiChecked(calendar_name),
168 : SLOPPY)
169 3399 : .Assert();
170 :
171 1133 : const icu::TimeZone& tz = calendar->getTimeZone();
172 1133 : icu::UnicodeString time_zone;
173 : tz.getID(time_zone);
174 :
175 1133 : icu::UnicodeString canonical_time_zone;
176 1133 : icu::TimeZone::getCanonicalID(time_zone, canonical_time_zone, status);
177 1133 : if (U_SUCCESS(status)) {
178 1133 : if (canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/GMT")) {
179 : JSObject::SetProperty(resolved,
180 : factory->NewStringFromStaticChars("timeZone"),
181 : factory->NewStringFromStaticChars("UTC"), SLOPPY)
182 0 : .Assert();
183 : } else {
184 : JSObject::SetProperty(resolved,
185 : factory->NewStringFromStaticChars("timeZone"),
186 : factory
187 : ->NewStringFromTwoByte(Vector<const uint16_t>(
188 : reinterpret_cast<const uint16_t*>(
189 : canonical_time_zone.getBuffer()),
190 : canonical_time_zone.length()))
191 : .ToHandleChecked(),
192 : SLOPPY)
193 3399 : .Assert();
194 : }
195 : }
196 :
197 : // Ugly hack. ICU doesn't expose numbering system in any way, so we have
198 : // to assume that for given locale NumberingSystem constructor produces the
199 : // same digits as NumberFormat/Calendar would.
200 1133 : status = U_ZERO_ERROR;
201 : icu::NumberingSystem* numbering_system =
202 1133 : icu::NumberingSystem::createInstance(icu_locale, status);
203 1133 : if (U_SUCCESS(status)) {
204 1133 : const char* ns = numbering_system->getName();
205 : JSObject::SetProperty(resolved,
206 : factory->NewStringFromStaticChars("numberingSystem"),
207 : factory->NewStringFromAsciiChecked(ns), SLOPPY)
208 3399 : .Assert();
209 : } else {
210 : JSObject::SetProperty(resolved,
211 : factory->NewStringFromStaticChars("numberingSystem"),
212 : factory->undefined_value(), SLOPPY)
213 0 : .Assert();
214 : }
215 1133 : delete numbering_system;
216 :
217 : // Set the locale
218 : char result[ULOC_FULLNAME_CAPACITY];
219 1133 : status = U_ZERO_ERROR;
220 : uloc_toLanguageTag(icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY,
221 1133 : FALSE, &status);
222 1133 : if (U_SUCCESS(status)) {
223 : JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
224 : factory->NewStringFromAsciiChecked(result), SLOPPY)
225 3399 : .Assert();
226 : } else {
227 : // This would never happen, since we got the locale from ICU.
228 : JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
229 : factory->NewStringFromStaticChars("und"), SLOPPY)
230 0 : .Assert();
231 1133 : }
232 1133 : }
233 :
234 734 : icu::DecimalFormat* CreateICUNumberFormat(Isolate* isolate,
235 : const icu::Locale& icu_locale,
236 : Handle<JSObject> options) {
237 : // Make formatter from options. Numbering system is added
238 : // to the locale as Unicode extension (if it was specified at all).
239 734 : UErrorCode status = U_ZERO_ERROR;
240 : icu::DecimalFormat* number_format = NULL;
241 : icu::UnicodeString style;
242 734 : icu::UnicodeString currency;
243 734 : if (ExtractStringSetting(isolate, options, "style", &style)) {
244 734 : if (style == UNICODE_STRING_SIMPLE("currency")) {
245 : icu::UnicodeString display;
246 98 : ExtractStringSetting(isolate, options, "currency", ¤cy);
247 98 : ExtractStringSetting(isolate, options, "currencyDisplay", &display);
248 :
249 : #if (U_ICU_VERSION_MAJOR_NUM == 4) && (U_ICU_VERSION_MINOR_NUM <= 6)
250 : icu::NumberFormat::EStyles format_style;
251 : if (display == UNICODE_STRING_SIMPLE("code")) {
252 : format_style = icu::NumberFormat::kIsoCurrencyStyle;
253 : } else if (display == UNICODE_STRING_SIMPLE("name")) {
254 : format_style = icu::NumberFormat::kPluralCurrencyStyle;
255 : } else {
256 : format_style = icu::NumberFormat::kCurrencyStyle;
257 : }
258 : #else // ICU version is 4.8 or above (we ignore versions below 4.0).
259 : UNumberFormatStyle format_style;
260 98 : if (display == UNICODE_STRING_SIMPLE("code")) {
261 : format_style = UNUM_CURRENCY_ISO;
262 84 : } else if (display == UNICODE_STRING_SIMPLE("name")) {
263 : format_style = UNUM_CURRENCY_PLURAL;
264 : } else {
265 : format_style = UNUM_CURRENCY;
266 : }
267 : #endif
268 :
269 : number_format = static_cast<icu::DecimalFormat*>(
270 98 : icu::NumberFormat::createInstance(icu_locale, format_style, status));
271 :
272 98 : if (U_FAILURE(status)) {
273 0 : delete number_format;
274 0 : return NULL;
275 98 : }
276 636 : } else if (style == UNICODE_STRING_SIMPLE("percent")) {
277 : number_format = static_cast<icu::DecimalFormat*>(
278 28 : icu::NumberFormat::createPercentInstance(icu_locale, status));
279 28 : if (U_FAILURE(status)) {
280 0 : delete number_format;
281 : return NULL;
282 : }
283 : // Make sure 1.1% doesn't go into 2%.
284 28 : number_format->setMinimumFractionDigits(1);
285 : } else {
286 : // Make a decimal instance by default.
287 : number_format = static_cast<icu::DecimalFormat*>(
288 608 : icu::NumberFormat::createInstance(icu_locale, status));
289 : }
290 : }
291 :
292 734 : if (U_FAILURE(status)) {
293 0 : delete number_format;
294 : return NULL;
295 : }
296 :
297 : // Set all options.
298 1468 : if (!currency.isEmpty()) {
299 196 : number_format->setCurrency(currency.getBuffer(), status);
300 : }
301 :
302 : int32_t digits;
303 734 : if (ExtractIntegerSetting(isolate, options, "minimumIntegerDigits",
304 : &digits)) {
305 734 : number_format->setMinimumIntegerDigits(digits);
306 : }
307 :
308 734 : if (ExtractIntegerSetting(isolate, options, "minimumFractionDigits",
309 : &digits)) {
310 734 : number_format->setMinimumFractionDigits(digits);
311 : }
312 :
313 734 : if (ExtractIntegerSetting(isolate, options, "maximumFractionDigits",
314 : &digits)) {
315 734 : number_format->setMaximumFractionDigits(digits);
316 : }
317 :
318 : bool significant_digits_used = false;
319 734 : if (ExtractIntegerSetting(isolate, options, "minimumSignificantDigits",
320 : &digits)) {
321 42 : number_format->setMinimumSignificantDigits(digits);
322 : significant_digits_used = true;
323 : }
324 :
325 734 : if (ExtractIntegerSetting(isolate, options, "maximumSignificantDigits",
326 : &digits)) {
327 42 : number_format->setMaximumSignificantDigits(digits);
328 : significant_digits_used = true;
329 : }
330 :
331 734 : number_format->setSignificantDigitsUsed(significant_digits_used);
332 :
333 : bool grouping;
334 734 : if (ExtractBooleanSetting(isolate, options, "useGrouping", &grouping)) {
335 734 : number_format->setGroupingUsed(grouping);
336 : }
337 :
338 : // Set rounding mode.
339 734 : number_format->setRoundingMode(icu::DecimalFormat::kRoundHalfUp);
340 :
341 1468 : return number_format;
342 : }
343 :
344 1468 : void SetResolvedNumberSettings(Isolate* isolate, const icu::Locale& icu_locale,
345 : icu::DecimalFormat* number_format,
346 : Handle<JSObject> resolved) {
347 : Factory* factory = isolate->factory();
348 : icu::UnicodeString pattern;
349 734 : number_format->toPattern(pattern);
350 : JSObject::SetProperty(
351 : resolved, factory->intl_pattern_symbol(),
352 : factory
353 : ->NewStringFromTwoByte(Vector<const uint16_t>(
354 : reinterpret_cast<const uint16_t*>(pattern.getBuffer()),
355 : pattern.length()))
356 : .ToHandleChecked(),
357 : SLOPPY)
358 1468 : .Assert();
359 :
360 : // Set resolved currency code in options.currency if not empty.
361 1468 : icu::UnicodeString currency(number_format->getCurrency());
362 1468 : if (!currency.isEmpty()) {
363 : JSObject::SetProperty(
364 : resolved, factory->NewStringFromStaticChars("currency"),
365 : factory
366 : ->NewStringFromTwoByte(Vector<const uint16_t>(
367 : reinterpret_cast<const uint16_t*>(currency.getBuffer()),
368 : currency.length()))
369 : .ToHandleChecked(),
370 : SLOPPY)
371 294 : .Assert();
372 : }
373 :
374 : // Ugly hack. ICU doesn't expose numbering system in any way, so we have
375 : // to assume that for given locale NumberingSystem constructor produces the
376 : // same digits as NumberFormat/Calendar would.
377 734 : UErrorCode status = U_ZERO_ERROR;
378 : icu::NumberingSystem* numbering_system =
379 734 : icu::NumberingSystem::createInstance(icu_locale, status);
380 734 : if (U_SUCCESS(status)) {
381 734 : const char* ns = numbering_system->getName();
382 : JSObject::SetProperty(resolved,
383 : factory->NewStringFromStaticChars("numberingSystem"),
384 : factory->NewStringFromAsciiChecked(ns), SLOPPY)
385 2202 : .Assert();
386 : } else {
387 : JSObject::SetProperty(resolved,
388 : factory->NewStringFromStaticChars("numberingSystem"),
389 : factory->undefined_value(), SLOPPY)
390 0 : .Assert();
391 : }
392 734 : delete numbering_system;
393 :
394 : JSObject::SetProperty(
395 : resolved, factory->NewStringFromStaticChars("useGrouping"),
396 734 : factory->ToBoolean(number_format->isGroupingUsed()), SLOPPY)
397 1468 : .Assert();
398 :
399 : JSObject::SetProperty(
400 : resolved, factory->NewStringFromStaticChars("minimumIntegerDigits"),
401 : factory->NewNumberFromInt(number_format->getMinimumIntegerDigits()),
402 : SLOPPY)
403 1468 : .Assert();
404 :
405 : JSObject::SetProperty(
406 : resolved, factory->NewStringFromStaticChars("minimumFractionDigits"),
407 : factory->NewNumberFromInt(number_format->getMinimumFractionDigits()),
408 : SLOPPY)
409 1468 : .Assert();
410 :
411 : JSObject::SetProperty(
412 : resolved, factory->NewStringFromStaticChars("maximumFractionDigits"),
413 : factory->NewNumberFromInt(number_format->getMaximumFractionDigits()),
414 : SLOPPY)
415 1468 : .Assert();
416 :
417 : Handle<String> key =
418 734 : factory->NewStringFromStaticChars("minimumSignificantDigits");
419 734 : Maybe<bool> maybe = JSReceiver::HasOwnProperty(resolved, key);
420 734 : CHECK(maybe.IsJust());
421 734 : if (maybe.FromJust()) {
422 : JSObject::SetProperty(
423 : resolved, factory->NewStringFromStaticChars("minimumSignificantDigits"),
424 : factory->NewNumberFromInt(number_format->getMinimumSignificantDigits()),
425 : SLOPPY)
426 84 : .Assert();
427 : }
428 :
429 734 : key = factory->NewStringFromStaticChars("maximumSignificantDigits");
430 734 : maybe = JSReceiver::HasOwnProperty(resolved, key);
431 734 : CHECK(maybe.IsJust());
432 734 : if (maybe.FromJust()) {
433 : JSObject::SetProperty(
434 : resolved, factory->NewStringFromStaticChars("maximumSignificantDigits"),
435 : factory->NewNumberFromInt(number_format->getMaximumSignificantDigits()),
436 : SLOPPY)
437 84 : .Assert();
438 : }
439 :
440 : // Set the locale
441 : char result[ULOC_FULLNAME_CAPACITY];
442 734 : status = U_ZERO_ERROR;
443 : uloc_toLanguageTag(icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY,
444 734 : FALSE, &status);
445 734 : if (U_SUCCESS(status)) {
446 : JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
447 : factory->NewStringFromAsciiChecked(result), SLOPPY)
448 2202 : .Assert();
449 : } else {
450 : // This would never happen, since we got the locale from ICU.
451 : JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
452 : factory->NewStringFromStaticChars("und"), SLOPPY)
453 0 : .Assert();
454 734 : }
455 734 : }
456 :
457 15096 : icu::Collator* CreateICUCollator(Isolate* isolate,
458 : const icu::Locale& icu_locale,
459 : Handle<JSObject> options) {
460 : // Make collator from options.
461 : icu::Collator* collator = NULL;
462 15096 : UErrorCode status = U_ZERO_ERROR;
463 15096 : collator = icu::Collator::createInstance(icu_locale, status);
464 :
465 15096 : if (U_FAILURE(status)) {
466 0 : delete collator;
467 : return NULL;
468 : }
469 :
470 : // Set flags first, and then override them with sensitivity if necessary.
471 : bool numeric;
472 15096 : if (ExtractBooleanSetting(isolate, options, "numeric", &numeric)) {
473 : collator->setAttribute(UCOL_NUMERIC_COLLATION, numeric ? UCOL_ON : UCOL_OFF,
474 0 : status);
475 : }
476 :
477 : // Normalization is always on, by the spec. We are free to optimize
478 : // if the strings are already normalized (but we don't have a way to tell
479 : // that right now).
480 15096 : collator->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status);
481 :
482 : icu::UnicodeString case_first;
483 15096 : if (ExtractStringSetting(isolate, options, "caseFirst", &case_first)) {
484 336 : if (case_first == UNICODE_STRING_SIMPLE("upper")) {
485 336 : collator->setAttribute(UCOL_CASE_FIRST, UCOL_UPPER_FIRST, status);
486 0 : } else if (case_first == UNICODE_STRING_SIMPLE("lower")) {
487 0 : collator->setAttribute(UCOL_CASE_FIRST, UCOL_LOWER_FIRST, status);
488 : } else {
489 : // Default (false/off).
490 0 : collator->setAttribute(UCOL_CASE_FIRST, UCOL_OFF, status);
491 : }
492 : }
493 :
494 15096 : icu::UnicodeString sensitivity;
495 15096 : if (ExtractStringSetting(isolate, options, "sensitivity", &sensitivity)) {
496 15082 : if (sensitivity == UNICODE_STRING_SIMPLE("base")) {
497 0 : collator->setStrength(icu::Collator::PRIMARY);
498 15082 : } else if (sensitivity == UNICODE_STRING_SIMPLE("accent")) {
499 0 : collator->setStrength(icu::Collator::SECONDARY);
500 15082 : } else if (sensitivity == UNICODE_STRING_SIMPLE("case")) {
501 0 : collator->setStrength(icu::Collator::PRIMARY);
502 0 : collator->setAttribute(UCOL_CASE_LEVEL, UCOL_ON, status);
503 : } else {
504 : // variant (default)
505 15082 : collator->setStrength(icu::Collator::TERTIARY);
506 : }
507 : }
508 :
509 : bool ignore;
510 15096 : if (ExtractBooleanSetting(isolate, options, "ignorePunctuation", &ignore)) {
511 15096 : if (ignore) {
512 0 : collator->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_SHIFTED, status);
513 : }
514 : }
515 :
516 15096 : return collator;
517 : }
518 :
519 15096 : void SetResolvedCollatorSettings(Isolate* isolate,
520 15096 : const icu::Locale& icu_locale,
521 : icu::Collator* collator,
522 : Handle<JSObject> resolved) {
523 : Factory* factory = isolate->factory();
524 15096 : UErrorCode status = U_ZERO_ERROR;
525 :
526 : JSObject::SetProperty(
527 : resolved, factory->NewStringFromStaticChars("numeric"),
528 : factory->ToBoolean(
529 15096 : collator->getAttribute(UCOL_NUMERIC_COLLATION, status) == UCOL_ON),
530 : SLOPPY)
531 30192 : .Assert();
532 :
533 15096 : switch (collator->getAttribute(UCOL_CASE_FIRST, status)) {
534 : case UCOL_LOWER_FIRST:
535 : JSObject::SetProperty(resolved,
536 : factory->NewStringFromStaticChars("caseFirst"),
537 : factory->NewStringFromStaticChars("lower"), SLOPPY)
538 0 : .Assert();
539 0 : break;
540 : case UCOL_UPPER_FIRST:
541 : JSObject::SetProperty(resolved,
542 : factory->NewStringFromStaticChars("caseFirst"),
543 : factory->NewStringFromStaticChars("upper"), SLOPPY)
544 1008 : .Assert();
545 336 : break;
546 : default:
547 : JSObject::SetProperty(resolved,
548 : factory->NewStringFromStaticChars("caseFirst"),
549 : factory->NewStringFromStaticChars("false"), SLOPPY)
550 44280 : .Assert();
551 : }
552 :
553 15096 : switch (collator->getAttribute(UCOL_STRENGTH, status)) {
554 : case UCOL_PRIMARY: {
555 : JSObject::SetProperty(
556 : resolved, factory->NewStringFromStaticChars("strength"),
557 : factory->NewStringFromStaticChars("primary"), SLOPPY)
558 0 : .Assert();
559 :
560 : // case level: true + s1 -> case, s1 -> base.
561 0 : if (UCOL_ON == collator->getAttribute(UCOL_CASE_LEVEL, status)) {
562 : JSObject::SetProperty(resolved,
563 : factory->NewStringFromStaticChars("sensitivity"),
564 : factory->NewStringFromStaticChars("case"), SLOPPY)
565 0 : .Assert();
566 : } else {
567 : JSObject::SetProperty(resolved,
568 : factory->NewStringFromStaticChars("sensitivity"),
569 : factory->NewStringFromStaticChars("base"), SLOPPY)
570 0 : .Assert();
571 : }
572 : break;
573 : }
574 : case UCOL_SECONDARY:
575 : JSObject::SetProperty(
576 : resolved, factory->NewStringFromStaticChars("strength"),
577 : factory->NewStringFromStaticChars("secondary"), SLOPPY)
578 0 : .Assert();
579 : JSObject::SetProperty(resolved,
580 : factory->NewStringFromStaticChars("sensitivity"),
581 : factory->NewStringFromStaticChars("accent"), SLOPPY)
582 0 : .Assert();
583 0 : break;
584 : case UCOL_TERTIARY:
585 : JSObject::SetProperty(
586 : resolved, factory->NewStringFromStaticChars("strength"),
587 : factory->NewStringFromStaticChars("tertiary"), SLOPPY)
588 45288 : .Assert();
589 : JSObject::SetProperty(
590 : resolved, factory->NewStringFromStaticChars("sensitivity"),
591 : factory->NewStringFromStaticChars("variant"), SLOPPY)
592 45288 : .Assert();
593 15096 : break;
594 : case UCOL_QUATERNARY:
595 : // We shouldn't get quaternary and identical from ICU, but if we do
596 : // put them into variant.
597 : JSObject::SetProperty(
598 : resolved, factory->NewStringFromStaticChars("strength"),
599 : factory->NewStringFromStaticChars("quaternary"), SLOPPY)
600 0 : .Assert();
601 : JSObject::SetProperty(
602 : resolved, factory->NewStringFromStaticChars("sensitivity"),
603 : factory->NewStringFromStaticChars("variant"), SLOPPY)
604 0 : .Assert();
605 0 : break;
606 : default:
607 : JSObject::SetProperty(
608 : resolved, factory->NewStringFromStaticChars("strength"),
609 : factory->NewStringFromStaticChars("identical"), SLOPPY)
610 0 : .Assert();
611 : JSObject::SetProperty(
612 : resolved, factory->NewStringFromStaticChars("sensitivity"),
613 : factory->NewStringFromStaticChars("variant"), SLOPPY)
614 0 : .Assert();
615 : }
616 :
617 : JSObject::SetProperty(
618 : resolved, factory->NewStringFromStaticChars("ignorePunctuation"),
619 : factory->ToBoolean(collator->getAttribute(UCOL_ALTERNATE_HANDLING,
620 15096 : status) == UCOL_SHIFTED),
621 : SLOPPY)
622 30192 : .Assert();
623 :
624 : // Set the locale
625 : char result[ULOC_FULLNAME_CAPACITY];
626 15096 : status = U_ZERO_ERROR;
627 : uloc_toLanguageTag(icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY,
628 15096 : FALSE, &status);
629 15096 : if (U_SUCCESS(status)) {
630 : JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
631 : factory->NewStringFromAsciiChecked(result), SLOPPY)
632 45288 : .Assert();
633 : } else {
634 : // This would never happen, since we got the locale from ICU.
635 : JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
636 : factory->NewStringFromStaticChars("und"), SLOPPY)
637 0 : .Assert();
638 : }
639 15096 : }
640 :
641 176 : icu::BreakIterator* CreateICUBreakIterator(Isolate* isolate,
642 : const icu::Locale& icu_locale,
643 : Handle<JSObject> options) {
644 176 : UErrorCode status = U_ZERO_ERROR;
645 : icu::BreakIterator* break_iterator = NULL;
646 : icu::UnicodeString type;
647 176 : if (!ExtractStringSetting(isolate, options, "type", &type)) return NULL;
648 :
649 176 : if (type == UNICODE_STRING_SIMPLE("character")) {
650 : break_iterator =
651 0 : icu::BreakIterator::createCharacterInstance(icu_locale, status);
652 176 : } else if (type == UNICODE_STRING_SIMPLE("sentence")) {
653 : break_iterator =
654 0 : icu::BreakIterator::createSentenceInstance(icu_locale, status);
655 176 : } else if (type == UNICODE_STRING_SIMPLE("line")) {
656 0 : break_iterator = icu::BreakIterator::createLineInstance(icu_locale, status);
657 : } else {
658 : // Defualt is word iterator.
659 176 : break_iterator = icu::BreakIterator::createWordInstance(icu_locale, status);
660 : }
661 :
662 176 : if (U_FAILURE(status)) {
663 0 : delete break_iterator;
664 : return NULL;
665 : }
666 :
667 176 : isolate->CountUsage(v8::Isolate::UseCounterFeature::kBreakIterator);
668 :
669 176 : return break_iterator;
670 : }
671 :
672 176 : void SetResolvedBreakIteratorSettings(Isolate* isolate,
673 176 : const icu::Locale& icu_locale,
674 : icu::BreakIterator* break_iterator,
675 : Handle<JSObject> resolved) {
676 : Factory* factory = isolate->factory();
677 176 : UErrorCode status = U_ZERO_ERROR;
678 :
679 : // Set the locale
680 : char result[ULOC_FULLNAME_CAPACITY];
681 : status = U_ZERO_ERROR;
682 : uloc_toLanguageTag(icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY,
683 176 : FALSE, &status);
684 176 : if (U_SUCCESS(status)) {
685 : JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
686 : factory->NewStringFromAsciiChecked(result), SLOPPY)
687 528 : .Assert();
688 : } else {
689 : // This would never happen, since we got the locale from ICU.
690 : JSObject::SetProperty(resolved, factory->NewStringFromStaticChars("locale"),
691 : factory->NewStringFromStaticChars("und"), SLOPPY)
692 0 : .Assert();
693 : }
694 176 : }
695 : } // namespace
696 :
697 : // static
698 1133 : MaybeHandle<JSIntlDateTimeFormat> JSIntlDateTimeFormat::New(
699 1133 : Isolate* isolate, Handle<String> locale, Handle<JSObject> options,
700 : Handle<JSObject> resolved) {
701 : // Convert BCP47 into ICU locale format.
702 1133 : UErrorCode status = U_ZERO_ERROR;
703 1133 : icu::Locale icu_locale;
704 : char icu_result[ULOC_FULLNAME_CAPACITY];
705 1133 : int icu_length = 0;
706 2266 : v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale));
707 1133 : if (bcp47_locale.length() != 0) {
708 1133 : uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
709 1133 : &icu_length, &status);
710 1133 : if (U_FAILURE(status) || icu_length == 0) {
711 0 : FATAL("Locale lookup bug Intl.DateTimeFormat");
712 : }
713 1133 : icu_locale = icu::Locale(icu_result);
714 : }
715 :
716 : icu::SimpleDateFormat* date_format =
717 1133 : CreateICUDateFormat(isolate, icu_locale, options);
718 1133 : if (!date_format) {
719 : // Remove extensions and try again.
720 0 : icu::Locale no_extension_locale(icu_locale.getBaseName());
721 0 : date_format = CreateICUDateFormat(isolate, no_extension_locale, options);
722 :
723 0 : if (!date_format) {
724 0 : FATAL("Failed to create ICU date format, are ICU data files missing?");
725 : }
726 :
727 : // Set resolved settings (pattern, numbering system, calendar).
728 : SetResolvedDateSettings(isolate, no_extension_locale, date_format,
729 0 : resolved);
730 : } else {
731 1133 : SetResolvedDateSettings(isolate, icu_locale, date_format, resolved);
732 : }
733 :
734 : DCHECK_NOT_NULL(date_format);
735 :
736 : Handle<JSFunction> constructor(
737 2266 : isolate->native_context()->intl_date_time_format_function());
738 :
739 : Handle<Object> dtf_object;
740 2266 : ASSIGN_RETURN_ON_EXCEPTION(isolate, dtf_object,
741 : JSObject::New(constructor, constructor),
742 : JSIntlDateTimeFormat);
743 : Handle<JSIntlDateTimeFormat> dtf(
744 : reinterpret_cast<JSIntlDateTimeFormat*>(*dtf_object));
745 :
746 : dtf->set_simple_date_format(date_format);
747 :
748 : // Make object handle weak so we can delete the data format once GC kicks in.
749 : Handle<Object> wrapper = isolate->global_handles()->Create(*dtf);
750 : GlobalHandles::MakeWeak(wrapper.location(), wrapper.location(),
751 : JSIntlDateTimeFormat::Delete,
752 1133 : WeakCallbackType::kFinalizer);
753 :
754 1133 : return dtf;
755 : }
756 :
757 27 : void JSIntlDateTimeFormat::Delete(const v8::WeakCallbackInfo<void>& data) {
758 : Object** wrapper = reinterpret_cast<Object**>(data.GetParameter());
759 27 : JSIntlDateTimeFormat* dtf = reinterpret_cast<JSIntlDateTimeFormat*>(*wrapper);
760 27 : delete dtf->simple_date_format();
761 27 : GlobalHandles::Destroy(wrapper);
762 27 : }
763 :
764 734 : MaybeHandle<JSIntlNumberFormat> JSIntlNumberFormat::New(
765 734 : Isolate* isolate, Handle<String> locale, Handle<JSObject> options,
766 : Handle<JSObject> resolved) {
767 : // Convert BCP47 into ICU locale format.
768 734 : UErrorCode status = U_ZERO_ERROR;
769 734 : icu::Locale icu_locale;
770 : char icu_result[ULOC_FULLNAME_CAPACITY];
771 734 : int icu_length = 0;
772 1468 : v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale));
773 734 : if (bcp47_locale.length() != 0) {
774 734 : uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
775 734 : &icu_length, &status);
776 734 : if (U_FAILURE(status) || icu_length == 0) {
777 0 : FATAL("Locale lookup bug Intl.NumberFormat");
778 : }
779 734 : icu_locale = icu::Locale(icu_result);
780 : }
781 :
782 : icu::DecimalFormat* number_format =
783 734 : CreateICUNumberFormat(isolate, icu_locale, options);
784 734 : if (!number_format) {
785 : // Remove extensions and try again.
786 0 : icu::Locale no_extension_locale(icu_locale.getBaseName());
787 : number_format =
788 0 : CreateICUNumberFormat(isolate, no_extension_locale, options);
789 :
790 0 : if (!number_format) {
791 0 : FATAL("Failed to create ICU number format, are ICU data files missing?");
792 : }
793 :
794 : // Set resolved settings (pattern, numbering system).
795 : SetResolvedNumberSettings(isolate, no_extension_locale, number_format,
796 0 : resolved);
797 : } else {
798 734 : SetResolvedNumberSettings(isolate, icu_locale, number_format, resolved);
799 : }
800 :
801 : DCHECK_NOT_NULL(number_format);
802 :
803 : Handle<JSFunction> constructor(
804 1468 : isolate->native_context()->intl_number_format_function());
805 :
806 : Handle<Object> nf_object;
807 1468 : ASSIGN_RETURN_ON_EXCEPTION(isolate, nf_object,
808 : JSObject::New(constructor, constructor),
809 : JSIntlNumberFormat);
810 : Handle<JSIntlNumberFormat> nf(
811 : reinterpret_cast<JSIntlNumberFormat*>(*nf_object));
812 :
813 : nf->set_decimal_format(number_format);
814 :
815 : // Make object handle weak so we can delete the data format once GC kicks in.
816 : Handle<Object> wrapper = isolate->global_handles()->Create(*nf);
817 : GlobalHandles::MakeWeak(wrapper.location(), wrapper.location(),
818 : JSIntlNumberFormat::Delete,
819 734 : WeakCallbackType::kFinalizer);
820 :
821 734 : return nf;
822 : }
823 :
824 6 : void JSIntlNumberFormat::Delete(const v8::WeakCallbackInfo<void>& data) {
825 : Object** wrapper = reinterpret_cast<Object**>(data.GetParameter());
826 6 : JSIntlNumberFormat* nf = reinterpret_cast<JSIntlNumberFormat*>(*wrapper);
827 6 : delete nf->decimal_format();
828 6 : GlobalHandles::Destroy(wrapper);
829 6 : }
830 :
831 30192 : MaybeHandle<JSIntlCollator> JSIntlCollator::New(Isolate* isolate,
832 : Handle<String> locale,
833 : Handle<JSObject> options,
834 : Handle<JSObject> resolved) {
835 : // Convert BCP47 into ICU locale format.
836 15096 : UErrorCode status = U_ZERO_ERROR;
837 15096 : icu::Locale icu_locale;
838 : char icu_result[ULOC_FULLNAME_CAPACITY];
839 15096 : int icu_length = 0;
840 30192 : v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale));
841 15096 : if (bcp47_locale.length() != 0) {
842 15096 : uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
843 15096 : &icu_length, &status);
844 15096 : if (U_FAILURE(status) || icu_length == 0) {
845 0 : FATAL("Locale lookup bug Intl.Collator");
846 : }
847 15096 : icu_locale = icu::Locale(icu_result);
848 : }
849 :
850 15096 : icu::Collator* collator = CreateICUCollator(isolate, icu_locale, options);
851 15096 : if (!collator) {
852 : // Remove extensions and try again.
853 0 : icu::Locale no_extension_locale(icu_locale.getBaseName());
854 0 : collator = CreateICUCollator(isolate, no_extension_locale, options);
855 :
856 0 : if (!collator) {
857 0 : FATAL("Failed to create ICU collator, are ICU data files missing?");
858 : }
859 :
860 : // Set resolved settings (pattern, numbering system).
861 : SetResolvedCollatorSettings(isolate, no_extension_locale, collator,
862 0 : resolved);
863 : } else {
864 15096 : SetResolvedCollatorSettings(isolate, icu_locale, collator, resolved);
865 : }
866 :
867 : DCHECK_NOT_NULL(collator);
868 :
869 : Handle<JSFunction> constructor(
870 30192 : isolate->native_context()->intl_collator_function());
871 :
872 : Handle<Object> coll_object;
873 30192 : ASSIGN_RETURN_ON_EXCEPTION(isolate, coll_object,
874 : JSObject::New(constructor, constructor),
875 : JSIntlCollator);
876 : Handle<JSIntlCollator> coll(reinterpret_cast<JSIntlCollator*>(*coll_object));
877 :
878 : coll->set_collator(collator);
879 :
880 : // Make object handle weak so we can delete the data format once GC kicks in.
881 : Handle<Object> wrapper = isolate->global_handles()->Create(*coll);
882 : GlobalHandles::MakeWeak(wrapper.location(), wrapper.location(),
883 15096 : JSIntlCollator::Delete, WeakCallbackType::kFinalizer);
884 :
885 15096 : return coll;
886 : }
887 :
888 24 : void JSIntlCollator::Delete(const v8::WeakCallbackInfo<void>& data) {
889 : Object** wrapper = reinterpret_cast<Object**>(data.GetParameter());
890 24 : JSIntlCollator* coll = reinterpret_cast<JSIntlCollator*>(*wrapper);
891 24 : delete coll->collator();
892 24 : GlobalHandles::Destroy(wrapper);
893 24 : }
894 :
895 176 : MaybeHandle<JSIntlV8BreakIterator> JSIntlV8BreakIterator::New(
896 176 : Isolate* isolate, Handle<String> locale, Handle<JSObject> options,
897 : Handle<JSObject> resolved) {
898 : // Convert BCP47 into ICU locale format.
899 176 : UErrorCode status = U_ZERO_ERROR;
900 176 : icu::Locale icu_locale;
901 : char icu_result[ULOC_FULLNAME_CAPACITY];
902 176 : int icu_length = 0;
903 352 : v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale));
904 176 : if (bcp47_locale.length() != 0) {
905 176 : uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
906 176 : &icu_length, &status);
907 176 : if (U_FAILURE(status) || icu_length == 0) {
908 0 : FATAL("Locale lookup bug Intl.v8BreakIterator");
909 : }
910 176 : icu_locale = icu::Locale(icu_result);
911 : }
912 :
913 : icu::BreakIterator* break_iterator =
914 176 : CreateICUBreakIterator(isolate, icu_locale, options);
915 176 : if (!break_iterator) {
916 : // Remove extensions and try again.
917 0 : icu::Locale no_extension_locale(icu_locale.getBaseName());
918 : break_iterator =
919 0 : CreateICUBreakIterator(isolate, no_extension_locale, options);
920 :
921 0 : if (!break_iterator) {
922 0 : FATAL("Failed to create ICU break iterator, are ICU data files missing?");
923 : }
924 :
925 : // Set resolved settings (locale).
926 : SetResolvedBreakIteratorSettings(isolate, no_extension_locale,
927 0 : break_iterator, resolved);
928 : } else {
929 : SetResolvedBreakIteratorSettings(isolate, icu_locale, break_iterator,
930 176 : resolved);
931 : }
932 :
933 : DCHECK_NOT_NULL(break_iterator);
934 :
935 : Handle<JSFunction> constructor(
936 352 : isolate->native_context()->intl_v8_break_iterator_function());
937 :
938 : Handle<Object> br_object;
939 352 : ASSIGN_RETURN_ON_EXCEPTION(isolate, br_object,
940 : JSObject::New(constructor, constructor),
941 : JSIntlV8BreakIterator);
942 : Handle<JSIntlV8BreakIterator> br(
943 : reinterpret_cast<JSIntlV8BreakIterator*>(*br_object));
944 :
945 : br->set_break_iterator(break_iterator);
946 : br->set_unicode_string(nullptr);
947 :
948 : // Make object handle weak so we can delete the data format once GC kicks in.
949 : Handle<Object> wrapper = isolate->global_handles()->Create(*br);
950 : GlobalHandles::MakeWeak(wrapper.location(), wrapper.location(),
951 : JSIntlV8BreakIterator::Delete,
952 176 : WeakCallbackType::kFinalizer);
953 :
954 176 : return br;
955 : }
956 :
957 8 : void JSIntlV8BreakIterator::Delete(const v8::WeakCallbackInfo<void>& data) {
958 : Object** wrapper = reinterpret_cast<Object**>(data.GetParameter());
959 : JSIntlV8BreakIterator* br =
960 8 : reinterpret_cast<JSIntlV8BreakIterator*>(*wrapper);
961 8 : delete br->break_iterator();
962 8 : delete br->unicode_string();
963 8 : GlobalHandles::Destroy(wrapper);
964 8 : }
965 :
966 : } // namespace internal
967 : } // namespace v8
|