• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&regions);
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