Move i18n extension's date-format C++ code to runtime
authorjochen@chromium.org <jochen@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 7 Aug 2013 03:40:44 +0000 (03:40 +0000)
committerjochen@chromium.org <jochen@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 7 Aug 2013 03:40:44 +0000 (03:40 +0000)
BUG=v8:2745
R=dcarney@chromium.org, mstarzinger@chromium.org

Review URL: https://codereview.chromium.org/22411003

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16086 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/extensions/i18n/date-format.cc [deleted file]
src/extensions/i18n/date-format.js
src/extensions/i18n/i18n-extension.cc
src/i18n.cc [new file with mode: 0644]
src/i18n.h [moved from src/extensions/i18n/date-format.h with 69% similarity]
src/runtime.cc
src/runtime.h
tools/gyp/v8.gyp

diff --git a/src/extensions/i18n/date-format.cc b/src/extensions/i18n/date-format.cc
deleted file mode 100644 (file)
index 1058e37..0000000
+++ /dev/null
@@ -1,329 +0,0 @@
-// Copyright 2013 the V8 project authors. All rights reserved.
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright
-//       notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-//       copyright notice, this list of conditions and the following
-//       disclaimer in the documentation and/or other materials provided
-//       with the distribution.
-//     * Neither the name of Google Inc. nor the names of its
-//       contributors may be used to endorse or promote products derived
-//       from this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-// limitations under the License.
-
-#include "date-format.h"
-
-#include <string.h>
-
-#include "i18n-utils.h"
-#include "unicode/calendar.h"
-#include "unicode/dtfmtsym.h"
-#include "unicode/dtptngen.h"
-#include "unicode/locid.h"
-#include "unicode/numsys.h"
-#include "unicode/smpdtfmt.h"
-#include "unicode/timezone.h"
-
-namespace v8_i18n {
-
-static icu::SimpleDateFormat* InitializeDateTimeFormat(v8::Handle<v8::String>,
-                                                       v8::Handle<v8::Object>,
-                                                       v8::Handle<v8::Object>);
-static icu::SimpleDateFormat* CreateICUDateFormat(const icu::Locale&,
-                                                  v8::Handle<v8::Object>);
-static void SetResolvedSettings(const icu::Locale&,
-                                icu::SimpleDateFormat*,
-                                v8::Handle<v8::Object>);
-
-icu::SimpleDateFormat* DateFormat::UnpackDateFormat(
-    v8::Handle<v8::Object> obj) {
-  v8::HandleScope handle_scope;
-
-  if (obj->HasOwnProperty(v8::String::New("dateFormat"))) {
-    return static_cast<icu::SimpleDateFormat*>(
-        obj->GetAlignedPointerFromInternalField(0));
-  }
-
-  return NULL;
-}
-
-void DateFormat::DeleteDateFormat(v8::Isolate* isolate,
-                                  v8::Persistent<v8::Object>* object,
-                                  void* param) {
-  // First delete the hidden C++ object.
-  // Unpacking should never return NULL here. That would only happen if
-  // this method is used as the weak callback for persistent handles not
-  // pointing to a date time formatter.
-  v8::HandleScope handle_scope(isolate);
-  v8::Local<v8::Object> handle = v8::Local<v8::Object>::New(isolate, *object);
-  delete UnpackDateFormat(handle);
-
-  // Then dispose of the persistent handle to JS object.
-  object->Dispose(isolate);
-}
-
-void DateFormat::JSInternalFormat(
-    const v8::FunctionCallbackInfo<v8::Value>& args) {
-  double millis = 0.0;
-  if (args.Length() != 2 || !args[0]->IsObject() || !args[1]->IsDate()) {
-    v8::ThrowException(v8::Exception::Error(
-        v8::String::New(
-            "Internal error. Formatter and date value have to be specified.")));
-    return;
-  } else {
-    millis = v8::Date::Cast(*args[1])->NumberValue();
-  }
-
-  icu::SimpleDateFormat* date_format = UnpackDateFormat(args[0]->ToObject());
-  if (!date_format) {
-    v8::ThrowException(v8::Exception::Error(
-        v8::String::New("DateTimeFormat method called on an object "
-                        "that is not a DateTimeFormat.")));
-    return;
-  }
-
-  icu::UnicodeString result;
-  date_format->format(millis, result);
-
-  args.GetReturnValue().Set(v8::String::New(
-      reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length()));
-}
-
-void DateFormat::JSInternalParse(
-    const v8::FunctionCallbackInfo<v8::Value>& args) {
-  icu::UnicodeString string_date;
-  if (args.Length() != 2 || !args[0]->IsObject() || !args[1]->IsString()) {
-    v8::ThrowException(v8::Exception::Error(
-        v8::String::New(
-            "Internal error. Formatter and string have to be specified.")));
-    return;
-  } else {
-    if (!Utils::V8StringToUnicodeString(args[1], &string_date)) {
-      string_date = "";
-    }
-  }
-
-  icu::SimpleDateFormat* date_format = UnpackDateFormat(args[0]->ToObject());
-  if (!date_format) {
-    v8::ThrowException(v8::Exception::Error(
-        v8::String::New("DateTimeFormat method called on an object "
-                        "that is not a DateTimeFormat.")));
-    return;
-  }
-
-  UErrorCode status = U_ZERO_ERROR;
-  UDate date = date_format->parse(string_date, status);
-  if (U_FAILURE(status)) {
-    return;
-  }
-
-  args.GetReturnValue().Set(v8::Date::New(static_cast<double>(date)));
-}
-
-void DateFormat::JSCreateDateTimeFormat(
-    const v8::FunctionCallbackInfo<v8::Value>& args) {
-  if (args.Length() != 3 ||
-      !args[0]->IsString() ||
-      !args[1]->IsObject() ||
-      !args[2]->IsObject()) {
-    v8::ThrowException(v8::Exception::Error(
-        v8::String::New("Internal error, wrong parameters.")));
-    return;
-  }
-
-  v8::Isolate* isolate = args.GetIsolate();
-  v8::Local<v8::ObjectTemplate> date_format_template =
-      Utils::GetTemplate(isolate);
-
-  // Create an empty object wrapper.
-  v8::Local<v8::Object> local_object = date_format_template->NewInstance();
-  // But the handle shouldn't be empty.
-  // That can happen if there was a stack overflow when creating the object.
-  if (local_object.IsEmpty()) {
-    args.GetReturnValue().Set(local_object);
-    return;
-  }
-
-  // Set date time formatter as internal field of the resulting JS object.
-  icu::SimpleDateFormat* date_format = InitializeDateTimeFormat(
-      args[0]->ToString(), args[1]->ToObject(), args[2]->ToObject());
-
-  if (!date_format) {
-    v8::ThrowException(v8::Exception::Error(v8::String::New(
-        "Internal error. Couldn't create ICU date time formatter.")));
-    return;
-  } else {
-    local_object->SetAlignedPointerInInternalField(0, date_format);
-
-    v8::TryCatch try_catch;
-    local_object->Set(v8::String::New("dateFormat"), v8::String::New("valid"));
-    if (try_catch.HasCaught()) {
-      v8::ThrowException(v8::Exception::Error(
-          v8::String::New("Internal error, couldn't set property.")));
-      return;
-    }
-  }
-
-  v8::Persistent<v8::Object> wrapper(isolate, local_object);
-  // Make object handle weak so we can delete iterator once GC kicks in.
-  wrapper.MakeWeak<void>(NULL, &DeleteDateFormat);
-  args.GetReturnValue().Set(wrapper);
-  wrapper.ClearAndLeak();
-}
-
-static icu::SimpleDateFormat* InitializeDateTimeFormat(
-    v8::Handle<v8::String> locale,
-    v8::Handle<v8::Object> options,
-    v8::Handle<v8::Object> resolved) {
-  // Convert BCP47 into ICU locale format.
-  UErrorCode status = U_ZERO_ERROR;
-  icu::Locale icu_locale;
-  char icu_result[ULOC_FULLNAME_CAPACITY];
-  int icu_length = 0;
-  v8::String::AsciiValue bcp47_locale(locale);
-  if (bcp47_locale.length() != 0) {
-    uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
-                        &icu_length, &status);
-    if (U_FAILURE(status) || icu_length == 0) {
-      return NULL;
-    }
-    icu_locale = icu::Locale(icu_result);
-  }
-
-  icu::SimpleDateFormat* date_format = CreateICUDateFormat(icu_locale, options);
-  if (!date_format) {
-    // Remove extensions and try again.
-    icu::Locale no_extension_locale(icu_locale.getBaseName());
-    date_format = CreateICUDateFormat(no_extension_locale, options);
-
-    // Set resolved settings (pattern, numbering system, calendar).
-    SetResolvedSettings(no_extension_locale, date_format, resolved);
-  } else {
-    SetResolvedSettings(icu_locale, date_format, resolved);
-  }
-
-  return date_format;
-}
-
-static icu::SimpleDateFormat* CreateICUDateFormat(
-    const icu::Locale& icu_locale, v8::Handle<v8::Object> options) {
-  // Create time zone as specified by the user. We have to re-create time zone
-  // since calendar takes ownership.
-  icu::TimeZone* tz = NULL;
-  icu::UnicodeString timezone;
-  if (Utils::ExtractStringSetting(options, "timeZone", &timezone)) {
-    tz = icu::TimeZone::createTimeZone(timezone);
-  } else {
-    tz = icu::TimeZone::createDefault();
-  }
-
-  // Create a calendar using locale, and apply time zone to it.
-  UErrorCode status = U_ZERO_ERROR;
-  icu::Calendar* calendar =
-      icu::Calendar::createInstance(tz, icu_locale, status);
-
-  // Make formatter from skeleton. Calendar and numbering system are added
-  // to the locale as Unicode extension (if they were specified at all).
-  icu::SimpleDateFormat* date_format = NULL;
-  icu::UnicodeString skeleton;
-  if (Utils::ExtractStringSetting(options, "skeleton", &skeleton)) {
-    icu::DateTimePatternGenerator* generator =
-        icu::DateTimePatternGenerator::createInstance(icu_locale, status);
-    icu::UnicodeString pattern;
-    if (U_SUCCESS(status)) {
-      pattern = generator->getBestPattern(skeleton, status);
-      delete generator;
-    }
-
-    date_format = new icu::SimpleDateFormat(pattern, icu_locale, status);
-    if (U_SUCCESS(status)) {
-      date_format->adoptCalendar(calendar);
-    }
-  }
-
-  if (U_FAILURE(status)) {
-    delete calendar;
-    delete date_format;
-    date_format = NULL;
-  }
-
-  return date_format;
-}
-
-static void SetResolvedSettings(const icu::Locale& icu_locale,
-                                icu::SimpleDateFormat* date_format,
-                                v8::Handle<v8::Object> resolved) {
-  UErrorCode status = U_ZERO_ERROR;
-  icu::UnicodeString pattern;
-  date_format->toPattern(pattern);
-  resolved->Set(v8::String::New("pattern"),
-                v8::String::New(reinterpret_cast<const uint16_t*>(
-                    pattern.getBuffer()), pattern.length()));
-
-  // Set time zone and calendar.
-  if (date_format) {
-    const icu::Calendar* calendar = date_format->getCalendar();
-    const char* calendar_name = calendar->getType();
-    resolved->Set(v8::String::New("calendar"), v8::String::New(calendar_name));
-
-    const icu::TimeZone& tz = calendar->getTimeZone();
-    icu::UnicodeString time_zone;
-    tz.getID(time_zone);
-
-    icu::UnicodeString canonical_time_zone;
-    icu::TimeZone::getCanonicalID(time_zone, canonical_time_zone, status);
-    if (U_SUCCESS(status)) {
-      if (canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/GMT")) {
-        resolved->Set(v8::String::New("timeZone"), v8::String::New("UTC"));
-      } else {
-        resolved->Set(v8::String::New("timeZone"),
-                      v8::String::New(reinterpret_cast<const uint16_t*>(
-                          canonical_time_zone.getBuffer()),
-                                      canonical_time_zone.length()));
-      }
-    }
-  }
-
-  // Ugly hack. ICU doesn't expose numbering system in any way, so we have
-  // to assume that for given locale NumberingSystem constructor produces the
-  // same digits as NumberFormat/Calendar would.
-  status = U_ZERO_ERROR;
-  icu::NumberingSystem* numbering_system =
-      icu::NumberingSystem::createInstance(icu_locale, status);
-  if (U_SUCCESS(status)) {
-    const char* ns = numbering_system->getName();
-    resolved->Set(v8::String::New("numberingSystem"), v8::String::New(ns));
-  } else {
-    resolved->Set(v8::String::New("numberingSystem"), v8::Undefined());
-  }
-  delete numbering_system;
-
-  // Set the locale
-  char result[ULOC_FULLNAME_CAPACITY];
-  status = U_ZERO_ERROR;
-  uloc_toLanguageTag(
-      icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
-  if (U_SUCCESS(status)) {
-    resolved->Set(v8::String::New("locale"), v8::String::New(result));
-  } else {
-    // This would never happen, since we got the locale from ICU.
-    resolved->Set(v8::String::New("locale"), v8::String::New("und"));
-  }
-}
-
-}  // namespace v8_i18n
index 04e7a7c..b1d28e5 100644 (file)
@@ -235,7 +235,6 @@ function toDateTimeOptions(options, required, defaults) {
  * Useful for subclassing.
  */
 function initializeDateTimeFormat(dateFormat, locales, options) {
-  native function NativeJSCreateDateTimeFormat();
 
   if (dateFormat.hasOwnProperty('__initializedIntlObject')) {
     throw new TypeError('Trying to re-initialize DateTimeFormat object.');
@@ -292,7 +291,7 @@ function initializeDateTimeFormat(dateFormat, locales, options) {
     year: {writable: true}
   });
 
-  var formatter = NativeJSCreateDateTimeFormat(
+  var formatter = %CreateDateTimeFormat(
     requestedLocale, {skeleton: ldmlString, timeZone: tz}, resolved);
 
   if (tz !== undefined && tz !== resolved.timeZone) {
@@ -409,8 +408,6 @@ function initializeDateTimeFormat(dateFormat, locales, options) {
  * DateTimeFormat.
  */
 function formatDate(formatter, dateValue) {
-  native function NativeJSInternalDateFormat();
-
   var dateMs;
   if (dateValue === undefined) {
     dateMs = Date.now();
@@ -422,7 +419,7 @@ function formatDate(formatter, dateValue) {
     throw new RangeError('Provided date is not in valid range.');
   }
 
-  return NativeJSInternalDateFormat(formatter.formatter, new Date(dateMs));
+  return %InternalDateFormat(formatter.formatter, new Date(dateMs));
 }
 
 
@@ -433,8 +430,7 @@ function formatDate(formatter, dateValue) {
  * Returns undefined if date string cannot be parsed.
  */
 function parseDate(formatter, value) {
-  native function NativeJSInternalDateParse();
-  return NativeJSInternalDateParse(formatter.formatter, String(value));
+  return %InternalDateParse(formatter.formatter, String(value));
 }
 
 
index 01fe526..b110b7d 100644 (file)
@@ -30,7 +30,6 @@
 
 #include "break-iterator.h"
 #include "collator.h"
-#include "date-format.h"
 #include "natives.h"
 #include "number-format.h"
 
@@ -48,15 +47,6 @@ Extension::Extension()
 
 v8::Handle<v8::FunctionTemplate> Extension::GetNativeFunction(
     v8::Handle<v8::String> name) {
-  // Date format and parse.
-  if (name->Equals(v8::String::New("NativeJSCreateDateTimeFormat"))) {
-    return v8::FunctionTemplate::New(DateFormat::JSCreateDateTimeFormat);
-  } else if (name->Equals(v8::String::New("NativeJSInternalDateFormat"))) {
-    return v8::FunctionTemplate::New(DateFormat::JSInternalFormat);
-  } else if (name->Equals(v8::String::New("NativeJSInternalDateParse"))) {
-    return v8::FunctionTemplate::New(DateFormat::JSInternalParse);
-  }
-
   // Number format and parse.
   if (name->Equals(v8::String::New("NativeJSCreateNumberFormat"))) {
     return v8::FunctionTemplate::New(NumberFormat::JSCreateNumberFormat);
diff --git a/src/i18n.cc b/src/i18n.cc
new file mode 100644 (file)
index 0000000..b2ccfd4
--- /dev/null
@@ -0,0 +1,297 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// limitations under the License.
+
+#include "i18n.h"
+
+#include "unicode/calendar.h"
+#include "unicode/dtfmtsym.h"
+#include "unicode/dtptngen.h"
+#include "unicode/locid.h"
+#include "unicode/numsys.h"
+#include "unicode/smpdtfmt.h"
+#include "unicode/timezone.h"
+
+namespace v8 {
+namespace internal {
+
+namespace {
+
+icu::SimpleDateFormat* CreateICUDateFormat(
+    Isolate* isolate,
+    const icu::Locale& icu_locale,
+    Handle<Object> options) {
+  // Create time zone as specified by the user. We have to re-create time zone
+  // since calendar takes ownership.
+  icu::TimeZone* tz = NULL;
+  MaybeObject* maybe_object = options->GetProperty(
+      *isolate->factory()->NewStringFromAscii(CStrVector("timeZone")));
+  Object* timezone;
+  if (maybe_object->ToObject(&timezone) && timezone->IsString()) {
+    v8::String::Utf8Value utf8_timezone(
+        v8::Utils::ToLocal(Handle<String>(String::cast(timezone))));
+    icu::UnicodeString u_timezone(icu::UnicodeString::fromUTF8(*utf8_timezone));
+    tz = icu::TimeZone::createTimeZone(u_timezone);
+  } else {
+    tz = icu::TimeZone::createDefault();
+  }
+
+  // Create a calendar using locale, and apply time zone to it.
+  UErrorCode status = U_ZERO_ERROR;
+  icu::Calendar* calendar =
+      icu::Calendar::createInstance(tz, icu_locale, status);
+
+  // Make formatter from skeleton. Calendar and numbering system are added
+  // to the locale as Unicode extension (if they were specified at all).
+  icu::SimpleDateFormat* date_format = NULL;
+  Object* skeleton;
+  maybe_object = options->GetProperty(
+      *isolate->factory()->NewStringFromAscii(CStrVector("skeleton")));
+  if (maybe_object->ToObject(&skeleton) && skeleton->IsString()) {
+    v8::String::Utf8Value utf8_skeleton(
+        v8::Utils::ToLocal(Handle<String>(String::cast(skeleton))));
+    icu::UnicodeString u_skeleton(icu::UnicodeString::fromUTF8(*utf8_skeleton));
+    icu::DateTimePatternGenerator* generator =
+        icu::DateTimePatternGenerator::createInstance(icu_locale, status);
+    icu::UnicodeString pattern;
+    if (U_SUCCESS(status)) {
+      pattern = generator->getBestPattern(u_skeleton, status);
+      delete generator;
+    }
+
+    date_format = new icu::SimpleDateFormat(pattern, icu_locale, status);
+    if (U_SUCCESS(status)) {
+      date_format->adoptCalendar(calendar);
+    }
+  }
+
+  if (U_FAILURE(status)) {
+    delete calendar;
+    delete date_format;
+    date_format = NULL;
+  }
+
+  return date_format;
+}
+
+
+void SetResolvedSettings(Isolate* isolate,
+                         const icu::Locale& icu_locale,
+                         icu::SimpleDateFormat* date_format,
+                         Handle<JSObject> resolved) {
+  UErrorCode status = U_ZERO_ERROR;
+  icu::UnicodeString pattern;
+  date_format->toPattern(pattern);
+  JSObject::SetProperty(
+      resolved,
+      isolate->factory()->NewStringFromAscii(CStrVector("pattern")),
+      isolate->factory()->NewStringFromTwoByte(
+        Vector<const uint16_t>(
+            reinterpret_cast<const uint16_t*>(pattern.getBuffer()),
+            pattern.length())),
+      NONE,
+      kNonStrictMode);
+
+  // Set time zone and calendar.
+  const icu::Calendar* calendar = date_format->getCalendar();
+  const char* calendar_name = calendar->getType();
+  JSObject::SetProperty(
+      resolved,
+      isolate->factory()->NewStringFromAscii(CStrVector("calendar")),
+      isolate->factory()->NewStringFromAscii(CStrVector(calendar_name)),
+      NONE,
+      kNonStrictMode);
+
+  const icu::TimeZone& tz = calendar->getTimeZone();
+  icu::UnicodeString time_zone;
+  tz.getID(time_zone);
+
+  icu::UnicodeString canonical_time_zone;
+  icu::TimeZone::getCanonicalID(time_zone, canonical_time_zone, status);
+  if (U_SUCCESS(status)) {
+    if (canonical_time_zone == UNICODE_STRING_SIMPLE("Etc/GMT")) {
+      JSObject::SetProperty(
+          resolved,
+          isolate->factory()->NewStringFromAscii(CStrVector("timeZone")),
+          isolate->factory()->NewStringFromAscii(CStrVector("UTC")),
+          NONE,
+          kNonStrictMode);
+    } else {
+      JSObject::SetProperty(
+          resolved,
+          isolate->factory()->NewStringFromAscii(CStrVector("timeZone")),
+          isolate->factory()->NewStringFromTwoByte(
+            Vector<const uint16_t>(
+                reinterpret_cast<const uint16_t*>(
+                    canonical_time_zone.getBuffer()),
+                canonical_time_zone.length())),
+          NONE,
+          kNonStrictMode);
+    }
+  }
+
+  // Ugly hack. ICU doesn't expose numbering system in any way, so we have
+  // to assume that for given locale NumberingSystem constructor produces the
+  // same digits as NumberFormat/Calendar would.
+  status = U_ZERO_ERROR;
+  icu::NumberingSystem* numbering_system =
+      icu::NumberingSystem::createInstance(icu_locale, status);
+  if (U_SUCCESS(status)) {
+    const char* ns = numbering_system->getName();
+    JSObject::SetProperty(
+        resolved,
+        isolate->factory()->NewStringFromAscii(CStrVector("numberingSystem")),
+        isolate->factory()->NewStringFromAscii(CStrVector(ns)),
+        NONE,
+        kNonStrictMode);
+  } else {
+    JSObject::SetProperty(
+        resolved,
+        isolate->factory()->NewStringFromAscii(CStrVector("numberingSystem")),
+        isolate->factory()->undefined_value(),
+        NONE,
+        kNonStrictMode);
+  }
+  delete numbering_system;
+
+  // Set the locale
+  char result[ULOC_FULLNAME_CAPACITY];
+  status = U_ZERO_ERROR;
+  uloc_toLanguageTag(
+      icu_locale.getName(), result, ULOC_FULLNAME_CAPACITY, FALSE, &status);
+  if (U_SUCCESS(status)) {
+    JSObject::SetProperty(
+        resolved,
+        isolate->factory()->NewStringFromAscii(CStrVector("locale")),
+        isolate->factory()->NewStringFromAscii(CStrVector(result)),
+        NONE,
+        kNonStrictMode);
+  } else {
+    // This would never happen, since we got the locale from ICU.
+    JSObject::SetProperty(
+        resolved,
+        isolate->factory()->NewStringFromAscii(CStrVector("locale")),
+        isolate->factory()->NewStringFromAscii(CStrVector("und")),
+        NONE,
+        kNonStrictMode);
+  }
+}
+
+
+template<int internal_fields, EternalHandles::SingletonHandle field>
+Handle<ObjectTemplateInfo> GetEternal(Isolate* isolate) {
+  if (isolate->eternal_handles()->Exists(field)) {
+    return Handle<ObjectTemplateInfo>::cast(
+        isolate->eternal_handles()->GetSingleton(field));
+  }
+  v8::Local<v8::ObjectTemplate> raw_template(v8::ObjectTemplate::New());
+  raw_template->SetInternalFieldCount(internal_fields);
+  return Handle<ObjectTemplateInfo>::cast(
+      isolate->eternal_handles()->CreateSingleton(
+        isolate,
+        *v8::Utils::OpenHandle(*raw_template),
+        field));
+}
+
+}  // namespace
+
+
+// static
+Handle<ObjectTemplateInfo> I18N::GetTemplate(Isolate* isolate) {
+  return GetEternal<1, i::EternalHandles::I18N_TEMPLATE_ONE>(isolate);
+}
+
+
+// static
+Handle<ObjectTemplateInfo> I18N::GetTemplate2(Isolate* isolate) {
+  return GetEternal<2, i::EternalHandles::I18N_TEMPLATE_TWO>(isolate);
+}
+
+
+// static
+icu::SimpleDateFormat* DateFormat::InitializeDateTimeFormat(
+    Isolate* isolate,
+    Handle<String> locale,
+    Handle<JSObject> options,
+    Handle<JSObject> resolved) {
+  // Convert BCP47 into ICU locale format.
+  UErrorCode status = U_ZERO_ERROR;
+  icu::Locale icu_locale;
+  char icu_result[ULOC_FULLNAME_CAPACITY];
+  int icu_length = 0;
+  v8::String::Utf8Value bcp47_locale(v8::Utils::ToLocal(locale));
+  if (bcp47_locale.length() != 0) {
+    uloc_forLanguageTag(*bcp47_locale, icu_result, ULOC_FULLNAME_CAPACITY,
+                        &icu_length, &status);
+    if (U_FAILURE(status) || icu_length == 0) {
+      return NULL;
+    }
+    icu_locale = icu::Locale(icu_result);
+  }
+
+  icu::SimpleDateFormat* date_format = CreateICUDateFormat(
+      isolate, icu_locale, options);
+  if (!date_format) {
+    // Remove extensions and try again.
+    icu::Locale no_extension_locale(icu_locale.getBaseName());
+    date_format = CreateICUDateFormat(isolate, no_extension_locale, options);
+
+    // Set resolved settings (pattern, numbering system, calendar).
+    SetResolvedSettings(isolate, no_extension_locale, date_format, resolved);
+  } else {
+    SetResolvedSettings(isolate, icu_locale, date_format, resolved);
+  }
+
+  return date_format;
+}
+
+
+icu::SimpleDateFormat* DateFormat::UnpackDateFormat(
+    Isolate* isolate,
+    Handle<JSObject> obj) {
+  if (obj->HasLocalProperty(
+          *isolate->factory()->NewStringFromAscii(CStrVector("dateFormat")))) {
+    return reinterpret_cast<icu::SimpleDateFormat*>(
+        obj->GetInternalField(0));
+  }
+
+  return NULL;
+}
+
+
+void DateFormat::DeleteDateFormat(v8::Isolate* isolate,
+                                  Persistent<v8::Object>* object,
+                                  void* param) {
+  // First delete the hidden C++ object.
+  delete reinterpret_cast<icu::SimpleDateFormat*>(Handle<JSObject>::cast(
+      v8::Utils::OpenPersistent(object))->GetInternalField(0));
+
+  // Then dispose of the persistent handle to JS object.
+  object->Dispose(isolate);
+}
+
+} }  // namespace v8::internal
similarity index 69%
rename from src/extensions/i18n/date-format.h
rename to src/i18n.h
index daa5964..37c57b1 100644 (file)
@@ -26,8 +26,8 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 // limitations under the License.
 
-#ifndef V8_EXTENSIONS_I18N_DATE_FORMAT_H_
-#define V8_EXTENSIONS_I18N_DATE_FORMAT_H_
+#ifndef V8_I18N_H_
+#define V8_I18N_H_
 
 #include "unicode/uversion.h"
 #include "v8.h"
@@ -36,36 +36,44 @@ namespace U_ICU_NAMESPACE {
 class SimpleDateFormat;
 }
 
-namespace v8_i18n {
+namespace v8 {
+namespace internal {
 
-class DateFormat {
+class I18N {
  public:
-  static void JSCreateDateTimeFormat(
-      const v8::FunctionCallbackInfo<v8::Value>& args);
+  // Creates an ObjectTemplate with one internal field.
+  static Handle<ObjectTemplateInfo> GetTemplate(Isolate* isolate);
+
+  // Creates an ObjectTemplate with two internal fields.
+  static Handle<ObjectTemplateInfo> GetTemplate2(Isolate* isolate);
+
+ private:
+  I18N();
+};
 
-  // Helper methods for various bindings.
+class DateFormat {
+ public:
+  // Create a formatter for the specificied locale and options. Returns the
+  // resolved settings for the locale / options.
+  static icu::SimpleDateFormat* InitializeDateTimeFormat(
+      Isolate* isolate,
+      Handle<String> locale,
+      Handle<JSObject> options,
+      Handle<JSObject> resolved);
 
   // Unpacks date format object from corresponding JavaScript object.
-  static icu::SimpleDateFormat* UnpackDateFormat(
-      v8::Handle<v8::Object> obj);
+  static icu::SimpleDateFormat* UnpackDateFormat(Isolate* isolate,
+                                                 Handle<JSObject> obj);
 
   // Release memory we allocated for the DateFormat once the JS object that
   // holds the pointer gets garbage collected.
   static void DeleteDateFormat(v8::Isolate* isolate,
-                               v8::Persistent<v8::Object>* object,
+                               Persistent<v8::Object>* object,
                                void* param);
-
-  // Formats date and returns corresponding string.
-  static void JSInternalFormat(const v8::FunctionCallbackInfo<v8::Value>& args);
-
-  // Parses date and returns corresponding Date object or undefined if parse
-  // failed.
-  static void JSInternalParse(const v8::FunctionCallbackInfo<v8::Value>& args);
-
  private:
   DateFormat();
 };
 
-}  // namespace v8_i18n
+} }  // namespace v8::internal
 
-#endif  // V8_EXTENSIONS_I18N_DATE_FORMAT_H_
+#endif  // V8_I18N_H_
index c9d3f49..0916b93 100644 (file)
 #include "vm-state-inl.h"
 
 #ifdef V8_I18N_SUPPORT
+#include "i18n.h"
 #include "unicode/brkiter.h"
+#include "unicode/calendar.h"
 #include "unicode/coll.h"
 #include "unicode/datefmt.h"
+#include "unicode/dtfmtsym.h"
+#include "unicode/dtptngen.h"
+#include "unicode/locid.h"
 #include "unicode/numfmt.h"
+#include "unicode/numsys.h"
+#include "unicode/smpdtfmt.h"
+#include "unicode/timezone.h"
 #include "unicode/uloc.h"
 #include "unicode/uversion.h"
 #endif
@@ -13553,6 +13561,110 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetLanguageTagVariants) {
   result->set_length(Smi::FromInt(length));
   return *result;
 }
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateDateTimeFormat) {
+  HandleScope scope(isolate);
+
+  ASSERT(args.length() == 3);
+
+  CONVERT_ARG_HANDLE_CHECKED(String, locale, 0);
+  CONVERT_ARG_HANDLE_CHECKED(JSObject, options, 1);
+  CONVERT_ARG_HANDLE_CHECKED(JSObject, resolved, 2);
+
+  Handle<ObjectTemplateInfo> date_format_template =
+      I18N::GetTemplate(isolate);
+
+  // Create an empty object wrapper.
+  bool has_pending_exception = false;
+  Handle<JSObject> local_object = Execution::InstantiateObject(
+      date_format_template, &has_pending_exception);
+  if (has_pending_exception) {
+    ASSERT(isolate->has_pending_exception());
+    return Failure::Exception();
+  }
+
+  // Set date time formatter as internal field of the resulting JS object.
+  icu::SimpleDateFormat* date_format = DateFormat::InitializeDateTimeFormat(
+      isolate, locale, options, resolved);
+
+  if (!date_format) return isolate->ThrowIllegalOperation();
+
+  local_object->SetInternalField(0, reinterpret_cast<Smi*>(date_format));
+
+  RETURN_IF_EMPTY_HANDLE(isolate,
+      JSObject::SetLocalPropertyIgnoreAttributes(
+          local_object,
+          isolate->factory()->NewStringFromAscii(CStrVector("dateFormat")),
+          isolate->factory()->NewStringFromAscii(CStrVector("valid")),
+          NONE));
+
+  Persistent<v8::Object> wrapper(reinterpret_cast<v8::Isolate*>(isolate),
+                                 v8::Utils::ToLocal(local_object));
+  // Make object handle weak so we can delete the data format once GC kicks in.
+  wrapper.MakeWeak<void>(NULL, &DateFormat::DeleteDateFormat);
+  Handle<Object> result = Utils::OpenPersistent(wrapper);
+  wrapper.ClearAndLeak();
+  return *result;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_InternalDateFormat) {
+  HandleScope scope(isolate);
+
+  ASSERT(args.length() == 2);
+
+  CONVERT_ARG_HANDLE_CHECKED(JSObject, date_format_holder, 0);
+  CONVERT_ARG_HANDLE_CHECKED(JSDate, date, 1);
+
+  bool has_pending_exception = false;
+  double millis = Execution::ToNumber(date, &has_pending_exception)->Number();
+  if (has_pending_exception) {
+    ASSERT(isolate->has_pending_exception());
+    return Failure::Exception();
+  }
+
+  icu::SimpleDateFormat* date_format =
+      DateFormat::UnpackDateFormat(isolate, date_format_holder);
+  if (!date_format) return isolate->ThrowIllegalOperation();
+
+  icu::UnicodeString result;
+  date_format->format(millis, result);
+
+  return *isolate->factory()->NewStringFromTwoByte(
+      Vector<const uint16_t>(
+          reinterpret_cast<const uint16_t*>(result.getBuffer()),
+          result.length()));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_InternalDateParse) {
+  HandleScope scope(isolate);
+
+  ASSERT(args.length() == 2);
+
+  CONVERT_ARG_HANDLE_CHECKED(JSObject, date_format_holder, 0);
+  CONVERT_ARG_HANDLE_CHECKED(String, date_string, 1);
+
+  v8::String::Utf8Value utf8_date(v8::Utils::ToLocal(date_string));
+  icu::UnicodeString u_date(icu::UnicodeString::fromUTF8(*utf8_date));
+  icu::SimpleDateFormat* date_format =
+      DateFormat::UnpackDateFormat(isolate, date_format_holder);
+  if (!date_format) return isolate->ThrowIllegalOperation();
+
+  UErrorCode status = U_ZERO_ERROR;
+  UDate date = date_format->parse(u_date, status);
+  if (U_FAILURE(status)) return isolate->heap()->undefined_value();
+
+  bool has_pending_exception = false;
+  Handle<JSDate> result = Handle<JSDate>::cast(
+      Execution::NewDate(static_cast<double>(date), &has_pending_exception));
+  if (has_pending_exception) {
+    ASSERT(isolate->has_pending_exception());
+    return Failure::Exception();
+  }
+  return *result;
+}
 #endif  // V8_I18N_SUPPORT
 
 
index 6b68f24..ade7e73 100644 (file)
@@ -543,6 +543,11 @@ namespace internal {
   F(AvailableLocalesOf, 1, 1) \
   F(GetDefaultICULocale, 0, 1) \
   F(GetLanguageTagVariants, 1, 1) \
+  \
+  /* Date format and parse. */ \
+  F(CreateDateTimeFormat, 3, 1) \
+  F(InternalDateFormat, 2, 1) \
+  F(InternalDateParse, 2, 1) \
 
 #else
 #define RUNTIME_FUNCTION_LIST_I18N_SUPPORT(F)
index 82b4c3f..cb9b85c 100644 (file)
         }],
         ['v8_enable_i18n_support==1', {
           'sources': [
+            '../../src/i18n.cc',
+            '../../src/i18n.h',
             '../../src/extensions/i18n/break-iterator.cc',
             '../../src/extensions/i18n/break-iterator.h',
             '../../src/extensions/i18n/collator.cc',
             '../../src/extensions/i18n/collator.h',
-            '../../src/extensions/i18n/date-format.cc',
-            '../../src/extensions/i18n/date-format.h',
             '../../src/extensions/i18n/i18n-extension.cc',
             '../../src/extensions/i18n/i18n-extension.h',
             '../../src/extensions/i18n/i18n-utils.cc',