039807258c7d5b786ed22cc67c69fe8e49ddc747
[WebKit-https.git] / Source / JavaScriptCore / runtime / IntlNumberFormatPrototype.cpp
1 /*
2  * Copyright (C) 2015 Andy VanWagoner (andy@vanwagoner.family)
3  * Copyright (C) 2016 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24  * THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "IntlNumberFormatPrototype.h"
29
30 #if ENABLE(INTL)
31
32 #include "BuiltinNames.h"
33 #include "Error.h"
34 #include "IntlNumberFormat.h"
35 #include "JSBoundFunction.h"
36 #include "JSCInlines.h"
37 #include "JSObjectInlines.h"
38 #include "Options.h"
39
40 namespace JSC {
41
42 static EncodedJSValue JSC_HOST_CALL IntlNumberFormatPrototypeGetterFormat(ExecState*);
43 #if HAVE(ICU_FORMAT_DOUBLE_FOR_FIELDS)
44 static EncodedJSValue JSC_HOST_CALL IntlNumberFormatPrototypeFuncFormatToParts(ExecState*);
45 #endif
46 static EncodedJSValue JSC_HOST_CALL IntlNumberFormatPrototypeFuncResolvedOptions(ExecState*);
47
48 }
49
50 #include "IntlNumberFormatPrototype.lut.h"
51
52 namespace JSC {
53
54 const ClassInfo IntlNumberFormatPrototype::s_info = { "Object", &Base::s_info, &numberFormatPrototypeTable, nullptr, CREATE_METHOD_TABLE(IntlNumberFormatPrototype) };
55
56 /* Source for IntlNumberFormatPrototype.lut.h
57 @begin numberFormatPrototypeTable
58   format           IntlNumberFormatPrototypeGetterFormat         DontEnum|Accessor
59   resolvedOptions  IntlNumberFormatPrototypeFuncResolvedOptions  DontEnum|Function 0
60 @end
61 */
62
63 IntlNumberFormatPrototype* IntlNumberFormatPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
64 {
65     IntlNumberFormatPrototype* object = new (NotNull, allocateCell<IntlNumberFormatPrototype>(vm.heap)) IntlNumberFormatPrototype(vm, structure);
66     object->finishCreation(vm, globalObject, structure);
67     return object;
68 }
69
70 Structure* IntlNumberFormatPrototype::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
71 {
72     return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
73 }
74
75 IntlNumberFormatPrototype::IntlNumberFormatPrototype(VM& vm, Structure* structure)
76     : Base(vm, structure)
77 {
78 }
79
80 void IntlNumberFormatPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject, Structure*)
81 {
82     Base::finishCreation(vm);
83 #if HAVE(ICU_FORMAT_DOUBLE_FOR_FIELDS)
84     if (Options::useIntlNumberFormatToParts())
85         JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->formatToParts, IntlNumberFormatPrototypeFuncFormatToParts, static_cast<unsigned>(PropertyAttribute::DontEnum), 1);
86 #else
87     UNUSED_PARAM(globalObject);
88 #endif
89
90     putDirectWithoutTransition(vm, vm.propertyNames->toStringTagSymbol, jsString(&vm, "Object"), PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly);
91     didBecomePrototype();
92 }
93
94 static EncodedJSValue JSC_HOST_CALL IntlNumberFormatFuncFormatNumber(ExecState* state)
95 {
96     VM& vm = state->vm();
97     auto scope = DECLARE_THROW_SCOPE(vm);
98     // 11.3.4 Format Number Functions (ECMA-402 2.0)
99     // 1. Let nf be the this value.
100     // 2. Assert: Type(nf) is Object and nf has an [[initializedNumberFormat]] internal slot whose value  true.
101     IntlNumberFormat* numberFormat = jsCast<IntlNumberFormat*>(state->thisValue());
102
103     // 3. If value is not provided, let value be undefined.
104     // 4. Let x be ToNumber(value).
105     double number = state->argument(0).toNumber(state);
106     // 5. ReturnIfAbrupt(x).
107     RETURN_IF_EXCEPTION(scope, encodedJSValue());
108
109     // 6. Return FormatNumber(nf, x).
110     RELEASE_AND_RETURN(scope, JSValue::encode(numberFormat->formatNumber(*state, number)));
111 }
112
113 EncodedJSValue JSC_HOST_CALL IntlNumberFormatPrototypeGetterFormat(ExecState* state)
114 {
115     VM& vm = state->vm();
116     auto scope = DECLARE_THROW_SCOPE(vm);
117
118     // 11.3.3 Intl.NumberFormat.prototype.format (ECMA-402 2.0)
119     // 1. Let nf be this NumberFormat object.
120     IntlNumberFormat* nf = jsDynamicCast<IntlNumberFormat*>(vm, state->thisValue());
121
122     // FIXME: Workaround to provide compatibility with ECMA-402 1.0 call/apply patterns.
123     // https://bugs.webkit.org/show_bug.cgi?id=153679
124     if (!nf) {
125         JSValue value = state->thisValue().get(state, vm.propertyNames->builtinNames().intlSubstituteValuePrivateName());
126         RETURN_IF_EXCEPTION(scope, encodedJSValue());
127         nf = jsDynamicCast<IntlNumberFormat*>(vm, value);
128     }
129
130     if (!nf)
131         return JSValue::encode(throwTypeError(state, scope, "Intl.NumberFormat.prototype.format called on value that's not an object initialized as a NumberFormat"_s));
132     
133     JSBoundFunction* boundFormat = nf->boundFormat();
134     // 2. If nf.[[boundFormat]] is undefined,
135     if (!boundFormat) {
136         JSGlobalObject* globalObject = nf->globalObject(vm);
137         // a. Let F be a new built-in function object as defined in 11.3.4.
138         // b. The value of F’s length property is 1.
139         JSFunction* targetObject = JSFunction::create(vm, globalObject, 1, "format"_s, IntlNumberFormatFuncFormatNumber, NoIntrinsic);
140         // c. Let bf be BoundFunctionCreate(F, «this value»).
141         boundFormat = JSBoundFunction::create(vm, state, globalObject, targetObject, nf, nullptr, 1, "format"_s);
142         RETURN_IF_EXCEPTION(scope, encodedJSValue());
143         // d. Set nf.[[boundFormat]] to bf.
144         nf->setBoundFormat(vm, boundFormat);
145     }
146     // 3. Return nf.[[boundFormat]].
147     return JSValue::encode(boundFormat);
148 }
149
150 #if HAVE(ICU_FORMAT_DOUBLE_FOR_FIELDS)
151 EncodedJSValue JSC_HOST_CALL IntlNumberFormatPrototypeFuncFormatToParts(ExecState* state)
152 {
153     VM& vm = state->vm();
154     auto scope = DECLARE_THROW_SCOPE(vm);
155
156     // Intl.NumberFormat.prototype.formatToParts (ECMA-402)
157     // https://tc39.github.io/ecma402/#sec-intl.numberformat.prototype.formattoparts
158
159     IntlNumberFormat* numberFormat = jsDynamicCast<IntlNumberFormat*>(vm, state->thisValue());
160     if (!numberFormat)
161         return JSValue::encode(throwTypeError(state, scope, "Intl.NumberFormat.prototype.formatToParts called on value that's not an object initialized as a NumberFormat"_s));
162
163     double value = state->argument(0).toNumber(state);
164     RETURN_IF_EXCEPTION(scope, encodedJSValue());
165
166     RELEASE_AND_RETURN(scope, JSValue::encode(numberFormat->formatToParts(*state, value)));
167 }
168 #endif
169
170 EncodedJSValue JSC_HOST_CALL IntlNumberFormatPrototypeFuncResolvedOptions(ExecState* state)
171 {
172     VM& vm = state->vm();
173     auto scope = DECLARE_THROW_SCOPE(vm);
174
175     // 11.3.5 Intl.NumberFormat.prototype.resolvedOptions() (ECMA-402 2.0)
176     IntlNumberFormat* numberFormat = jsDynamicCast<IntlNumberFormat*>(vm, state->thisValue());
177
178     // FIXME: Workaround to provide compatibility with ECMA-402 1.0 call/apply patterns.
179     // https://bugs.webkit.org/show_bug.cgi?id=153679
180     if (!numberFormat) {
181         JSValue value = state->thisValue().get(state, vm.propertyNames->builtinNames().intlSubstituteValuePrivateName());
182         RETURN_IF_EXCEPTION(scope, encodedJSValue());
183         numberFormat = jsDynamicCast<IntlNumberFormat*>(vm, value);
184     }
185
186     if (!numberFormat)
187         return JSValue::encode(throwTypeError(state, scope, "Intl.NumberFormat.prototype.resolvedOptions called on value that's not an object initialized as a NumberFormat"_s));
188
189     RELEASE_AND_RETURN(scope, JSValue::encode(numberFormat->resolvedOptions(*state)));
190 }
191
192 } // namespace JSC
193
194 #endif // ENABLE(INTL)