1 // Copyright 2017 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 <cmath>
10 #include <list>
11 #include <memory>
12
13 #include "src/builtins/builtins-intl.h"
14 #include "src/builtins/builtins-utils-inl.h"
15 #include "src/builtins/builtins.h"
16 #include "src/date.h"
17 #include "src/elements.h"
18 #include "src/intl.h"
19 #include "src/objects-inl.h"
20 #include "src/objects/intl-objects.h"
21 #include "src/objects/js-array-inl.h"
22 #include "src/objects/js-collator-inl.h"
23 #include "src/objects/js-list-format-inl.h"
24 #include "src/objects/js-locale-inl.h"
25 #include "src/objects/js-plural-rules-inl.h"
26 #include "src/objects/js-relative-time-format-inl.h"
27
28 #include "unicode/datefmt.h"
29 #include "unicode/decimfmt.h"
30 #include "unicode/fieldpos.h"
31 #include "unicode/fpositer.h"
32 #include "unicode/listformatter.h"
33 #include "unicode/normalizer2.h"
34 #include "unicode/numfmt.h"
35 #include "unicode/reldatefmt.h"
36 #include "unicode/smpdtfmt.h"
37 #include "unicode/udat.h"
38 #include "unicode/ufieldpositer.h"
39 #include "unicode/unistr.h"
40 #include "unicode/ureldatefmt.h"
41 #include "unicode/ustring.h"
42
43 namespace v8 {
44 namespace internal {
45
BUILTIN(StringPrototypeToUpperCaseIntl)46 BUILTIN(StringPrototypeToUpperCaseIntl) {
47 HandleScope scope(isolate);
48 TO_THIS_STRING(string, "String.prototype.toUpperCase");
49 string = String::Flatten(isolate, string);
50 RETURN_RESULT_OR_FAILURE(isolate, ConvertCase(string, true, isolate));
51 }
52
BUILTIN(StringPrototypeNormalizeIntl)53 BUILTIN(StringPrototypeNormalizeIntl) {
54 HandleScope handle_scope(isolate);
55 TO_THIS_STRING(string, "String.prototype.normalize");
56
57 Handle<Object> form_input = args.atOrUndefined(isolate, 1);
58 const char* form_name;
59 UNormalization2Mode form_mode;
60 if (form_input->IsUndefined(isolate)) {
61 // default is FNC
62 form_name = "nfc";
63 form_mode = UNORM2_COMPOSE;
64 } else {
65 Handle<String> form;
66 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, form,
67 Object::ToString(isolate, form_input));
68
69 if (String::Equals(isolate, form, isolate->factory()->NFC_string())) {
70 form_name = "nfc";
71 form_mode = UNORM2_COMPOSE;
72 } else if (String::Equals(isolate, form,
73 isolate->factory()->NFD_string())) {
74 form_name = "nfc";
75 form_mode = UNORM2_DECOMPOSE;
76 } else if (String::Equals(isolate, form,
77 isolate->factory()->NFKC_string())) {
78 form_name = "nfkc";
79 form_mode = UNORM2_COMPOSE;
80 } else if (String::Equals(isolate, form,
81 isolate->factory()->NFKD_string())) {
82 form_name = "nfkc";
83 form_mode = UNORM2_DECOMPOSE;
84 } else {
85 Handle<String> valid_forms =
86 isolate->factory()->NewStringFromStaticChars("NFC, NFD, NFKC, NFKD");
87 THROW_NEW_ERROR_RETURN_FAILURE(
88 isolate,
89 NewRangeError(MessageTemplate::kNormalizationForm, valid_forms));
90 }
91 }
92
93 int length = string->length();
94 string = String::Flatten(isolate, string);
95 icu::UnicodeString result;
96 std::unique_ptr<uc16[]> sap;
97 UErrorCode status = U_ZERO_ERROR;
98 {
99 DisallowHeapAllocation no_gc;
100 String::FlatContent flat = string->GetFlatContent();
101 const UChar* src = GetUCharBufferFromFlat(flat, &sap, length);
102 icu::UnicodeString input(false, src, length);
103 // Getting a singleton. Should not free it.
104 const icu::Normalizer2* normalizer =
105 icu::Normalizer2::getInstance(nullptr, form_name, form_mode, status);
106 DCHECK(U_SUCCESS(status));
107 CHECK_NOT_NULL(normalizer);
108 int32_t normalized_prefix_length =
109 normalizer->spanQuickCheckYes(input, status);
110 // Quick return if the input is already normalized.
111 if (length == normalized_prefix_length) return *string;
112 icu::UnicodeString unnormalized =
113 input.tempSubString(normalized_prefix_length);
114 // Read-only alias of the normalized prefix.
115 result.setTo(false, input.getBuffer(), normalized_prefix_length);
116 // copy-on-write; normalize the suffix and append to |result|.
117 normalizer->normalizeSecondAndAppend(result, unnormalized, status);
118 }
119
120 if (U_FAILURE(status)) {
121 THROW_NEW_ERROR_RETURN_FAILURE(isolate,
122 NewTypeError(MessageTemplate::kIcuError));
123 }
124
125 RETURN_RESULT_OR_FAILURE(
126 isolate, isolate->factory()->NewStringFromTwoByte(Vector<const uint16_t>(
127 reinterpret_cast<const uint16_t*>(result.getBuffer()),
128 result.length())));
129 }
130
131 namespace {
132
133 // The list comes from third_party/icu/source/i18n/unicode/unum.h.
134 // They're mapped to NumberFormat part types mentioned throughout
135 // https://tc39.github.io/ecma402/#sec-partitionnumberpattern .
IcuNumberFieldIdToNumberType(int32_t field_id,double number,Isolate * isolate)136 Handle<String> IcuNumberFieldIdToNumberType(int32_t field_id, double number,
137 Isolate* isolate) {
138 switch (static_cast<UNumberFormatFields>(field_id)) {
139 case UNUM_INTEGER_FIELD:
140 if (std::isfinite(number)) return isolate->factory()->integer_string();
141 if (std::isnan(number)) return isolate->factory()->nan_string();
142 return isolate->factory()->infinity_string();
143 case UNUM_FRACTION_FIELD:
144 return isolate->factory()->fraction_string();
145 case UNUM_DECIMAL_SEPARATOR_FIELD:
146 return isolate->factory()->decimal_string();
147 case UNUM_GROUPING_SEPARATOR_FIELD:
148 return isolate->factory()->group_string();
149 case UNUM_CURRENCY_FIELD:
150 return isolate->factory()->currency_string();
151 case UNUM_PERCENT_FIELD:
152 return isolate->factory()->percentSign_string();
153 case UNUM_SIGN_FIELD:
154 return number < 0 ? isolate->factory()->minusSign_string()
155 : isolate->factory()->plusSign_string();
156
157 case UNUM_EXPONENT_SYMBOL_FIELD:
158 case UNUM_EXPONENT_SIGN_FIELD:
159 case UNUM_EXPONENT_FIELD:
160 // We should never get these because we're not using any scientific
161 // formatter.
162 UNREACHABLE();
163 return Handle<String>();
164
165 case UNUM_PERMILL_FIELD:
166 // We're not creating any permill formatter, and it's not even clear how
167 // that would be possible with the ICU API.
168 UNREACHABLE();
169 return Handle<String>();
170
171 default:
172 UNREACHABLE();
173 return Handle<String>();
174 }
175 }
176
177 // The list comes from third_party/icu/source/i18n/unicode/udat.h.
178 // They're mapped to DateTimeFormat components listed at
179 // https://tc39.github.io/ecma402/#sec-datetimeformat-abstracts .
180
IcuDateFieldIdToDateType(int32_t field_id,Isolate * isolate)181 Handle<String> IcuDateFieldIdToDateType(int32_t field_id, Isolate* isolate) {
182 switch (field_id) {
183 case -1:
184 return isolate->factory()->literal_string();
185 case UDAT_YEAR_FIELD:
186 case UDAT_EXTENDED_YEAR_FIELD:
187 case UDAT_YEAR_NAME_FIELD:
188 return isolate->factory()->year_string();
189 case UDAT_MONTH_FIELD:
190 case UDAT_STANDALONE_MONTH_FIELD:
191 return isolate->factory()->month_string();
192 case UDAT_DATE_FIELD:
193 return isolate->factory()->day_string();
194 case UDAT_HOUR_OF_DAY1_FIELD:
195 case UDAT_HOUR_OF_DAY0_FIELD:
196 case UDAT_HOUR1_FIELD:
197 case UDAT_HOUR0_FIELD:
198 return isolate->factory()->hour_string();
199 case UDAT_MINUTE_FIELD:
200 return isolate->factory()->minute_string();
201 case UDAT_SECOND_FIELD:
202 return isolate->factory()->second_string();
203 case UDAT_DAY_OF_WEEK_FIELD:
204 case UDAT_DOW_LOCAL_FIELD:
205 case UDAT_STANDALONE_DAY_FIELD:
206 return isolate->factory()->weekday_string();
207 case UDAT_AM_PM_FIELD:
208 return isolate->factory()->dayperiod_string();
209 case UDAT_TIMEZONE_FIELD:
210 case UDAT_TIMEZONE_RFC_FIELD:
211 case UDAT_TIMEZONE_GENERIC_FIELD:
212 case UDAT_TIMEZONE_SPECIAL_FIELD:
213 case UDAT_TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD:
214 case UDAT_TIMEZONE_ISO_FIELD:
215 case UDAT_TIMEZONE_ISO_LOCAL_FIELD:
216 return isolate->factory()->timeZoneName_string();
217 case UDAT_ERA_FIELD:
218 return isolate->factory()->era_string();
219 default:
220 // Other UDAT_*_FIELD's cannot show up because there is no way to specify
221 // them via options of Intl.DateTimeFormat.
222 UNREACHABLE();
223 // To prevent MSVC from issuing C4715 warning.
224 return Handle<String>();
225 }
226 }
227
cmp_NumberFormatSpan(const NumberFormatSpan & a,const NumberFormatSpan & b)228 bool cmp_NumberFormatSpan(const NumberFormatSpan& a,
229 const NumberFormatSpan& b) {
230 // Regions that start earlier should be encountered earlier.
231 if (a.begin_pos < b.begin_pos) return true;
232 if (a.begin_pos > b.begin_pos) return false;
233 // For regions that start in the same place, regions that last longer should
234 // be encountered earlier.
235 if (a.end_pos < b.end_pos) return false;
236 if (a.end_pos > b.end_pos) return true;
237 // For regions that are exactly the same, one of them must be the "literal"
238 // backdrop we added, which has a field_id of -1, so consider higher field_ids
239 // to be later.
240 return a.field_id < b.field_id;
241 }
242
FormatNumberToParts(Isolate * isolate,icu::NumberFormat * fmt,double number)243 MaybeHandle<Object> FormatNumberToParts(Isolate* isolate,
244 icu::NumberFormat* fmt, double number) {
245 Factory* factory = isolate->factory();
246
247 icu::UnicodeString formatted;
248 icu::FieldPositionIterator fp_iter;
249 UErrorCode status = U_ZERO_ERROR;
250 fmt->format(number, formatted, &fp_iter, status);
251 if (U_FAILURE(status)) {
252 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), Object);
253 }
254
255 Handle<JSArray> result = factory->NewJSArray(0);
256 int32_t length = formatted.length();
257 if (length == 0) return result;
258
259 std::vector<NumberFormatSpan> regions;
260 // Add a "literal" backdrop for the entire string. This will be used if no
261 // other region covers some part of the formatted string. It's possible
262 // there's another field with exactly the same begin and end as this backdrop,
263 // in which case the backdrop's field_id of -1 will give it lower priority.
264 regions.push_back(NumberFormatSpan(-1, 0, formatted.length()));
265
266 {
267 icu::FieldPosition fp;
268 while (fp_iter.next(fp)) {
269 regions.push_back(NumberFormatSpan(fp.getField(), fp.getBeginIndex(),
270 fp.getEndIndex()));
271 }
272 }
273
274 std::vector<NumberFormatSpan> parts = FlattenRegionsToParts(®ions);
275
276 int index = 0;
277 for (auto it = parts.begin(); it < parts.end(); it++) {
278 NumberFormatSpan part = *it;
279 Handle<String> field_type_string =
280 part.field_id == -1
281 ? isolate->factory()->literal_string()
282 : IcuNumberFieldIdToNumberType(part.field_id, number, isolate);
283 Handle<String> substring;
284 ASSIGN_RETURN_ON_EXCEPTION(
285 isolate, substring,
286 Intl::ToString(isolate, formatted, part.begin_pos, part.end_pos),
287 Object);
288 Intl::AddElement(isolate, result, index, field_type_string, substring);
289 ++index;
290 }
291 JSObject::ValidateElements(*result);
292
293 return result;
294 }
295
FormatDateToParts(Isolate * isolate,icu::DateFormat * format,double date_value)296 MaybeHandle<Object> FormatDateToParts(Isolate* isolate, icu::DateFormat* format,
297 double date_value) {
298 Factory* factory = isolate->factory();
299
300 icu::UnicodeString formatted;
301 icu::FieldPositionIterator fp_iter;
302 icu::FieldPosition fp;
303 UErrorCode status = U_ZERO_ERROR;
304 format->format(date_value, formatted, &fp_iter, status);
305 if (U_FAILURE(status)) {
306 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), Object);
307 }
308
309 Handle<JSArray> result = factory->NewJSArray(0);
310 int32_t length = formatted.length();
311 if (length == 0) return result;
312
313 int index = 0;
314 int32_t previous_end_pos = 0;
315 Handle<String> substring;
316 while (fp_iter.next(fp)) {
317 int32_t begin_pos = fp.getBeginIndex();
318 int32_t end_pos = fp.getEndIndex();
319
320 if (previous_end_pos < begin_pos) {
321 ASSIGN_RETURN_ON_EXCEPTION(
322 isolate, substring,
323 Intl::ToString(isolate, formatted, previous_end_pos, begin_pos),
324 Object);
325 Intl::AddElement(isolate, result, index,
326 IcuDateFieldIdToDateType(-1, isolate), substring);
327 ++index;
328 }
329 ASSIGN_RETURN_ON_EXCEPTION(
330 isolate, substring,
331 Intl::ToString(isolate, formatted, begin_pos, end_pos), Object);
332 Intl::AddElement(isolate, result, index,
333 IcuDateFieldIdToDateType(fp.getField(), isolate),
334 substring);
335 previous_end_pos = end_pos;
336 ++index;
337 }
338 if (previous_end_pos < length) {
339 ASSIGN_RETURN_ON_EXCEPTION(
340 isolate, substring,
341 Intl::ToString(isolate, formatted, previous_end_pos, length), Object);
342 Intl::AddElement(isolate, result, index,
343 IcuDateFieldIdToDateType(-1, isolate), substring);
344 }
345 JSObject::ValidateElements(*result);
346 return result;
347 }
348
349 } // namespace
350
351 // Flattens a list of possibly-overlapping "regions" to a list of
352 // non-overlapping "parts". At least one of the input regions must span the
353 // entire space of possible indexes. The regions parameter will sorted in-place
354 // according to some criteria; this is done for performance to avoid copying the
355 // input.
FlattenRegionsToParts(std::vector<NumberFormatSpan> * regions)356 std::vector<NumberFormatSpan> FlattenRegionsToParts(
357 std::vector<NumberFormatSpan>* regions) {
358 // The intention of this algorithm is that it's used to translate ICU "fields"
359 // to JavaScript "parts" of a formatted string. Each ICU field and JavaScript
360 // part has an integer field_id, which corresponds to something like "grouping
361 // separator", "fraction", or "percent sign", and has a begin and end
362 // position. Here's a diagram of:
363
364 // var nf = new Intl.NumberFormat(['de'], {style:'currency',currency:'EUR'});
365 // nf.formatToParts(123456.78);
366
367 // : 6
368 // input regions: 0000000211 7
369 // ('-' means -1): ------------
370 // formatted string: "123.456,78 €"
371 // output parts: 0006000211-7
372
373 // To illustrate the requirements of this algorithm, here's a contrived and
374 // convoluted example of inputs and expected outputs:
375
376 // : 4
377 // : 22 33 3
378 // : 11111 22
379 // input regions: 0000000 111
380 // : ------------
381 // formatted string: "abcdefghijkl"
382 // output parts: 0221340--231
383 // (The characters in the formatted string are irrelevant to this function.)
384
385 // We arrange the overlapping input regions like a mountain range where
386 // smaller regions are "on top" of larger regions, and we output a birds-eye
387 // view of the mountains, so that smaller regions take priority over larger
388 // regions.
389 std::sort(regions->begin(), regions->end(), cmp_NumberFormatSpan);
390 std::vector<size_t> overlapping_region_index_stack;
391 // At least one item in regions must be a region spanning the entire string.
392 // Due to the sorting above, the first item in the vector will be one of them.
393 overlapping_region_index_stack.push_back(0);
394 NumberFormatSpan top_region = regions->at(0);
395 size_t region_iterator = 1;
396 int32_t entire_size = top_region.end_pos;
397
398 std::vector<NumberFormatSpan> out_parts;
399
400 // The "climber" is a cursor that advances from left to right climbing "up"
401 // and "down" the mountains. Whenever the climber moves to the right, that
402 // represents an item of output.
403 int32_t climber = 0;
404 while (climber < entire_size) {
405 int32_t next_region_begin_pos;
406 if (region_iterator < regions->size()) {
407 next_region_begin_pos = regions->at(region_iterator).begin_pos;
408 } else {
409 // finish off the rest of the input by proceeding to the end.
410 next_region_begin_pos = entire_size;
411 }
412
413 if (climber < next_region_begin_pos) {
414 while (top_region.end_pos < next_region_begin_pos) {
415 if (climber < top_region.end_pos) {
416 // step down
417 out_parts.push_back(NumberFormatSpan(top_region.field_id, climber,
418 top_region.end_pos));
419 climber = top_region.end_pos;
420 } else {
421 // drop down
422 }
423 overlapping_region_index_stack.pop_back();
424 top_region = regions->at(overlapping_region_index_stack.back());
425 }
426 if (climber < next_region_begin_pos) {
427 // cross a plateau/mesa/valley
428 out_parts.push_back(NumberFormatSpan(top_region.field_id, climber,
429 next_region_begin_pos));
430 climber = next_region_begin_pos;
431 }
432 }
433 if (region_iterator < regions->size()) {
434 overlapping_region_index_stack.push_back(region_iterator++);
435 top_region = regions->at(overlapping_region_index_stack.back());
436 }
437 }
438 return out_parts;
439 }
440
BUILTIN(NumberFormatPrototypeFormatToParts)441 BUILTIN(NumberFormatPrototypeFormatToParts) {
442 const char* const method = "Intl.NumberFormat.prototype.formatToParts";
443 HandleScope handle_scope(isolate);
444 CHECK_RECEIVER(JSObject, number_format_holder, method);
445
446 if (!Intl::IsObjectOfType(isolate, number_format_holder,
447 Intl::Type::kNumberFormat)) {
448 THROW_NEW_ERROR_RETURN_FAILURE(
449 isolate,
450 NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
451 isolate->factory()->NewStringFromAsciiChecked(method),
452 number_format_holder));
453 }
454
455 Handle<Object> x;
456 if (args.length() >= 2) {
457 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, x,
458 Object::ToNumber(isolate, args.at(1)));
459 } else {
460 x = isolate->factory()->nan_value();
461 }
462
463 icu::DecimalFormat* number_format =
464 NumberFormat::UnpackNumberFormat(number_format_holder);
465 CHECK_NOT_NULL(number_format);
466
467 RETURN_RESULT_OR_FAILURE(
468 isolate, FormatNumberToParts(isolate, number_format, x->Number()));
469 }
470
BUILTIN(DateTimeFormatPrototypeFormatToParts)471 BUILTIN(DateTimeFormatPrototypeFormatToParts) {
472 const char* const method = "Intl.DateTimeFormat.prototype.formatToParts";
473 HandleScope handle_scope(isolate);
474 CHECK_RECEIVER(JSObject, date_format_holder, method);
475 Factory* factory = isolate->factory();
476
477 if (!Intl::IsObjectOfType(isolate, date_format_holder,
478 Intl::Type::kDateTimeFormat)) {
479 THROW_NEW_ERROR_RETURN_FAILURE(
480 isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
481 factory->NewStringFromAsciiChecked(method),
482 date_format_holder));
483 }
484
485 Handle<Object> x = args.atOrUndefined(isolate, 1);
486 if (x->IsUndefined(isolate)) {
487 x = factory->NewNumber(JSDate::CurrentTimeValue(isolate));
488 } else {
489 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, x,
490 Object::ToNumber(isolate, args.at(1)));
491 }
492
493 double date_value = DateCache::TimeClip(x->Number());
494 if (std::isnan(date_value)) {
495 THROW_NEW_ERROR_RETURN_FAILURE(
496 isolate, NewRangeError(MessageTemplate::kInvalidTimeValue));
497 }
498
499 icu::SimpleDateFormat* date_format =
500 DateFormat::UnpackDateFormat(date_format_holder);
501 CHECK_NOT_NULL(date_format);
502
503 RETURN_RESULT_OR_FAILURE(isolate,
504 FormatDateToParts(isolate, date_format, date_value));
505 }
506
BUILTIN(NumberFormatPrototypeFormatNumber)507 BUILTIN(NumberFormatPrototypeFormatNumber) {
508 const char* const method = "get Intl.NumberFormat.prototype.format";
509 HandleScope scope(isolate);
510
511 // 1. Let nf be the this value.
512 // 2. If Type(nf) is not Object, throw a TypeError exception.
513 CHECK_RECEIVER(JSReceiver, receiver, method);
514
515 // 3. Let nf be ? UnwrapNumberFormat(nf).
516 Handle<JSObject> number_format_holder;
517 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
518 isolate, number_format_holder,
519 NumberFormat::Unwrap(isolate, receiver, method));
520
521 DCHECK(Intl::IsObjectOfType(isolate, number_format_holder,
522 Intl::Type::kNumberFormat));
523
524 Handle<Object> bound_format = Handle<Object>(
525 number_format_holder->GetEmbedderField(NumberFormat::kBoundFormatIndex),
526 isolate);
527
528 // 4. If nf.[[BoundFormat]] is undefined, then
529 if (!bound_format->IsUndefined(isolate)) {
530 DCHECK(bound_format->IsJSFunction());
531 // 5. Return nf.[[BoundFormat]].
532 return *bound_format;
533 }
534
535 Handle<NativeContext> native_context(isolate->context()->native_context(),
536 isolate);
537
538 Handle<Context> context = isolate->factory()->NewBuiltinContext(
539 native_context, NumberFormat::ContextSlot::kLength);
540
541 // 4. b. Set F.[[NumberFormat]] to nf.
542 context->set(NumberFormat::ContextSlot::kNumberFormat, *number_format_holder);
543
544 Handle<SharedFunctionInfo> info = Handle<SharedFunctionInfo>(
545 native_context->number_format_internal_format_number_shared_fun(),
546 isolate);
547
548 Handle<Map> map = isolate->strict_function_without_prototype_map();
549
550 // 4. a. Let F be a new built-in function object as defined in
551 // Number Format Functions (11.1.4).
552 Handle<JSFunction> new_bound_format_function =
553 isolate->factory()->NewFunctionFromSharedFunctionInfo(map, info, context);
554
555 // 4. c. Set nf.[[BoundFormat]] to F.
556 number_format_holder->SetEmbedderField(NumberFormat::kBoundFormatIndex,
557 *new_bound_format_function);
558
559 // 5. Return nf.[[BoundFormat]].
560 return *new_bound_format_function;
561 }
562
BUILTIN(NumberFormatInternalFormatNumber)563 BUILTIN(NumberFormatInternalFormatNumber) {
564 HandleScope scope(isolate);
565
566 Handle<Context> context = Handle<Context>(isolate->context(), isolate);
567
568 // 1. Let nf be F.[[NumberFormat]].
569 Handle<JSObject> number_format_holder = Handle<JSObject>(
570 JSObject::cast(context->get(NumberFormat::ContextSlot::kNumberFormat)),
571 isolate);
572
573 // 2. Assert: Type(nf) is Object and nf has an
574 // [[InitializedNumberFormat]] internal slot.
575 DCHECK(Intl::IsObjectOfType(isolate, number_format_holder,
576 Intl::Type::kNumberFormat));
577
578 // 3. If value is not provided, let value be undefined.
579 Handle<Object> value = args.atOrUndefined(isolate, 1);
580
581 // 4. Let x be ? ToNumber(value).
582 Handle<Object> number_obj;
583 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number_obj,
584 Object::ToNumber(isolate, value));
585
586 // Spec treats -0 as 0.
587 if (number_obj->IsMinusZero()) {
588 number_obj = Handle<Smi>(Smi::kZero, isolate);
589 }
590
591 double number = number_obj->Number();
592 // Return FormatNumber(nf, x).
593 RETURN_RESULT_OR_FAILURE(isolate, NumberFormat::FormatNumber(
594 isolate, number_format_holder, number));
595 }
596
BUILTIN(DateTimeFormatPrototypeFormat)597 BUILTIN(DateTimeFormatPrototypeFormat) {
598 const char* const method = "get Intl.DateTimeFormat.prototype.format";
599 HandleScope scope(isolate);
600
601 // 1. Let dtf be this value.
602 // 2. If Type(dtf) is not Object, throw a TypeError exception.
603 CHECK_RECEIVER(JSReceiver, receiver, method);
604
605 // 3. Let dtf be ? UnwrapDateTimeFormat(dtf).
606 Handle<JSObject> date_format_holder;
607 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
608 isolate, date_format_holder,
609 DateFormat::Unwrap(isolate, receiver, method));
610 DCHECK(Intl::IsObjectOfType(isolate, date_format_holder,
611 Intl::Type::kDateTimeFormat));
612
613 Handle<Object> bound_format = Handle<Object>(
614 date_format_holder->GetEmbedderField(DateFormat::kBoundFormatIndex),
615 isolate);
616
617 // 4. If dtf.[[BoundFormat]] is undefined, then
618 if (!bound_format->IsUndefined(isolate)) {
619 DCHECK(bound_format->IsJSFunction());
620 // 5. Return dtf.[[BoundFormat]].
621 return *bound_format;
622 }
623
624 Handle<NativeContext> native_context(isolate->context()->native_context(),
625 isolate);
626 Handle<Context> context = isolate->factory()->NewBuiltinContext(
627 native_context, DateFormat::ContextSlot::kLength);
628
629 // 4.b. Set F.[[DateTimeFormat]] to dtf.
630 context->set(DateFormat::ContextSlot::kDateFormat, *date_format_holder);
631
632 Handle<SharedFunctionInfo> info = Handle<SharedFunctionInfo>(
633 native_context->date_format_internal_format_shared_fun(), isolate);
634 Handle<Map> map = isolate->strict_function_without_prototype_map();
635
636 // 4.a. Let F be a new built-in function object as defined in DateTime Format
637 // Functions (12.1.5).
638 Handle<JSFunction> new_bound_format_function =
639 isolate->factory()->NewFunctionFromSharedFunctionInfo(map, info, context);
640
641 // 4.c. Set dtf.[[BoundFormat]] to F.
642 date_format_holder->SetEmbedderField(DateFormat::kBoundFormatIndex,
643 *new_bound_format_function);
644
645 // 5. Return dtf.[[BoundFormat]].
646 return *new_bound_format_function;
647 }
648
BUILTIN(DateTimeFormatInternalFormat)649 BUILTIN(DateTimeFormatInternalFormat) {
650 HandleScope scope(isolate);
651 Handle<Context> context = Handle<Context>(isolate->context(), isolate);
652
653 // 1. Let dtf be F.[[DateTimeFormat]].
654 Handle<JSObject> date_format_holder = Handle<JSObject>(
655 JSObject::cast(context->get(DateFormat::ContextSlot::kDateFormat)),
656 isolate);
657
658 // 2. Assert: Type(dtf) is Object and dtf has an [[InitializedDateTimeFormat]]
659 // internal slot.
660 DCHECK(Intl::IsObjectOfType(isolate, date_format_holder,
661 Intl::Type::kDateTimeFormat));
662
663 Handle<Object> date = args.atOrUndefined(isolate, 1);
664
665 RETURN_RESULT_OR_FAILURE(
666 isolate, DateFormat::DateTimeFormat(isolate, date_format_holder, date));
667 }
668
BUILTIN(ListFormatConstructor)669 BUILTIN(ListFormatConstructor) {
670 HandleScope scope(isolate);
671 // 1. If NewTarget is undefined, throw a TypeError exception.
672 if (args.new_target()->IsUndefined(isolate)) { // [[Call]]
673 THROW_NEW_ERROR_RETURN_FAILURE(
674 isolate, NewTypeError(MessageTemplate::kConstructorNotFunction,
675 isolate->factory()->NewStringFromStaticChars(
676 "Intl.ListFormat")));
677 }
678 // [[Construct]]
679 Handle<JSFunction> target = args.target();
680 Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target());
681
682 Handle<JSObject> result;
683 // 2. Let listFormat be OrdinaryCreateFromConstructor(NewTarget,
684 // "%ListFormatPrototype%").
685 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
686 JSObject::New(target, new_target));
687 Handle<JSListFormat> format = Handle<JSListFormat>::cast(result);
688 format->set_flags(0);
689
690 Handle<Object> locales = args.atOrUndefined(isolate, 1);
691 Handle<Object> options = args.atOrUndefined(isolate, 2);
692
693 // 3. Return InitializeListFormat(listFormat, locales, options).
694 RETURN_RESULT_OR_FAILURE(isolate, JSListFormat::InitializeListFormat(
695 isolate, format, locales, options));
696 }
697
BUILTIN(ListFormatPrototypeResolvedOptions)698 BUILTIN(ListFormatPrototypeResolvedOptions) {
699 HandleScope scope(isolate);
700 CHECK_RECEIVER(JSListFormat, format_holder,
701 "Intl.ListFormat.prototype.resolvedOptions");
702 return *JSListFormat::ResolvedOptions(isolate, format_holder);
703 }
704
705 namespace {
706
CreateLocale(Isolate * isolate,Handle<JSFunction> constructor,Handle<JSReceiver> new_target,Handle<Object> tag,Handle<Object> options)707 MaybeHandle<JSLocale> CreateLocale(Isolate* isolate,
708 Handle<JSFunction> constructor,
709 Handle<JSReceiver> new_target,
710 Handle<Object> tag, Handle<Object> options) {
711 Handle<JSObject> result;
712 ASSIGN_RETURN_ON_EXCEPTION(isolate, result,
713 JSObject::New(constructor, new_target), JSLocale);
714
715 // First parameter is a locale, as a string/object. Can't be empty.
716 if (!tag->IsString() && !tag->IsJSReceiver()) {
717 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kLocaleNotEmpty),
718 JSLocale);
719 }
720
721 Handle<String> locale_string;
722 if (tag->IsJSLocale() && Handle<JSLocale>::cast(tag)->locale()->IsString()) {
723 locale_string =
724 Handle<String>(Handle<JSLocale>::cast(tag)->locale(), isolate);
725 } else {
726 ASSIGN_RETURN_ON_EXCEPTION(isolate, locale_string,
727 Object::ToString(isolate, tag), JSLocale);
728 }
729
730 Handle<JSReceiver> options_object;
731 if (options->IsNullOrUndefined(isolate)) {
732 // Make empty options bag.
733 options_object = isolate->factory()->NewJSObjectWithNullProto();
734 } else {
735 ASSIGN_RETURN_ON_EXCEPTION(isolate, options_object,
736 Object::ToObject(isolate, options), JSLocale);
737 }
738
739 return JSLocale::InitializeLocale(isolate, Handle<JSLocale>::cast(result),
740 locale_string, options_object);
741 }
742
743 } // namespace
744
745 // Intl.Locale implementation
BUILTIN(LocaleConstructor)746 BUILTIN(LocaleConstructor) {
747 HandleScope scope(isolate);
748 if (args.new_target()->IsUndefined(isolate)) { // [[Call]]
749 THROW_NEW_ERROR_RETURN_FAILURE(
750 isolate, NewTypeError(MessageTemplate::kConstructorNotFunction,
751 isolate->factory()->NewStringFromAsciiChecked(
752 "Intl.Locale")));
753 }
754 // [[Construct]]
755 Handle<JSFunction> target = args.target();
756 Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target());
757
758 Handle<Object> tag = args.atOrUndefined(isolate, 1);
759 Handle<Object> options = args.atOrUndefined(isolate, 2);
760
761 RETURN_RESULT_OR_FAILURE(
762 isolate, CreateLocale(isolate, target, new_target, tag, options));
763 }
764
BUILTIN(LocalePrototypeMaximize)765 BUILTIN(LocalePrototypeMaximize) {
766 HandleScope scope(isolate);
767 CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.maximize");
768 Handle<JSFunction> constructor(
769 isolate->native_context()->intl_locale_function(), isolate);
770 RETURN_RESULT_OR_FAILURE(
771 isolate,
772 CreateLocale(isolate, constructor, constructor,
773 JSLocale::Maximize(isolate, locale_holder->locale()),
774 isolate->factory()->NewJSObjectWithNullProto()));
775 }
776
BUILTIN(LocalePrototypeMinimize)777 BUILTIN(LocalePrototypeMinimize) {
778 HandleScope scope(isolate);
779 CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.minimize");
780 Handle<JSFunction> constructor(
781 isolate->native_context()->intl_locale_function(), isolate);
782 RETURN_RESULT_OR_FAILURE(
783 isolate,
784 CreateLocale(isolate, constructor, constructor,
785 JSLocale::Minimize(isolate, locale_holder->locale()),
786 isolate->factory()->NewJSObjectWithNullProto()));
787 }
788
789 namespace {
790
GenerateRelativeTimeFormatParts(Isolate * isolate,icu::UnicodeString formatted,icu::UnicodeString integer_part,Handle<String> unit)791 MaybeHandle<JSArray> GenerateRelativeTimeFormatParts(
792 Isolate* isolate, icu::UnicodeString formatted,
793 icu::UnicodeString integer_part, Handle<String> unit) {
794 Factory* factory = isolate->factory();
795 Handle<JSArray> array = factory->NewJSArray(0);
796 int32_t found = formatted.indexOf(integer_part);
797
798 Handle<String> substring;
799 if (found < 0) {
800 // Cannot find the integer_part in the formatted.
801 // Return [{'type': 'literal', 'value': formatted}]
802 ASSIGN_RETURN_ON_EXCEPTION(isolate, substring,
803 Intl::ToString(isolate, formatted), JSArray);
804 Intl::AddElement(isolate, array,
805 0, // index
806 factory->literal_string(), // field_type_string
807 substring);
808 } else {
809 // Found the formatted integer in the result.
810 int index = 0;
811
812 // array.push({
813 // 'type': 'literal',
814 // 'value': formatted.substring(0, found)})
815 if (found > 0) {
816 ASSIGN_RETURN_ON_EXCEPTION(isolate, substring,
817 Intl::ToString(isolate, formatted, 0, found),
818 JSArray);
819 Intl::AddElement(isolate, array, index++,
820 factory->literal_string(), // field_type_string
821 substring);
822 }
823
824 // array.push({
825 // 'type': 'integer',
826 // 'value': formatted.substring(found, found + integer_part.length),
827 // 'unit': unit})
828 ASSIGN_RETURN_ON_EXCEPTION(isolate, substring,
829 Intl::ToString(isolate, formatted, found,
830 found + integer_part.length()),
831 JSArray);
832 Intl::AddElement(isolate, array, index++,
833 factory->integer_string(), // field_type_string
834 substring, factory->unit_string(), unit);
835
836 // array.push({
837 // 'type': 'literal',
838 // 'value': formatted.substring(
839 // found + integer_part.length, formatted.length)})
840 if (found + integer_part.length() < formatted.length()) {
841 ASSIGN_RETURN_ON_EXCEPTION(
842 isolate, substring,
843 Intl::ToString(isolate, formatted, found + integer_part.length(),
844 formatted.length()),
845 JSArray);
846 Intl::AddElement(isolate, array, index,
847 factory->literal_string(), // field_type_string
848 substring);
849 }
850 }
851 return array;
852 }
853
GetURelativeDateTimeUnit(Handle<String> unit,URelativeDateTimeUnit * unit_enum)854 bool GetURelativeDateTimeUnit(Handle<String> unit,
855 URelativeDateTimeUnit* unit_enum) {
856 std::unique_ptr<char[]> unit_str = unit->ToCString();
857 if ((strcmp("second", unit_str.get()) == 0) ||
858 (strcmp("seconds", unit_str.get()) == 0)) {
859 *unit_enum = UDAT_REL_UNIT_SECOND;
860 } else if ((strcmp("minute", unit_str.get()) == 0) ||
861 (strcmp("minutes", unit_str.get()) == 0)) {
862 *unit_enum = UDAT_REL_UNIT_MINUTE;
863 } else if ((strcmp("hour", unit_str.get()) == 0) ||
864 (strcmp("hours", unit_str.get()) == 0)) {
865 *unit_enum = UDAT_REL_UNIT_HOUR;
866 } else if ((strcmp("day", unit_str.get()) == 0) ||
867 (strcmp("days", unit_str.get()) == 0)) {
868 *unit_enum = UDAT_REL_UNIT_DAY;
869 } else if ((strcmp("week", unit_str.get()) == 0) ||
870 (strcmp("weeks", unit_str.get()) == 0)) {
871 *unit_enum = UDAT_REL_UNIT_WEEK;
872 } else if ((strcmp("month", unit_str.get()) == 0) ||
873 (strcmp("months", unit_str.get()) == 0)) {
874 *unit_enum = UDAT_REL_UNIT_MONTH;
875 } else if ((strcmp("quarter", unit_str.get()) == 0) ||
876 (strcmp("quarters", unit_str.get()) == 0)) {
877 *unit_enum = UDAT_REL_UNIT_QUARTER;
878 } else if ((strcmp("year", unit_str.get()) == 0) ||
879 (strcmp("years", unit_str.get()) == 0)) {
880 *unit_enum = UDAT_REL_UNIT_YEAR;
881 } else {
882 return false;
883 }
884 return true;
885 }
886
RelativeTimeFormatPrototypeFormatCommon(BuiltinArguments args,Isolate * isolate,Handle<JSRelativeTimeFormat> format_holder,const char * func_name,bool to_parts)887 MaybeHandle<Object> RelativeTimeFormatPrototypeFormatCommon(
888 BuiltinArguments args, Isolate* isolate,
889 Handle<JSRelativeTimeFormat> format_holder, const char* func_name,
890 bool to_parts) {
891 Factory* factory = isolate->factory();
892 Handle<Object> value_obj = args.atOrUndefined(isolate, 1);
893 Handle<Object> unit_obj = args.atOrUndefined(isolate, 2);
894
895 // 3. Let value be ? ToNumber(value).
896 Handle<Object> value;
897 ASSIGN_RETURN_ON_EXCEPTION(isolate, value,
898 Object::ToNumber(isolate, value_obj), Object);
899 double number = value->Number();
900 // 4. Let unit be ? ToString(unit).
901 Handle<String> unit;
902 ASSIGN_RETURN_ON_EXCEPTION(isolate, unit, Object::ToString(isolate, unit_obj),
903 Object);
904
905 // 4. If isFinite(value) is false, then throw a RangeError exception.
906 if (!std::isfinite(number)) {
907 THROW_NEW_ERROR(
908 isolate,
909 NewRangeError(MessageTemplate::kNotFiniteNumber,
910 isolate->factory()->NewStringFromAsciiChecked(func_name)),
911 Object);
912 }
913
914 icu::RelativeDateTimeFormatter* formatter =
915 JSRelativeTimeFormat::UnpackFormatter(format_holder);
916 CHECK_NOT_NULL(formatter);
917
918 URelativeDateTimeUnit unit_enum;
919 if (!GetURelativeDateTimeUnit(unit, &unit_enum)) {
920 THROW_NEW_ERROR(
921 isolate,
922 NewRangeError(MessageTemplate::kInvalidUnit,
923 isolate->factory()->NewStringFromAsciiChecked(func_name),
924 unit),
925 Object);
926 }
927
928 UErrorCode status = U_ZERO_ERROR;
929 icu::UnicodeString formatted;
930 if (unit_enum == UDAT_REL_UNIT_QUARTER) {
931 // ICU have not yet implement UDAT_REL_UNIT_QUARTER.
932 } else {
933 if (format_holder->numeric() == JSRelativeTimeFormat::Numeric::ALWAYS) {
934 formatter->formatNumeric(number, unit_enum, formatted, status);
935 } else {
936 DCHECK_EQ(JSRelativeTimeFormat::Numeric::AUTO, format_holder->numeric());
937 formatter->format(number, unit_enum, formatted, status);
938 }
939 }
940
941 if (U_FAILURE(status)) {
942 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError), Object);
943 }
944
945 if (to_parts) {
946 icu::UnicodeString integer;
947 icu::FieldPosition pos;
948 formatter->getNumberFormat().format(std::abs(number), integer, pos, status);
949 if (U_FAILURE(status)) {
950 THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kIcuError),
951 Object);
952 }
953
954 Handle<JSArray> elements;
955 ASSIGN_RETURN_ON_EXCEPTION(
956 isolate, elements,
957 GenerateRelativeTimeFormatParts(isolate, formatted, integer, unit),
958 Object);
959 return elements;
960 }
961
962 return factory->NewStringFromTwoByte(Vector<const uint16_t>(
963 reinterpret_cast<const uint16_t*>(formatted.getBuffer()),
964 formatted.length()));
965 }
966
967 } // namespace
968
BUILTIN(RelativeTimeFormatPrototypeFormat)969 BUILTIN(RelativeTimeFormatPrototypeFormat) {
970 HandleScope scope(isolate);
971 // 1. Let relativeTimeFormat be the this value.
972 // 2. If Type(relativeTimeFormat) is not Object or relativeTimeFormat does not
973 // have an [[InitializedRelativeTimeFormat]] internal slot whose value is
974 // true, throw a TypeError exception.
975 CHECK_RECEIVER(JSRelativeTimeFormat, format_holder,
976 "Intl.RelativeTimeFormat.prototype.format");
977 RETURN_RESULT_OR_FAILURE(isolate,
978 RelativeTimeFormatPrototypeFormatCommon(
979 args, isolate, format_holder, "format", false));
980 }
981
BUILTIN(RelativeTimeFormatPrototypeFormatToParts)982 BUILTIN(RelativeTimeFormatPrototypeFormatToParts) {
983 HandleScope scope(isolate);
984 // 1. Let relativeTimeFormat be the this value.
985 // 2. If Type(relativeTimeFormat) is not Object or relativeTimeFormat does not
986 // have an [[InitializedRelativeTimeFormat]] internal slot whose value is
987 // true, throw a TypeError exception.
988 CHECK_RECEIVER(JSRelativeTimeFormat, format_holder,
989 "Intl.RelativeTimeFormat.prototype.formatToParts");
990 RETURN_RESULT_OR_FAILURE(
991 isolate, RelativeTimeFormatPrototypeFormatCommon(
992 args, isolate, format_holder, "formatToParts", true));
993 }
994
995 // Locale getters.
BUILTIN(LocalePrototypeLanguage)996 BUILTIN(LocalePrototypeLanguage) {
997 HandleScope scope(isolate);
998 // CHECK_RECEIVER will case locale_holder to JSLocale.
999 CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.language");
1000
1001 return locale_holder->language();
1002 }
1003
BUILTIN(LocalePrototypeScript)1004 BUILTIN(LocalePrototypeScript) {
1005 HandleScope scope(isolate);
1006 CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.script");
1007
1008 return locale_holder->script();
1009 }
1010
BUILTIN(LocalePrototypeRegion)1011 BUILTIN(LocalePrototypeRegion) {
1012 HandleScope scope(isolate);
1013 CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.region");
1014
1015 return locale_holder->region();
1016 }
1017
BUILTIN(LocalePrototypeBaseName)1018 BUILTIN(LocalePrototypeBaseName) {
1019 HandleScope scope(isolate);
1020 CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.baseName");
1021
1022 return locale_holder->base_name();
1023 }
1024
BUILTIN(LocalePrototypeCalendar)1025 BUILTIN(LocalePrototypeCalendar) {
1026 HandleScope scope(isolate);
1027 CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.calendar");
1028
1029 return locale_holder->calendar();
1030 }
1031
BUILTIN(LocalePrototypeCaseFirst)1032 BUILTIN(LocalePrototypeCaseFirst) {
1033 HandleScope scope(isolate);
1034 CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.caseFirst");
1035
1036 return locale_holder->case_first();
1037 }
1038
BUILTIN(LocalePrototypeCollation)1039 BUILTIN(LocalePrototypeCollation) {
1040 HandleScope scope(isolate);
1041 CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.collation");
1042
1043 return locale_holder->collation();
1044 }
1045
BUILTIN(LocalePrototypeHourCycle)1046 BUILTIN(LocalePrototypeHourCycle) {
1047 HandleScope scope(isolate);
1048 CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.hourCycle");
1049
1050 return locale_holder->hour_cycle();
1051 }
1052
BUILTIN(LocalePrototypeNumeric)1053 BUILTIN(LocalePrototypeNumeric) {
1054 HandleScope scope(isolate);
1055 CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.numeric");
1056
1057 return locale_holder->numeric();
1058 }
1059
BUILTIN(LocalePrototypeNumberingSystem)1060 BUILTIN(LocalePrototypeNumberingSystem) {
1061 HandleScope scope(isolate);
1062 CHECK_RECEIVER(JSLocale, locale_holder,
1063 "Intl.Locale.prototype.numberingSystem");
1064
1065 return locale_holder->numbering_system();
1066 }
1067
BUILTIN(LocalePrototypeToString)1068 BUILTIN(LocalePrototypeToString) {
1069 HandleScope scope(isolate);
1070 CHECK_RECEIVER(JSLocale, locale_holder, "Intl.Locale.prototype.toString");
1071
1072 return locale_holder->locale();
1073 }
1074
BUILTIN(RelativeTimeFormatConstructor)1075 BUILTIN(RelativeTimeFormatConstructor) {
1076 HandleScope scope(isolate);
1077 // 1. If NewTarget is undefined, throw a TypeError exception.
1078 if (args.new_target()->IsUndefined(isolate)) { // [[Call]]
1079 THROW_NEW_ERROR_RETURN_FAILURE(
1080 isolate, NewTypeError(MessageTemplate::kConstructorNotFunction,
1081 isolate->factory()->NewStringFromStaticChars(
1082 "Intl.RelativeTimeFormat")));
1083 }
1084 // [[Construct]]
1085 Handle<JSFunction> target = args.target();
1086 Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target());
1087
1088 Handle<JSObject> result;
1089 // 2. Let relativeTimeFormat be
1090 // ! OrdinaryCreateFromConstructor(NewTarget,
1091 // "%RelativeTimeFormatPrototype%").
1092 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
1093 JSObject::New(target, new_target));
1094 Handle<JSRelativeTimeFormat> format =
1095 Handle<JSRelativeTimeFormat>::cast(result);
1096 format->set_flags(0);
1097
1098 Handle<Object> locales = args.atOrUndefined(isolate, 1);
1099 Handle<Object> options = args.atOrUndefined(isolate, 2);
1100
1101 // 3. Return ? InitializeRelativeTimeFormat(relativeTimeFormat, locales,
1102 // options).
1103 RETURN_RESULT_OR_FAILURE(isolate,
1104 JSRelativeTimeFormat::InitializeRelativeTimeFormat(
1105 isolate, format, locales, options));
1106 }
1107
BUILTIN(RelativeTimeFormatPrototypeResolvedOptions)1108 BUILTIN(RelativeTimeFormatPrototypeResolvedOptions) {
1109 HandleScope scope(isolate);
1110 CHECK_RECEIVER(JSRelativeTimeFormat, format_holder,
1111 "Intl.RelativeTimeFormat.prototype.resolvedOptions");
1112 return *JSRelativeTimeFormat::ResolvedOptions(isolate, format_holder);
1113 }
1114
BUILTIN(StringPrototypeToLocaleLowerCase)1115 BUILTIN(StringPrototypeToLocaleLowerCase) {
1116 HandleScope scope(isolate);
1117 TO_THIS_STRING(string, "String.prototype.toLocaleLowerCase");
1118 RETURN_RESULT_OR_FAILURE(
1119 isolate, Intl::StringLocaleConvertCase(isolate, string, false,
1120 args.atOrUndefined(isolate, 1)));
1121 }
1122
BUILTIN(StringPrototypeToLocaleUpperCase)1123 BUILTIN(StringPrototypeToLocaleUpperCase) {
1124 HandleScope scope(isolate);
1125 TO_THIS_STRING(string, "String.prototype.toLocaleUpperCase");
1126 RETURN_RESULT_OR_FAILURE(
1127 isolate, Intl::StringLocaleConvertCase(isolate, string, true,
1128 args.atOrUndefined(isolate, 1)));
1129 }
1130
BUILTIN(PluralRulesConstructor)1131 BUILTIN(PluralRulesConstructor) {
1132 HandleScope scope(isolate);
1133
1134 // 1. If NewTarget is undefined, throw a TypeError exception.
1135 if (args.new_target()->IsUndefined(isolate)) { // [[Call]]
1136 THROW_NEW_ERROR_RETURN_FAILURE(
1137 isolate, NewTypeError(MessageTemplate::kConstructorNotFunction,
1138 isolate->factory()->NewStringFromStaticChars(
1139 "Intl.PluralRules")));
1140 }
1141
1142 // [[Construct]]
1143 Handle<JSFunction> target = args.target();
1144 Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target());
1145
1146 Handle<Object> locales = args.atOrUndefined(isolate, 1);
1147 Handle<Object> options = args.atOrUndefined(isolate, 2);
1148
1149 // 2. Let pluralRules be ? OrdinaryCreateFromConstructor(newTarget,
1150 // "%PluralRulesPrototype%", « [[InitializedPluralRules]],
1151 // [[Locale]], [[Type]], [[MinimumIntegerDigits]],
1152 // [[MinimumFractionDigits]], [[MaximumFractionDigits]],
1153 // [[MinimumSignificantDigits]], [[MaximumSignificantDigits]] »).
1154 Handle<JSObject> plural_rules_obj;
1155 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, plural_rules_obj,
1156 JSObject::New(target, new_target));
1157 Handle<JSPluralRules> plural_rules =
1158 Handle<JSPluralRules>::cast(plural_rules_obj);
1159
1160 // 3. Return ? InitializePluralRules(pluralRules, locales, options).
1161 RETURN_RESULT_OR_FAILURE(
1162 isolate, JSPluralRules::InitializePluralRules(isolate, plural_rules,
1163 locales, options));
1164 }
1165
BUILTIN(CollatorConstructor)1166 BUILTIN(CollatorConstructor) {
1167 HandleScope scope(isolate);
1168 Handle<JSReceiver> new_target;
1169 // 1. If NewTarget is undefined, let newTarget be the active
1170 // function object, else let newTarget be NewTarget.
1171 if (args.new_target()->IsUndefined(isolate)) {
1172 new_target = args.target();
1173 } else {
1174 new_target = Handle<JSReceiver>::cast(args.new_target());
1175 }
1176
1177 // [[Construct]]
1178 Handle<JSFunction> target = args.target();
1179
1180 Handle<Object> locales = args.atOrUndefined(isolate, 1);
1181 Handle<Object> options = args.atOrUndefined(isolate, 2);
1182
1183 // 5. Let collator be ? OrdinaryCreateFromConstructor(newTarget,
1184 // "%CollatorPrototype%", internalSlotsList).
1185 Handle<JSObject> collator_obj;
1186 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, collator_obj,
1187 JSObject::New(target, new_target));
1188 Handle<JSCollator> collator = Handle<JSCollator>::cast(collator_obj);
1189 collator->set_flags(0);
1190
1191 // 6. Return ? InitializeCollator(collator, locales, options).
1192 RETURN_RESULT_OR_FAILURE(isolate, JSCollator::InitializeCollator(
1193 isolate, collator, locales, options));
1194 }
1195
BUILTIN(CollatorPrototypeCompare)1196 BUILTIN(CollatorPrototypeCompare) {
1197 const char* const method = "get Intl.Collator.prototype.compare";
1198 HandleScope scope(isolate);
1199
1200 // 1. Let collator be this value.
1201 // 2. If Type(collator) is not Object, throw a TypeError exception.
1202 // 3. If collator does not have an [[InitializedCollator]] internal slot,
1203 // throw a TypeError exception.
1204 CHECK_RECEIVER(JSCollator, collator, method);
1205
1206 // 4. If collator.[[BoundCompare]] is undefined, then
1207 Handle<Object> bound_compare(collator->bound_compare(), isolate);
1208 if (!bound_compare->IsUndefined(isolate)) {
1209 DCHECK(bound_compare->IsJSFunction());
1210 // 5. Return collator.[[BoundCompare]].
1211 return *bound_compare;
1212 }
1213
1214 Handle<NativeContext> native_context(isolate->context()->native_context(),
1215 isolate);
1216 Handle<Context> context = isolate->factory()->NewBuiltinContext(
1217 native_context, JSCollator::ContextSlot::kLength);
1218
1219 // 4.b. Set F.[[Collator]] to collator.
1220 context->set(JSCollator::ContextSlot::kCollator, *collator);
1221
1222 Handle<SharedFunctionInfo> info = Handle<SharedFunctionInfo>(
1223 native_context->collator_internal_compare_shared_fun(), isolate);
1224 Handle<Map> map = isolate->strict_function_without_prototype_map();
1225
1226 // 4.a. Let F be a new built-in function object as defined in 10.3.3.1.
1227 Handle<JSFunction> new_bound_compare_function =
1228 isolate->factory()->NewFunctionFromSharedFunctionInfo(map, info, context);
1229
1230 // 4.c. Set collator.[[BoundCompare]] to F.
1231 collator->set_bound_compare(*new_bound_compare_function);
1232
1233 // 5. Return collator.[[BoundCompare]].
1234 return *new_bound_compare_function;
1235 }
1236
BUILTIN(CollatorInternalCompare)1237 BUILTIN(CollatorInternalCompare) {
1238 HandleScope scope(isolate);
1239 Handle<Context> context = Handle<Context>(isolate->context(), isolate);
1240
1241 // 1. Let collator be F.[[Collator]].
1242 // 2. Assert: Type(collator) is Object and collator has an
1243 // [[InitializedCollator]] internal slot.
1244 Handle<JSCollator> collator_holder = Handle<JSCollator>(
1245 JSCollator::cast(context->get(JSCollator::ContextSlot::kCollator)),
1246 isolate);
1247
1248 // 3. If x is not provided, let x be undefined.
1249 Handle<Object> x = args.atOrUndefined(isolate, 1);
1250 // 4. If y is not provided, let y be undefined.
1251 Handle<Object> y = args.atOrUndefined(isolate, 2);
1252
1253 // 5. Let X be ? ToString(x).
1254 Handle<String> string_x;
1255 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, string_x,
1256 Object::ToString(isolate, x));
1257 // 6. Let Y be ? ToString(y).
1258 Handle<String> string_y;
1259 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, string_y,
1260 Object::ToString(isolate, y));
1261
1262 // 7. Return CompareStrings(collator, X, Y).
1263 return *Intl::CompareStrings(isolate, collator_holder, string_x, string_y);
1264 }
1265
BUILTIN(BreakIteratorPrototypeAdoptText)1266 BUILTIN(BreakIteratorPrototypeAdoptText) {
1267 const char* const method = "get Intl.v8BreakIterator.prototype.adoptText";
1268 HandleScope scope(isolate);
1269
1270 CHECK_RECEIVER(JSObject, break_iterator_holder, method);
1271 if (!Intl::IsObjectOfType(isolate, break_iterator_holder,
1272 Intl::Type::kBreakIterator)) {
1273 THROW_NEW_ERROR_RETURN_FAILURE(
1274 isolate,
1275 NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
1276 isolate->factory()->NewStringFromAsciiChecked(method),
1277 break_iterator_holder));
1278 }
1279
1280 Handle<Object> bound_adopt_text =
1281 Handle<Object>(break_iterator_holder->GetEmbedderField(
1282 V8BreakIterator::kBoundAdoptTextIndex),
1283 isolate);
1284
1285 if (!bound_adopt_text->IsUndefined(isolate)) {
1286 DCHECK(bound_adopt_text->IsJSFunction());
1287 return *bound_adopt_text;
1288 }
1289
1290 Handle<NativeContext> native_context(isolate->context()->native_context(),
1291 isolate);
1292 Handle<Context> context = isolate->factory()->NewBuiltinContext(
1293 native_context, static_cast<int>(V8BreakIterator::ContextSlot::kLength));
1294
1295 context->set(static_cast<int>(V8BreakIterator::ContextSlot::kV8BreakIterator),
1296 *break_iterator_holder);
1297
1298 Handle<SharedFunctionInfo> info = Handle<SharedFunctionInfo>(
1299 native_context->break_iterator_internal_adopt_text_shared_fun(), isolate);
1300 Handle<Map> map = isolate->strict_function_without_prototype_map();
1301
1302 Handle<JSFunction> new_bound_adopt_text_function =
1303 isolate->factory()->NewFunctionFromSharedFunctionInfo(map, info, context);
1304
1305 break_iterator_holder->SetEmbedderField(V8BreakIterator::kBoundAdoptTextIndex,
1306 *new_bound_adopt_text_function);
1307
1308 return *new_bound_adopt_text_function;
1309 }
1310
BUILTIN(BreakIteratorInternalAdoptText)1311 BUILTIN(BreakIteratorInternalAdoptText) {
1312 HandleScope scope(isolate);
1313 Handle<Context> context = Handle<Context>(isolate->context(), isolate);
1314
1315 Handle<JSObject> break_iterator_holder = Handle<JSObject>(
1316 JSObject::cast(context->get(
1317 static_cast<int>(V8BreakIterator::ContextSlot::kV8BreakIterator))),
1318 isolate);
1319
1320 DCHECK(Intl::IsObjectOfType(isolate, break_iterator_holder,
1321 Intl::Type::kBreakIterator));
1322
1323 Handle<Object> input_text = args.atOrUndefined(isolate, 1);
1324 Handle<String> text;
1325 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, text,
1326 Object::ToString(isolate, input_text));
1327
1328 V8BreakIterator::AdoptText(isolate, break_iterator_holder, text);
1329 return ReadOnlyRoots(isolate).undefined_value();
1330 }
1331
1332 } // namespace internal
1333 } // namespace v8
1334