1 | /* |
2 | * Copyright (C) 2015 Andy VanWagoner (andy@vanwagoner.family) |
3 | * Copyright (C) 2016-2019 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 "IntlDateTimeFormatPrototype.h" |
29 | |
30 | #if ENABLE(INTL) |
31 | |
32 | #include "BuiltinNames.h" |
33 | #include "DateConstructor.h" |
34 | #include "Error.h" |
35 | #include "IntlDateTimeFormat.h" |
36 | #include "IntlObject.h" |
37 | #include "JSBoundFunction.h" |
38 | #include "JSCInlines.h" |
39 | #include "JSObjectInlines.h" |
40 | #include <wtf/DateMath.h> |
41 | |
42 | namespace JSC { |
43 | |
44 | static EncodedJSValue JSC_HOST_CALL IntlDateTimeFormatPrototypeGetterFormat(JSGlobalObject*, CallFrame*); |
45 | #if JSC_ICU_HAS_UFIELDPOSITER |
46 | static EncodedJSValue JSC_HOST_CALL IntlDateTimeFormatPrototypeFuncFormatToParts(JSGlobalObject*, CallFrame*); |
47 | #endif |
48 | static EncodedJSValue JSC_HOST_CALL IntlDateTimeFormatPrototypeFuncResolvedOptions(JSGlobalObject*, CallFrame*); |
49 | |
50 | } |
51 | |
52 | #include "IntlDateTimeFormatPrototype.lut.h" |
53 | |
54 | namespace JSC { |
55 | |
56 | const ClassInfo IntlDateTimeFormatPrototype::s_info = { "Object" , &Base::s_info, &dateTimeFormatPrototypeTable, nullptr, CREATE_METHOD_TABLE(IntlDateTimeFormatPrototype) }; |
57 | |
58 | /* Source for IntlDateTimeFormatPrototype.lut.h |
59 | @begin dateTimeFormatPrototypeTable |
60 | format IntlDateTimeFormatPrototypeGetterFormat DontEnum|Accessor |
61 | resolvedOptions IntlDateTimeFormatPrototypeFuncResolvedOptions DontEnum|Function 0 |
62 | @end |
63 | */ |
64 | |
65 | IntlDateTimeFormatPrototype* IntlDateTimeFormatPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure) |
66 | { |
67 | IntlDateTimeFormatPrototype* object = new (NotNull, allocateCell<IntlDateTimeFormatPrototype>(vm.heap)) IntlDateTimeFormatPrototype(vm, structure); |
68 | object->finishCreation(vm, globalObject, structure); |
69 | return object; |
70 | } |
71 | |
72 | Structure* IntlDateTimeFormatPrototype::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) |
73 | { |
74 | return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); |
75 | } |
76 | |
77 | IntlDateTimeFormatPrototype::IntlDateTimeFormatPrototype(VM& vm, Structure* structure) |
78 | : Base(vm, structure) |
79 | { |
80 | } |
81 | |
82 | void IntlDateTimeFormatPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject, Structure*) |
83 | { |
84 | Base::finishCreation(vm); |
85 | #if JSC_ICU_HAS_UFIELDPOSITER |
86 | JSFunction* formatToPartsFunction = JSFunction::create(vm, globalObject, 1, vm.propertyNames->formatToParts.string(), IntlDateTimeFormatPrototypeFuncFormatToParts); |
87 | putDirectWithoutTransition(vm, vm.propertyNames->formatToParts, formatToPartsFunction, static_cast<unsigned>(PropertyAttribute::DontEnum)); |
88 | #else |
89 | UNUSED_PARAM(globalObject); |
90 | #endif |
91 | |
92 | putDirectWithoutTransition(vm, vm.propertyNames->toStringTagSymbol, jsString(vm, "Object" ), PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly); |
93 | } |
94 | |
95 | static EncodedJSValue JSC_HOST_CALL IntlDateTimeFormatFuncFormatDateTime(JSGlobalObject* globalObject, CallFrame* callFrame) |
96 | { |
97 | VM& vm = globalObject->vm(); |
98 | auto scope = DECLARE_THROW_SCOPE(vm); |
99 | // 12.1.7 DateTime Format Functions (ECMA-402) |
100 | // https://tc39.github.io/ecma402/#sec-formatdatetime |
101 | |
102 | IntlDateTimeFormat* format = jsCast<IntlDateTimeFormat*>(callFrame->thisValue()); |
103 | |
104 | JSValue date = callFrame->argument(0); |
105 | double value; |
106 | |
107 | if (date.isUndefined()) |
108 | value = dateNowImpl().toNumber(globalObject); |
109 | else { |
110 | value = WTF::timeClip(date.toNumber(globalObject)); |
111 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
112 | } |
113 | |
114 | RELEASE_AND_RETURN(scope, JSValue::encode(format->format(globalObject, value))); |
115 | } |
116 | |
117 | EncodedJSValue JSC_HOST_CALL IntlDateTimeFormatPrototypeGetterFormat(JSGlobalObject* globalObject, CallFrame* callFrame) |
118 | { |
119 | VM& vm = globalObject->vm(); |
120 | auto scope = DECLARE_THROW_SCOPE(vm); |
121 | |
122 | // 12.3.3 Intl.DateTimeFormat.prototype.format (ECMA-402 2.0) |
123 | // 1. Let dtf be this DateTimeFormat object. |
124 | IntlDateTimeFormat* dtf = jsDynamicCast<IntlDateTimeFormat*>(vm, callFrame->thisValue()); |
125 | |
126 | // FIXME: Workaround to provide compatibility with ECMA-402 1.0 call/apply patterns. |
127 | // https://bugs.webkit.org/show_bug.cgi?id=153679 |
128 | if (!dtf) { |
129 | JSValue value = callFrame->thisValue().get(globalObject, vm.propertyNames->builtinNames().intlSubstituteValuePrivateName()); |
130 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
131 | dtf = jsDynamicCast<IntlDateTimeFormat*>(vm, value); |
132 | } |
133 | |
134 | // 2. ReturnIfAbrupt(dtf). |
135 | if (!dtf) |
136 | return JSValue::encode(throwTypeError(globalObject, scope, "Intl.DateTimeFormat.prototype.format called on value that's not an object initialized as a DateTimeFormat"_s )); |
137 | |
138 | JSBoundFunction* boundFormat = dtf->boundFormat(); |
139 | // 3. If the [[boundFormat]] internal slot of this DateTimeFormat object is undefined, |
140 | if (!boundFormat) { |
141 | JSGlobalObject* globalObject = dtf->globalObject(vm); |
142 | // a. Let F be a new built-in function object as defined in 12.3.4. |
143 | // b. The value of F’s length property is 1. (Note: F’s length property was 0 in ECMA-402 1.0) |
144 | JSFunction* targetObject = JSFunction::create(vm, globalObject, 1, "format"_s , IntlDateTimeFormatFuncFormatDateTime, NoIntrinsic); |
145 | // c. Let bf be BoundFunctionCreate(F, «this value»). |
146 | boundFormat = JSBoundFunction::create(vm, globalObject, targetObject, dtf, nullptr, 1, String()); |
147 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
148 | // d. Set dtf.[[boundFormat]] to bf. |
149 | dtf->setBoundFormat(vm, boundFormat); |
150 | } |
151 | // 4. Return dtf.[[boundFormat]]. |
152 | return JSValue::encode(boundFormat); |
153 | } |
154 | |
155 | #if JSC_ICU_HAS_UFIELDPOSITER |
156 | EncodedJSValue JSC_HOST_CALL IntlDateTimeFormatPrototypeFuncFormatToParts(JSGlobalObject* globalObject, CallFrame* callFrame) |
157 | { |
158 | VM& vm = globalObject->vm(); |
159 | auto scope = DECLARE_THROW_SCOPE(vm); |
160 | |
161 | // 15.4 Intl.DateTimeFormat.prototype.formatToParts (ECMA-402 4.0) |
162 | // https://tc39.github.io/ecma402/#sec-Intl.DateTimeFormat.prototype.formatToParts |
163 | |
164 | IntlDateTimeFormat* dateTimeFormat = jsDynamicCast<IntlDateTimeFormat*>(vm, callFrame->thisValue()); |
165 | if (!dateTimeFormat) |
166 | return JSValue::encode(throwTypeError(globalObject, scope, "Intl.DateTimeFormat.prototype.formatToParts called on value that's not an object initialized as a DateTimeFormat"_s )); |
167 | |
168 | JSValue date = callFrame->argument(0); |
169 | double value; |
170 | |
171 | if (date.isUndefined()) |
172 | value = dateNowImpl().toNumber(globalObject); |
173 | else { |
174 | value = WTF::timeClip(date.toNumber(globalObject)); |
175 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
176 | } |
177 | |
178 | RELEASE_AND_RETURN(scope, JSValue::encode(dateTimeFormat->formatToParts(globalObject, value))); |
179 | } |
180 | #endif |
181 | |
182 | EncodedJSValue JSC_HOST_CALL IntlDateTimeFormatPrototypeFuncResolvedOptions(JSGlobalObject* globalObject, CallFrame* callFrame) |
183 | { |
184 | VM& vm = globalObject->vm(); |
185 | auto scope = DECLARE_THROW_SCOPE(vm); |
186 | |
187 | // 12.3.5 Intl.DateTimeFormat.prototype.resolvedOptions() (ECMA-402 2.0) |
188 | IntlDateTimeFormat* dateTimeFormat = jsDynamicCast<IntlDateTimeFormat*>(vm, callFrame->thisValue()); |
189 | |
190 | // FIXME: Workaround to provide compatibility with ECMA-402 1.0 call/apply patterns. |
191 | // https://bugs.webkit.org/show_bug.cgi?id=153679 |
192 | if (!dateTimeFormat) { |
193 | JSValue value = callFrame->thisValue().get(globalObject, vm.propertyNames->builtinNames().intlSubstituteValuePrivateName()); |
194 | RETURN_IF_EXCEPTION(scope, encodedJSValue()); |
195 | dateTimeFormat = jsDynamicCast<IntlDateTimeFormat*>(vm, value); |
196 | } |
197 | |
198 | if (!dateTimeFormat) |
199 | return JSValue::encode(throwTypeError(globalObject, scope, "Intl.DateTimeFormat.prototype.resolvedOptions called on value that's not an object initialized as a DateTimeFormat"_s )); |
200 | |
201 | RELEASE_AND_RETURN(scope, JSValue::encode(dateTimeFormat->resolvedOptions(globalObject))); |
202 | } |
203 | |
204 | } // namespace JSC |
205 | |
206 | #endif // ENABLE(INTL) |
207 | |