IntlDateTimeFormatPrototype.cpp
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 #include "BuiltinNames.h" 31 #include "DateConstructor.h" 32 #include "IntlDateTimeFormatInlines.h" 33 #include "JSBoundFunction.h" 34 #include "JSCInlines.h" 35 #include <wtf/DateMath.h> 36 37 namespace JSC { 38 39 static JSC_DECLARE_HOST_FUNCTION(IntlDateTimeFormatPrototypeGetterFormat); 40 static JSC_DECLARE_HOST_FUNCTION(IntlDateTimeFormatPrototypeFuncFormatRange); 41 static JSC_DECLARE_HOST_FUNCTION(IntlDateTimeFormatPrototypeFuncFormatRangeToParts); 42 static JSC_DECLARE_HOST_FUNCTION(IntlDateTimeFormatPrototypeFuncFormatToParts); 43 static JSC_DECLARE_HOST_FUNCTION(IntlDateTimeFormatPrototypeFuncResolvedOptions); 44 static JSC_DECLARE_HOST_FUNCTION(IntlDateTimeFormatFuncFormatDateTime); 45 46 } 47 48 #include "IntlDateTimeFormatPrototype.lut.h" 49 50 namespace JSC { 51 52 const ClassInfo IntlDateTimeFormatPrototype::s_info = { "Intl.DateTimeFormat", &Base::s_info, &dateTimeFormatPrototypeTable, nullptr, CREATE_METHOD_TABLE(IntlDateTimeFormatPrototype) }; 53 54 /* Source for IntlDateTimeFormatPrototype.lut.h 55 @begin dateTimeFormatPrototypeTable 56 format IntlDateTimeFormatPrototypeGetterFormat DontEnum|Accessor 57 formatRange IntlDateTimeFormatPrototypeFuncFormatRange DontEnum|Function 2 58 formatToParts IntlDateTimeFormatPrototypeFuncFormatToParts DontEnum|Function 1 59 resolvedOptions IntlDateTimeFormatPrototypeFuncResolvedOptions DontEnum|Function 0 60 @end 61 */ 62 63 IntlDateTimeFormatPrototype* IntlDateTimeFormatPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure) 64 { 65 IntlDateTimeFormatPrototype* object = new (NotNull, allocateCell<IntlDateTimeFormatPrototype>(vm.heap)) IntlDateTimeFormatPrototype(vm, structure); 66 object->finishCreation(vm, globalObject); 67 return object; 68 } 69 70 Structure* IntlDateTimeFormatPrototype::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) 71 { 72 return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); 73 } 74 75 IntlDateTimeFormatPrototype::IntlDateTimeFormatPrototype(VM& vm, Structure* structure) 76 : Base(vm, structure) 77 { 78 } 79 80 void IntlDateTimeFormatPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) 81 { 82 Base::finishCreation(vm); 83 ASSERT(inherits(vm, info())); 84 #if HAVE(ICU_U_DATE_INTERVAL_FORMAT_FORMAT_RANGE_TO_PARTS) 85 if (Options::useIntlDateTimeFormatRangeToParts()) 86 JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("formatRangeToParts", IntlDateTimeFormatPrototypeFuncFormatRangeToParts, static_cast<unsigned>(PropertyAttribute::DontEnum), 2); 87 #else 88 UNUSED_PARAM(globalObject); 89 UNUSED_PARAM(&IntlDateTimeFormatPrototypeFuncFormatRangeToParts); 90 #endif 91 JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); 92 } 93 94 JSC_DEFINE_HOST_FUNCTION(IntlDateTimeFormatFuncFormatDateTime, (JSGlobalObject* globalObject, CallFrame* callFrame)) 95 { 96 VM& vm = globalObject->vm(); 97 auto scope = DECLARE_THROW_SCOPE(vm); 98 // 12.1.7 DateTime Format Functions (ECMA-402) 99 // https://tc39.github.io/ecma402/#sec-formatdatetime 100 101 IntlDateTimeFormat* format = jsCast<IntlDateTimeFormat*>(callFrame->thisValue()); 102 103 JSValue date = callFrame->argument(0); 104 double value; 105 106 if (date.isUndefined()) 107 value = dateNowImpl().toNumber(globalObject); 108 else { 109 value = WTF::timeClip(date.toNumber(globalObject)); 110 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 111 } 112 113 RELEASE_AND_RETURN(scope, JSValue::encode(format->format(globalObject, value))); 114 } 115 116 JSC_DEFINE_HOST_FUNCTION(IntlDateTimeFormatPrototypeGetterFormat, (JSGlobalObject* globalObject, CallFrame* callFrame)) 117 { 118 VM& vm = globalObject->vm(); 119 auto scope = DECLARE_THROW_SCOPE(vm); 120 121 // 12.3.3 Intl.DateTimeFormat.prototype.format (ECMA-402 2.0) 122 // 1. Let dtf be this DateTimeFormat object. 123 auto* dtf = IntlDateTimeFormat::unwrapForOldFunctions(globalObject, callFrame->thisValue()); 124 RETURN_IF_EXCEPTION(scope, { }); 125 // 2. ReturnIfAbrupt(dtf). 126 if (UNLIKELY(!dtf)) 127 return JSValue::encode(throwTypeError(globalObject, scope, "Intl.DateTimeFormat.prototype.format called on value that's not an object initialized as a DateTimeFormat"_s)); 128 129 JSBoundFunction* boundFormat = dtf->boundFormat(); 130 // 3. If the [[boundFormat]] internal slot of this DateTimeFormat object is undefined, 131 if (!boundFormat) { 132 JSGlobalObject* globalObject = dtf->globalObject(vm); 133 // a. Let F be a new built-in function object as defined in 12.3.4. 134 // b. The value of F’s length property is 1. (Note: F’s length property was 0 in ECMA-402 1.0) 135 JSFunction* targetObject = JSFunction::create(vm, globalObject, 1, "format"_s, IntlDateTimeFormatFuncFormatDateTime, NoIntrinsic); 136 // c. Let bf be BoundFunctionCreate(F, «this value»). 137 boundFormat = JSBoundFunction::create(vm, globalObject, targetObject, dtf, nullptr, 1, nullptr); 138 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 139 // d. Set dtf.[[boundFormat]] to bf. 140 dtf->setBoundFormat(vm, boundFormat); 141 } 142 // 4. Return dtf.[[boundFormat]]. 143 return JSValue::encode(boundFormat); 144 } 145 146 JSC_DEFINE_HOST_FUNCTION(IntlDateTimeFormatPrototypeFuncFormatToParts, (JSGlobalObject* globalObject, CallFrame* callFrame)) 147 { 148 VM& vm = globalObject->vm(); 149 auto scope = DECLARE_THROW_SCOPE(vm); 150 151 // 15.4 Intl.DateTimeFormat.prototype.formatToParts (ECMA-402 4.0) 152 // https://tc39.github.io/ecma402/#sec-Intl.DateTimeFormat.prototype.formatToParts 153 154 // Do not use unwrapForOldFunctions. 155 auto* dateTimeFormat = jsDynamicCast<IntlDateTimeFormat*>(vm, callFrame->thisValue()); 156 if (UNLIKELY(!dateTimeFormat)) 157 return JSValue::encode(throwTypeError(globalObject, scope, "Intl.DateTimeFormat.prototype.formatToParts called on value that's not an object initialized as a DateTimeFormat"_s)); 158 159 JSValue date = callFrame->argument(0); 160 double value; 161 162 if (date.isUndefined()) 163 value = dateNowImpl().toNumber(globalObject); 164 else { 165 value = WTF::timeClip(date.toNumber(globalObject)); 166 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 167 } 168 169 RELEASE_AND_RETURN(scope, JSValue::encode(dateTimeFormat->formatToParts(globalObject, value))); 170 } 171 172 // http://tc39.es/proposal-intl-DateTimeFormat-formatRange/#sec-intl.datetimeformat.prototype.formatRange 173 JSC_DEFINE_HOST_FUNCTION(IntlDateTimeFormatPrototypeFuncFormatRange, (JSGlobalObject* globalObject, CallFrame* callFrame)) 174 { 175 VM& vm = globalObject->vm(); 176 auto scope = DECLARE_THROW_SCOPE(vm); 177 178 // Do not use unwrapForOldFunctions. 179 auto* dateTimeFormat = jsDynamicCast<IntlDateTimeFormat*>(vm, callFrame->thisValue()); 180 if (UNLIKELY(!dateTimeFormat)) 181 return JSValue::encode(throwTypeError(globalObject, scope, "Intl.DateTimeFormat.prototype.formatRange called on value that's not an object initialized as a DateTimeFormat"_s)); 182 183 JSValue startDateValue = callFrame->argument(0); 184 JSValue endDateValue = callFrame->argument(1); 185 186 if (startDateValue.isUndefined() || endDateValue.isUndefined()) 187 return throwVMTypeError(globalObject, scope, "startDate or endDate is undefined"_s); 188 189 double startDate = startDateValue.toNumber(globalObject); 190 RETURN_IF_EXCEPTION(scope, { }); 191 double endDate = endDateValue.toNumber(globalObject); 192 RETURN_IF_EXCEPTION(scope, { }); 193 if (startDate > endDate) 194 return throwVMRangeError(globalObject, scope, "startDate is larger than endDate"_s); 195 196 RELEASE_AND_RETURN(scope, JSValue::encode(dateTimeFormat->formatRange(globalObject, startDate, endDate))); 197 } 198 199 // http://tc39.es/proposal-intl-DateTimeFormat-formatRange/#sec-intl.datetimeformat.prototype.formatRangeToParts 200 JSC_DEFINE_HOST_FUNCTION(IntlDateTimeFormatPrototypeFuncFormatRangeToParts, (JSGlobalObject* globalObject, CallFrame* callFrame)) 201 { 202 VM& vm = globalObject->vm(); 203 auto scope = DECLARE_THROW_SCOPE(vm); 204 205 // Do not use unwrapForOldFunctions. 206 auto* dateTimeFormat = jsDynamicCast<IntlDateTimeFormat*>(vm, callFrame->thisValue()); 207 if (UNLIKELY(!dateTimeFormat)) 208 return JSValue::encode(throwTypeError(globalObject, scope, "Intl.DateTimeFormat.prototype.formatRangeToParts called on value that's not an object initialized as a DateTimeFormat"_s)); 209 210 JSValue startDateValue = callFrame->argument(0); 211 JSValue endDateValue = callFrame->argument(1); 212 213 if (startDateValue.isUndefined() || endDateValue.isUndefined()) 214 return throwVMTypeError(globalObject, scope, "startDate or endDate is undefined"_s); 215 216 double startDate = startDateValue.toNumber(globalObject); 217 RETURN_IF_EXCEPTION(scope, { }); 218 double endDate = endDateValue.toNumber(globalObject); 219 RETURN_IF_EXCEPTION(scope, { }); 220 if (startDate > endDate) 221 return throwVMRangeError(globalObject, scope, "startDate is larger than endDate"_s); 222 223 RELEASE_AND_RETURN(scope, JSValue::encode(dateTimeFormat->formatRangeToParts(globalObject, startDate, endDate))); 224 } 225 226 JSC_DEFINE_HOST_FUNCTION(IntlDateTimeFormatPrototypeFuncResolvedOptions, (JSGlobalObject* globalObject, CallFrame* callFrame)) 227 { 228 VM& vm = globalObject->vm(); 229 auto scope = DECLARE_THROW_SCOPE(vm); 230 231 // 12.3.5 Intl.DateTimeFormat.prototype.resolvedOptions() (ECMA-402 2.0) 232 233 auto* dateTimeFormat = IntlDateTimeFormat::unwrapForOldFunctions(globalObject, callFrame->thisValue()); 234 RETURN_IF_EXCEPTION(scope, { }); 235 if (UNLIKELY(!dateTimeFormat)) 236 return JSValue::encode(throwTypeError(globalObject, scope, "Intl.DateTimeFormat.prototype.resolvedOptions called on value that's not an object initialized as a DateTimeFormat"_s)); 237 238 RELEASE_AND_RETURN(scope, JSValue::encode(dateTimeFormat->resolvedOptions(globalObject))); 239 } 240 241 } // namespace JSC