1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 /* Operations used to implement multiple Intl.* classes. */
8 
9 #include "builtin/intl/CommonFunctions.h"
10 
11 #include "mozilla/Assertions.h"
12 #include "mozilla/Casting.h"
13 #include "mozilla/intl/ICU4CGlue.h"
14 #include "mozilla/TextUtils.h"
15 
16 #include <algorithm>
17 
18 #include "gc/GCEnum.h"
19 #include "gc/Zone.h"
20 #include "gc/ZoneAllocator.h"
21 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_INTERNAL_INTL_ERROR
22 #include "js/Value.h"
23 #include "vm/JSContext.h"
24 #include "vm/JSObject.h"
25 #include "vm/SelfHosting.h"
26 #include "vm/Stack.h"
27 
28 #include "vm/JSObject-inl.h"
29 
InitializeObject(JSContext * cx,JS::Handle<JSObject * > obj,JS::Handle<PropertyName * > initializer,JS::Handle<JS::Value> locales,JS::Handle<JS::Value> options)30 bool js::intl::InitializeObject(JSContext* cx, JS::Handle<JSObject*> obj,
31                                 JS::Handle<PropertyName*> initializer,
32                                 JS::Handle<JS::Value> locales,
33                                 JS::Handle<JS::Value> options) {
34   FixedInvokeArgs<3> args(cx);
35 
36   args[0].setObject(*obj);
37   args[1].set(locales);
38   args[2].set(options);
39 
40   RootedValue ignored(cx);
41   if (!CallSelfHostedFunction(cx, initializer, JS::NullHandleValue, args,
42                               &ignored)) {
43     return false;
44   }
45 
46   MOZ_ASSERT(ignored.isUndefined(),
47              "Unexpected return value from non-legacy Intl object initializer");
48   return true;
49 }
50 
LegacyInitializeObject(JSContext * cx,JS::Handle<JSObject * > obj,JS::Handle<PropertyName * > initializer,JS::Handle<JS::Value> thisValue,JS::Handle<JS::Value> locales,JS::Handle<JS::Value> options,DateTimeFormatOptions dtfOptions,JS::MutableHandle<JS::Value> result)51 bool js::intl::LegacyInitializeObject(JSContext* cx, JS::Handle<JSObject*> obj,
52                                       JS::Handle<PropertyName*> initializer,
53                                       JS::Handle<JS::Value> thisValue,
54                                       JS::Handle<JS::Value> locales,
55                                       JS::Handle<JS::Value> options,
56                                       DateTimeFormatOptions dtfOptions,
57                                       JS::MutableHandle<JS::Value> result) {
58   FixedInvokeArgs<5> args(cx);
59 
60   args[0].setObject(*obj);
61   args[1].set(thisValue);
62   args[2].set(locales);
63   args[3].set(options);
64   args[4].setBoolean(dtfOptions == DateTimeFormatOptions::EnableMozExtensions);
65 
66   if (!CallSelfHostedFunction(cx, initializer, NullHandleValue, args, result)) {
67     return false;
68   }
69 
70   MOZ_ASSERT(result.isObject(),
71              "Legacy Intl object initializer must return an object");
72   return true;
73 }
74 
GetInternalsObject(JSContext * cx,JS::Handle<JSObject * > obj)75 JSObject* js::intl::GetInternalsObject(JSContext* cx,
76                                        JS::Handle<JSObject*> obj) {
77   FixedInvokeArgs<1> args(cx);
78 
79   args[0].setObject(*obj);
80 
81   RootedValue v(cx);
82   if (!js::CallSelfHostedFunction(cx, cx->names().getInternals, NullHandleValue,
83                                   args, &v)) {
84     return nullptr;
85   }
86 
87   return &v.toObject();
88 }
89 
ReportInternalError(JSContext * cx)90 void js::intl::ReportInternalError(JSContext* cx) {
91   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
92                             JSMSG_INTERNAL_INTL_ERROR);
93 }
94 
ReportInternalError(JSContext * cx,mozilla::intl::ICUError error)95 void js::intl::ReportInternalError(JSContext* cx,
96                                    mozilla::intl::ICUError error) {
97   switch (error) {
98     case mozilla::intl::ICUError::OutOfMemory:
99       ReportOutOfMemory(cx);
100       return;
101     case mozilla::intl::ICUError::InternalError:
102       ReportInternalError(cx);
103       return;
104     case mozilla::intl::ICUError::OverflowError:
105       ReportAllocationOverflow(cx);
106       return;
107   }
108   MOZ_CRASH("Unexpected ICU error");
109 }
110 
111 const js::intl::OldStyleLanguageTagMapping
112     js::intl::oldStyleLanguageTagMappings[] = {
113         {"pa-PK", "pa-Arab-PK"}, {"zh-CN", "zh-Hans-CN"},
114         {"zh-HK", "zh-Hant-HK"}, {"zh-SG", "zh-Hans-SG"},
115         {"zh-TW", "zh-Hant-TW"},
116 };
117 
EncodeLocale(JSContext * cx,JSString * locale)118 js::UniqueChars js::intl::EncodeLocale(JSContext* cx, JSString* locale) {
119   MOZ_ASSERT(locale->length() > 0);
120 
121   js::UniqueChars chars = EncodeAscii(cx, locale);
122 
123 #ifdef DEBUG
124   // Ensure the returned value contains only valid BCP 47 characters.
125   // (Lambdas can't be placed inside MOZ_ASSERT, so move the checks in an
126   // #ifdef block.)
127   if (chars) {
128     auto alnumOrDash = [](char c) {
129       return mozilla::IsAsciiAlphanumeric(c) || c == '-';
130     };
131     MOZ_ASSERT(mozilla::IsAsciiAlpha(chars[0]));
132     MOZ_ASSERT(
133         std::all_of(chars.get(), chars.get() + locale->length(), alnumOrDash));
134   }
135 #endif
136 
137   return chars;
138 }
139 
AddICUCellMemory(JSObject * obj,size_t nbytes)140 void js::intl::AddICUCellMemory(JSObject* obj, size_t nbytes) {
141   // Account the (estimated) number of bytes allocated by an ICU object against
142   // the JSObject's zone.
143   AddCellMemory(obj, nbytes, MemoryUse::ICUObject);
144 }
145 
RemoveICUCellMemory(JSFreeOp * fop,JSObject * obj,size_t nbytes)146 void js::intl::RemoveICUCellMemory(JSFreeOp* fop, JSObject* obj,
147                                    size_t nbytes) {
148   fop->removeCellMemory(obj, nbytes, MemoryUse::ICUObject);
149 }
150