/ runtime / IntlDateTimeFormatPrototype.cpp
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