• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef V8_INTL_SUPPORT
6 #error Internationalization is expected to be enabled.
7 #endif  // V8_INTL_SUPPORT
8 
9 #ifndef V8_OBJECTS_INTL_OBJECTS_H_
10 #define V8_OBJECTS_INTL_OBJECTS_H_
11 
12 #include <map>
13 #include <set>
14 #include <string>
15 
16 #include "src/contexts.h"
17 #include "src/intl.h"
18 #include "src/objects.h"
19 #include "unicode/locid.h"
20 #include "unicode/uversion.h"
21 
22 namespace U_ICU_NAMESPACE {
23 class BreakIterator;
24 class Collator;
25 class DecimalFormat;
26 class PluralRules;
27 class SimpleDateFormat;
28 class UnicodeString;
29 }
30 
31 namespace v8 {
32 namespace internal {
33 
34 template <typename T>
35 class Handle;
36 
37 class DateFormat {
38  public:
39   // Create a formatter for the specificied locale and options. Returns the
40   // resolved settings for the locale / options.
41   static icu::SimpleDateFormat* InitializeDateTimeFormat(
42       Isolate* isolate, Handle<String> locale, Handle<JSObject> options,
43       Handle<JSObject> resolved);
44 
45   // Unpacks date format object from corresponding JavaScript object.
46   static icu::SimpleDateFormat* UnpackDateFormat(Handle<JSObject> obj);
47 
48   // Release memory we allocated for the DateFormat once the JS object that
49   // holds the pointer gets garbage collected.
50   static void DeleteDateFormat(const v8::WeakCallbackInfo<void>& data);
51 
52   // ecma402/#sec-formatdatetime
53   // FormatDateTime( dateTimeFormat, x )
54   V8_WARN_UNUSED_RESULT static MaybeHandle<String> FormatDateTime(
55       Isolate* isolate, Handle<JSObject> date_time_format_holder, double x);
56 
57   // ecma402/#sec-datetime-format-functions
58   // DateTime Format Functions
59   V8_WARN_UNUSED_RESULT static MaybeHandle<String> DateTimeFormat(
60       Isolate* isolate, Handle<JSObject> date_time_format_holder,
61       Handle<Object> date);
62 
63   // The UnwrapDateTimeFormat abstract operation gets the underlying
64   // DateTimeFormat operation for various methods which implement ECMA-402 v1
65   // semantics for supporting initializing existing Intl objects.
66   //
67   // ecma402/#sec-unwrapdatetimeformat
68   V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> Unwrap(
69       Isolate* isolate, Handle<JSReceiver> receiver, const char* method_name);
70 
71   // ecma-402/#sec-todatetimeoptions
72   V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> ToDateTimeOptions(
73       Isolate* isolate, Handle<Object> input_options, const char* required,
74       const char* defaults);
75 
76   V8_WARN_UNUSED_RESULT static MaybeHandle<String> ToLocaleDateTime(
77       Isolate* isolate, Handle<Object> date, Handle<Object> locales,
78       Handle<Object> options, const char* required, const char* defaults,
79       const char* service);
80 
81   // Layout description.
82 #define DATE_FORMAT_FIELDS(V)        \
83   V(kSimpleDateFormat, kPointerSize) \
84   V(kBoundFormat, kPointerSize)      \
85   V(kSize, 0)
86 
87   DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize, DATE_FORMAT_FIELDS)
88 #undef DATE_FORMAT_FIELDS
89 
90   // ContextSlot defines the context structure for the bound
91   // DateTimeFormat.prototype.format function
92   enum ContextSlot {
93     kDateFormat = Context::MIN_CONTEXT_SLOTS,
94 
95     kLength
96   };
97 
98   // TODO(ryzokuken): Remove this and use regular accessors once DateFormat is a
99   // subclass of JSObject
100   //
101   // This needs to be consistent with the above Layout Description
102   static const int kSimpleDateFormatIndex = 0;
103   static const int kBoundFormatIndex = 1;
104 
105  private:
106   DateFormat();
107 };
108 
109 class NumberFormat {
110  public:
111   // Create a formatter for the specificied locale and options. Returns the
112   // resolved settings for the locale / options.
113   static icu::DecimalFormat* InitializeNumberFormat(Isolate* isolate,
114                                                     Handle<String> locale,
115                                                     Handle<JSObject> options,
116                                                     Handle<JSObject> resolved);
117 
118   // Unpacks number format object from corresponding JavaScript object.
119   static icu::DecimalFormat* UnpackNumberFormat(Handle<JSObject> obj);
120 
121   // Release memory we allocated for the NumberFormat once the JS object that
122   // holds the pointer gets garbage collected.
123   static void DeleteNumberFormat(const v8::WeakCallbackInfo<void>& data);
124 
125   // The UnwrapNumberFormat abstract operation gets the underlying
126   // NumberFormat operation for various methods which implement
127   // ECMA-402 v1 semantics for supporting initializing existing Intl
128   // objects.
129   //
130   // ecma402/#sec-unwrapnumberformat
131   static MaybeHandle<JSObject> Unwrap(Isolate* isolate,
132                                       Handle<JSReceiver> receiver,
133                                       const char* method_name);
134 
135   // ecm402/#sec-formatnumber
136   static MaybeHandle<String> FormatNumber(Isolate* isolate,
137                                           Handle<JSObject> number_format_holder,
138                                           double value);
139 
140   // Layout description.
141 #define NUMBER_FORMAT_FIELDS(V)   \
142   /* Pointer fields. */           \
143   V(kDecimalFormat, kPointerSize) \
144   V(kBoundFormat, kPointerSize)   \
145   V(kSize, 0)
146 
147   DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize, NUMBER_FORMAT_FIELDS)
148 #undef NUMBER_FORMAT_FIELDS
149 
150   // ContextSlot defines the context structure for the bound
151   // NumberFormat.prototype.format function.
152   enum ContextSlot {
153     // The number format instance that the function holding this
154     // context is bound to.
155     kNumberFormat = Context::MIN_CONTEXT_SLOTS,
156 
157     kLength
158   };
159 
160   // TODO(gsathya): Remove this and use regular accessors once
161   // NumberFormat is a sub class of JSObject.
162   //
163   // This needs to be consistent with the above LayoutDescription.
164   static const int kDecimalFormatIndex = 0;
165   static const int kBoundFormatIndex = 1;
166 
167  private:
168   NumberFormat();
169 };
170 
171 class V8BreakIterator {
172  public:
173   // Create a BreakIterator for the specificied locale and options. Returns the
174   // resolved settings for the locale / options.
175   static icu::BreakIterator* InitializeBreakIterator(Isolate* isolate,
176                                                      Handle<String> locale,
177                                                      Handle<JSObject> options,
178                                                      Handle<JSObject> resolved);
179 
180   // Unpacks break iterator object from corresponding JavaScript object.
181   static icu::BreakIterator* UnpackBreakIterator(Handle<JSObject> obj);
182 
183   // Release memory we allocated for the BreakIterator once the JS object that
184   // holds the pointer gets garbage collected.
185   static void DeleteBreakIterator(const v8::WeakCallbackInfo<void>& data);
186 
187   static void AdoptText(Isolate* isolate,
188                         Handle<JSObject> break_iterator_holder,
189                         Handle<String> text);
190 
191   // Layout description.
192 #define BREAK_ITERATOR_FIELDS(V)   \
193   /* Pointer fields. */            \
194   V(kBreakIterator, kPointerSize)  \
195   V(kUnicodeString, kPointerSize)  \
196   V(kBoundAdoptText, kPointerSize) \
197   V(kSize, 0)
198 
199   DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize, BREAK_ITERATOR_FIELDS)
200 #undef BREAK_ITERATOR_FIELDS
201 
202   // ContextSlot defines the context structure for the bound
203   // v8BreakIterator.prototype.adoptText function
204   enum class ContextSlot {
205     kV8BreakIterator = Context::MIN_CONTEXT_SLOTS,
206 
207     kLength
208   };
209 
210   // TODO(ryzokuken): Remove this and use regular accessors once v8BreakIterator
211   // is a subclass of JSObject
212   //
213   // This needs to be consistent with the above Layour Description
214   static const int kBreakIteratorIndex = 0;
215   static const int kUnicodeStringIndex = 1;
216   static const int kBoundAdoptTextIndex = 2;
217 
218  private:
219   V8BreakIterator();
220 };
221 
222 class Intl {
223  public:
224   enum Type {
225     kNumberFormat = 0,
226     kCollator,
227     kDateTimeFormat,
228     kPluralRules,
229     kBreakIterator,
230     kLocale,
231 
232     kTypeCount
233   };
234 
235   inline static Intl::Type TypeFromInt(int type);
236   inline static Intl::Type TypeFromSmi(Smi* type);
237 
238   // Checks if the given object has the expected_type based by looking
239   // up a private symbol on the object.
240   //
241   // TODO(gsathya): This should just be an instance type check once we
242   // move all the Intl objects to C++.
243   static bool IsObjectOfType(Isolate* isolate, Handle<Object> object,
244                              Intl::Type expected_type);
245 
246   static IcuService StringToIcuService(Handle<String> service);
247 
248   // Gets the ICU locales for a given service. If there is a locale with a
249   // script tag then the locales also include a locale without the script; eg,
250   // pa_Guru_IN (language=Panjabi, script=Gurmukhi, country-India) would include
251   // pa_IN.
252   static std::set<std::string> GetAvailableLocales(const IcuService& service);
253 
254   static V8_WARN_UNUSED_RESULT MaybeHandle<JSObject> AvailableLocalesOf(
255       Isolate* isolate, Handle<String> service);
256 
257   static MaybeHandle<JSObject> SupportedLocalesOf(Isolate* isolate,
258                                                   Handle<String> service,
259                                                   Handle<Object> locales_in,
260                                                   Handle<Object> options_in);
261 
262   static std::string DefaultLocale(Isolate* isolate);
263 
264   static void DefineWEProperty(Isolate* isolate, Handle<JSObject> target,
265                                Handle<Name> key, Handle<Object> value);
266 
267   // If locale has a script tag then return true and the locale without the
268   // script else return false and an empty string
269   static bool RemoveLocaleScriptTag(const std::string& icu_locale,
270                                     std::string* locale_less_script);
271 
272   // Returns the underlying Intl receiver for various methods which
273   // implement ECMA-402 v1 semantics for supporting initializing
274   // existing Intl objects.
275   V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> UnwrapReceiver(
276       Isolate* isolate, Handle<JSReceiver> receiver,
277       Handle<JSFunction> constructor, Intl::Type type,
278       Handle<String> method_name /* TODO(gsathya): Make this char const* */,
279       bool check_legacy_constructor = false);
280 
281   // The ResolveLocale abstract operation compares a BCP 47 language
282   // priority list requestedLocales against the locales in
283   // availableLocales and determines the best available language to
284   // meet the request. availableLocales, requestedLocales, and
285   // relevantExtensionKeys must be provided as List values, options
286   // and localeData as Records.
287   //
288   // #ecma402/sec-partitiondatetimepattern
289   //
290   // Returns a JSObject with two properties:
291   //   (1) locale
292   //   (2) extension
293   //
294   // To access either, use JSObject::GetDataProperty.
295   V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> ResolveLocale(
296       Isolate* isolate, const char* service, Handle<Object> requestedLocales,
297       Handle<Object> options);
298 
299   // This currently calls out to the JavaScript implementation of
300   // CanonicalizeLocaleList.
301   // Note: This is deprecated glue code, required only as long as ResolveLocale
302   // still calls a JS implementation. The C++ successor is the overloaded
303   // version below that returns a Maybe<std::vector<std::string>>.
304   //
305   // ecma402/#sec-canonicalizelocalelist
306   V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> CanonicalizeLocaleListJS(
307       Isolate* isolate, Handle<Object> locales);
308 
309   // ECMA402 9.2.10. GetOption( options, property, type, values, fallback)
310   // ecma402/#sec-getoption
311   //
312   // This is specialized for the case when type is string.
313   //
314   // Instead of passing undefined for the values argument as the spec
315   // defines, pass in an empty vector.
316   //
317   // Returns true if options object has the property and stores the
318   // result in value. Returns false if the value is not found. The
319   // caller is required to use fallback value appropriately in this
320   // case.
321   //
322   // service is a string denoting the type of Intl object; used when
323   // printing the error message.
324   V8_WARN_UNUSED_RESULT static Maybe<bool> GetStringOption(
325       Isolate* isolate, Handle<JSReceiver> options, const char* property,
326       std::vector<const char*> values, const char* service,
327       std::unique_ptr<char[]>* result);
328 
329   // ECMA402 9.2.10. GetOption( options, property, type, values, fallback)
330   // ecma402/#sec-getoption
331   //
332   // This is specialized for the case when type is boolean.
333   //
334   // Returns true if options object has the property and stores the
335   // result in value. Returns false if the value is not found. The
336   // caller is required to use fallback value appropriately in this
337   // case.
338   //
339   // service is a string denoting the type of Intl object; used when
340   // printing the error message.
341   V8_WARN_UNUSED_RESULT static Maybe<bool> GetBoolOption(
342       Isolate* isolate, Handle<JSReceiver> options, const char* property,
343       const char* service, bool* result);
344 
345   // Canonicalize the locale.
346   // https://tc39.github.io/ecma402/#sec-canonicalizelanguagetag,
347   // including type check and structural validity check.
348   static Maybe<std::string> CanonicalizeLanguageTag(Isolate* isolate,
349                                                     Handle<Object> locale_in);
350 
351   // https://tc39.github.io/ecma402/#sec-canonicalizelocalelist
352   // {only_return_one_result} is an optimization for callers that only
353   // care about the first result.
354   static Maybe<std::vector<std::string>> CanonicalizeLocaleList(
355       Isolate* isolate, Handle<Object> locales,
356       bool only_return_one_result = false);
357 
358   // ecma-402/#sec-currencydigits
359   // The currency is expected to an all upper case string value.
360   static Handle<Smi> CurrencyDigits(Isolate* isolate, Handle<String> currency);
361 
362   // TODO(ftang): Remove this and use ICU to the conversion in the future
363   static void ParseExtension(Isolate* isolate, const std::string& extension,
364                              std::map<std::string, std::string>& out);
365 
366   V8_WARN_UNUSED_RESULT static MaybeHandle<JSObject> CreateNumberFormat(
367       Isolate* isolate, Handle<String> locale, Handle<JSObject> options,
368       Handle<JSObject> resolved);
369 
370   // ecma402/#sec-iswellformedcurrencycode
371   static bool IsWellFormedCurrencyCode(Isolate* isolate,
372                                        Handle<String> currency);
373 
374   // For locale sensitive functions
375   V8_WARN_UNUSED_RESULT static MaybeHandle<String> StringLocaleConvertCase(
376       Isolate* isolate, Handle<String> s, bool is_upper,
377       Handle<Object> locales);
378 
379   V8_WARN_UNUSED_RESULT static MaybeHandle<Object> StringLocaleCompare(
380       Isolate* isolate, Handle<String> s1, Handle<String> s2,
381       Handle<Object> locales, Handle<Object> options);
382 
383   V8_WARN_UNUSED_RESULT static Handle<Object> CompareStrings(
384       Isolate* isolate, Handle<JSCollator> collator, Handle<String> s1,
385       Handle<String> s2);
386 
387   // ecma402/#sup-properties-of-the-number-prototype-object
388   V8_WARN_UNUSED_RESULT static MaybeHandle<String> NumberToLocaleString(
389       Isolate* isolate, Handle<Object> num, Handle<Object> locales,
390       Handle<Object> options);
391 
392   // ecma402/#sec-defaultnumberoption
393   V8_WARN_UNUSED_RESULT static Maybe<int> DefaultNumberOption(
394       Isolate* isolate, Handle<Object> value, int min, int max, int fallback,
395       Handle<String> property);
396 
397   // ecma402/#sec-getnumberoption
398   V8_WARN_UNUSED_RESULT static Maybe<int> GetNumberOption(
399       Isolate* isolate, Handle<JSReceiver> options, Handle<String> property,
400       int min, int max, int fallback);
401   V8_WARN_UNUSED_RESULT static Maybe<int> GetNumberOption(
402       Isolate* isolate, Handle<JSReceiver> options, const char* property,
403       int min, int max, int fallback);
404 
405   // ecma402/#sec-setnfdigitoptions
406   V8_WARN_UNUSED_RESULT static Maybe<bool> SetNumberFormatDigitOptions(
407       Isolate* isolate, icu::DecimalFormat* number_format,
408       Handle<JSReceiver> options, int mnfd_default, int mxfd_default);
409 
410   icu::Locale static CreateICULocale(Isolate* isolate,
411                                      Handle<String> bcp47_locale_str);
412 
413   // Helper funciton to convert a UnicodeString to a Handle<String>
414   V8_WARN_UNUSED_RESULT static MaybeHandle<String> ToString(
415       Isolate* isolate, const icu::UnicodeString& string);
416 
417   // Helper function to convert a substring of UnicodeString to a Handle<String>
418   V8_WARN_UNUSED_RESULT static MaybeHandle<String> ToString(
419       Isolate* isolate, const icu::UnicodeString& string, int32_t begin,
420       int32_t end);
421 
422   // A helper function to implement formatToParts which add element to array as
423   // $array[$index] = { type: $field_type_string, value: $value }
424   static void AddElement(Isolate* isolate, Handle<JSArray> array, int index,
425                          Handle<String> field_type_string,
426                          Handle<String> value);
427 
428   // A helper function to implement formatToParts which add element to array as
429   // $array[$index] = {
430   //   type: $field_type_string, value: $value,
431   //   $additional_property_name: $additional_property_value
432   // }
433   static void AddElement(Isolate* isolate, Handle<JSArray> array, int index,
434                          Handle<String> field_type_string, Handle<String> value,
435                          Handle<String> additional_property_name,
436                          Handle<String> additional_property_value);
437 };
438 
439 }  // namespace internal
440 }  // namespace v8
441 
442 #endif  // V8_OBJECTS_INTL_OBJECTS_H_
443