#include "break-iterator.h"
#include "collator.h"
#include "natives.h"
-#include "number-format.h"
using v8::internal::I18NNatives;
v8::Handle<v8::FunctionTemplate> Extension::GetNativeFunction(
v8::Handle<v8::String> name) {
- // Number format and parse.
- if (name->Equals(v8::String::New("NativeJSCreateNumberFormat"))) {
- return v8::FunctionTemplate::New(NumberFormat::JSCreateNumberFormat);
- } else if (name->Equals(v8::String::New("NativeJSInternalNumberFormat"))) {
- return v8::FunctionTemplate::New(NumberFormat::JSInternalFormat);
- } else if (name->Equals(v8::String::New("NativeJSInternalNumberParse"))) {
- return v8::FunctionTemplate::New(NumberFormat::JSInternalParse);
- }
-
// Collator.
if (name->Equals(v8::String::New("NativeJSCreateCollator"))) {
return v8::FunctionTemplate::New(Collator::JSCreateCollator);
+++ /dev/null
-// 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 "number-format.h"
-
-#include <string.h>
-
-#include "i18n-utils.h"
-#include "unicode/curramt.h"
-#include "unicode/dcfmtsym.h"
-#include "unicode/decimfmt.h"
-#include "unicode/locid.h"
-#include "unicode/numfmt.h"
-#include "unicode/numsys.h"
-#include "unicode/uchar.h"
-#include "unicode/ucurr.h"
-#include "unicode/unum.h"
-#include "unicode/uversion.h"
-
-namespace v8_i18n {
-
-static icu::DecimalFormat* InitializeNumberFormat(v8::Handle<v8::String>,
- v8::Handle<v8::Object>,
- v8::Handle<v8::Object>);
-static icu::DecimalFormat* CreateICUNumberFormat(const icu::Locale&,
- v8::Handle<v8::Object>);
-static void SetResolvedSettings(const icu::Locale&,
- icu::DecimalFormat*,
- v8::Handle<v8::Object>);
-
-icu::DecimalFormat* NumberFormat::UnpackNumberFormat(
- v8::Handle<v8::Object> obj) {
- v8::HandleScope handle_scope;
-
- // v8::ObjectTemplate doesn't have HasInstance method so we can't check
- // if obj is an instance of NumberFormat class. We'll check for a property
- // that has to be in the object. The same applies to other services, like
- // Collator and DateTimeFormat.
- if (obj->HasOwnProperty(v8::String::New("numberFormat"))) {
- return static_cast<icu::DecimalFormat*>(
- obj->GetAlignedPointerFromInternalField(0));
- }
-
- return NULL;
-}
-
-void NumberFormat::DeleteNumberFormat(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 UnpackNumberFormat(handle);
-
- // Then dispose of the persistent handle to JS object.
- object->Dispose(isolate);
-}
-
-void NumberFormat::JSInternalFormat(
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- if (args.Length() != 2 || !args[0]->IsObject() || !args[1]->IsNumber()) {
- v8::ThrowException(v8::Exception::Error(
- v8::String::New("Formatter and numeric value have to be specified.")));
- return;
- }
-
- icu::DecimalFormat* number_format = UnpackNumberFormat(args[0]->ToObject());
- if (!number_format) {
- v8::ThrowException(v8::Exception::Error(
- v8::String::New("NumberFormat method called on an object "
- "that is not a NumberFormat.")));
- return;
- }
-
- // ICU will handle actual NaN value properly and return NaN string.
- icu::UnicodeString result;
- number_format->format(args[1]->NumberValue(), result);
-
- args.GetReturnValue().Set(v8::String::New(
- reinterpret_cast<const uint16_t*>(result.getBuffer()), result.length()));
-}
-
-void NumberFormat::JSInternalParse(
- const v8::FunctionCallbackInfo<v8::Value>& args) {
- if (args.Length() != 2 || !args[0]->IsObject() || !args[1]->IsString()) {
- v8::ThrowException(v8::Exception::Error(
- v8::String::New("Formatter and string have to be specified.")));
- return;
- }
-
- icu::DecimalFormat* number_format = UnpackNumberFormat(args[0]->ToObject());
- if (!number_format) {
- v8::ThrowException(v8::Exception::Error(
- v8::String::New("NumberFormat method called on an object "
- "that is not a NumberFormat.")));
- return;
- }
-
- // ICU will handle actual NaN value properly and return NaN string.
- icu::UnicodeString string_number;
- if (!Utils::V8StringToUnicodeString(args[1]->ToString(), &string_number)) {
- string_number = "";
- }
-
- UErrorCode status = U_ZERO_ERROR;
- icu::Formattable result;
- // ICU 4.6 doesn't support parseCurrency call. We need to wait for ICU49
- // to be part of Chrome.
- // TODO(cira): Include currency parsing code using parseCurrency call.
- // We need to check if the formatter parses all currencies or only the
- // one it was constructed with (it will impact the API - how to return ISO
- // code and the value).
- number_format->parse(string_number, result, status);
- if (U_FAILURE(status)) {
- return;
- }
-
- switch (result.getType()) {
- case icu::Formattable::kDouble:
- args.GetReturnValue().Set(result.getDouble());
- return;
- case icu::Formattable::kLong:
- args.GetReturnValue().Set(result.getLong());
- return;
- case icu::Formattable::kInt64:
- args.GetReturnValue().Set(static_cast<double>(result.getInt64()));
- return;
- default:
- return;
- }
-}
-
-void NumberFormat::JSCreateNumberFormat(
- 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> number_format_template =
- Utils::GetTemplate(isolate);
-
- // Create an empty object wrapper.
- v8::Local<v8::Object> local_object = number_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 number formatter as internal field of the resulting JS object.
- icu::DecimalFormat* number_format = InitializeNumberFormat(
- args[0]->ToString(), args[1]->ToObject(), args[2]->ToObject());
-
- if (!number_format) {
- v8::ThrowException(v8::Exception::Error(v8::String::New(
- "Internal error. Couldn't create ICU number formatter.")));
- return;
- } else {
- local_object->SetAlignedPointerInInternalField(0, number_format);
-
- v8::TryCatch try_catch;
- local_object->Set(v8::String::New("numberFormat"),
- 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, &DeleteNumberFormat);
- args.GetReturnValue().Set(wrapper);
- wrapper.ClearAndLeak();
-}
-
-static icu::DecimalFormat* InitializeNumberFormat(
- 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::DecimalFormat* number_format =
- CreateICUNumberFormat(icu_locale, options);
- if (!number_format) {
- // Remove extensions and try again.
- icu::Locale no_extension_locale(icu_locale.getBaseName());
- number_format = CreateICUNumberFormat(no_extension_locale, options);
-
- // Set resolved settings (pattern, numbering system).
- SetResolvedSettings(no_extension_locale, number_format, resolved);
- } else {
- SetResolvedSettings(icu_locale, number_format, resolved);
- }
-
- return number_format;
-}
-
-static icu::DecimalFormat* CreateICUNumberFormat(
- const icu::Locale& icu_locale, v8::Handle<v8::Object> options) {
- // Make formatter from options. Numbering system is added
- // to the locale as Unicode extension (if it was specified at all).
- UErrorCode status = U_ZERO_ERROR;
- icu::DecimalFormat* number_format = NULL;
- icu::UnicodeString style;
- icu::UnicodeString currency;
- if (Utils::ExtractStringSetting(options, "style", &style)) {
- if (style == UNICODE_STRING_SIMPLE("currency")) {
- Utils::ExtractStringSetting(options, "currency", ¤cy);
-
- icu::UnicodeString display;
- Utils::ExtractStringSetting(options, "currencyDisplay", &display);
-#if (U_ICU_VERSION_MAJOR_NUM == 4) && (U_ICU_VERSION_MINOR_NUM <= 6)
- icu::NumberFormat::EStyles style;
- if (display == UNICODE_STRING_SIMPLE("code")) {
- style = icu::NumberFormat::kIsoCurrencyStyle;
- } else if (display == UNICODE_STRING_SIMPLE("name")) {
- style = icu::NumberFormat::kPluralCurrencyStyle;
- } else {
- style = icu::NumberFormat::kCurrencyStyle;
- }
-#else // ICU version is 4.8 or above (we ignore versions below 4.0).
- UNumberFormatStyle style;
- if (display == UNICODE_STRING_SIMPLE("code")) {
- style = UNUM_CURRENCY_ISO;
- } else if (display == UNICODE_STRING_SIMPLE("name")) {
- style = UNUM_CURRENCY_PLURAL;
- } else {
- style = UNUM_CURRENCY;
- }
-#endif
-
- number_format = static_cast<icu::DecimalFormat*>(
- icu::NumberFormat::createInstance(icu_locale, style, status));
- } else if (style == UNICODE_STRING_SIMPLE("percent")) {
- number_format = static_cast<icu::DecimalFormat*>(
- icu::NumberFormat::createPercentInstance(icu_locale, status));
- if (U_FAILURE(status)) {
- delete number_format;
- return NULL;
- }
- // Make sure 1.1% doesn't go into 2%.
- number_format->setMinimumFractionDigits(1);
- } else {
- // Make a decimal instance by default.
- number_format = static_cast<icu::DecimalFormat*>(
- icu::NumberFormat::createInstance(icu_locale, status));
- }
- }
-
- if (U_FAILURE(status)) {
- delete number_format;
- return NULL;
- }
-
- // Set all options.
- if (!currency.isEmpty()) {
- number_format->setCurrency(currency.getBuffer(), status);
- }
-
- int32_t digits;
- if (Utils::ExtractIntegerSetting(
- options, "minimumIntegerDigits", &digits)) {
- number_format->setMinimumIntegerDigits(digits);
- }
-
- if (Utils::ExtractIntegerSetting(
- options, "minimumFractionDigits", &digits)) {
- number_format->setMinimumFractionDigits(digits);
- }
-
- if (Utils::ExtractIntegerSetting(
- options, "maximumFractionDigits", &digits)) {
- number_format->setMaximumFractionDigits(digits);
- }
-
- bool significant_digits_used = false;
- if (Utils::ExtractIntegerSetting(
- options, "minimumSignificantDigits", &digits)) {
- number_format->setMinimumSignificantDigits(digits);
- significant_digits_used = true;
- }
-
- if (Utils::ExtractIntegerSetting(
- options, "maximumSignificantDigits", &digits)) {
- number_format->setMaximumSignificantDigits(digits);
- significant_digits_used = true;
- }
-
- number_format->setSignificantDigitsUsed(significant_digits_used);
-
- bool grouping;
- if (Utils::ExtractBooleanSetting(options, "useGrouping", &grouping)) {
- number_format->setGroupingUsed(grouping);
- }
-
- // Set rounding mode.
- number_format->setRoundingMode(icu::DecimalFormat::kRoundHalfUp);
-
- return number_format;
-}
-
-static void SetResolvedSettings(const icu::Locale& icu_locale,
- icu::DecimalFormat* number_format,
- v8::Handle<v8::Object> resolved) {
- icu::UnicodeString pattern;
- number_format->toPattern(pattern);
- resolved->Set(v8::String::New("pattern"),
- v8::String::New(reinterpret_cast<const uint16_t*>(
- pattern.getBuffer()), pattern.length()));
-
- // Set resolved currency code in options.currency if not empty.
- icu::UnicodeString currency(number_format->getCurrency());
- if (!currency.isEmpty()) {
- resolved->Set(v8::String::New("currency"),
- v8::String::New(reinterpret_cast<const uint16_t*>(
- currency.getBuffer()), currency.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 would.
- UErrorCode 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;
-
- resolved->Set(v8::String::New("useGrouping"),
- v8::Boolean::New(number_format->isGroupingUsed()));
-
- resolved->Set(v8::String::New("minimumIntegerDigits"),
- v8::Integer::New(number_format->getMinimumIntegerDigits()));
-
- resolved->Set(v8::String::New("minimumFractionDigits"),
- v8::Integer::New(number_format->getMinimumFractionDigits()));
-
- resolved->Set(v8::String::New("maximumFractionDigits"),
- v8::Integer::New(number_format->getMaximumFractionDigits()));
-
- if (resolved->HasOwnProperty(v8::String::New("minimumSignificantDigits"))) {
- resolved->Set(v8::String::New("minimumSignificantDigits"), v8::Integer::New(
- number_format->getMinimumSignificantDigits()));
- }
-
- if (resolved->HasOwnProperty(v8::String::New("maximumSignificantDigits"))) {
- resolved->Set(v8::String::New("maximumSignificantDigits"), v8::Integer::New(
- number_format->getMaximumSignificantDigits()));
- }
-
- // 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
+++ /dev/null
-// 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.
-
-#ifndef V8_EXTENSIONS_I18N_NUMBER_FORMAT_H_
-#define V8_EXTENSIONS_I18N_NUMBER_FORMAT_H_
-
-#include "unicode/uversion.h"
-#include "v8.h"
-
-namespace U_ICU_NAMESPACE {
-class DecimalFormat;
-}
-
-namespace v8_i18n {
-
-class NumberFormat {
- public:
- static void JSCreateNumberFormat(
- const v8::FunctionCallbackInfo<v8::Value>& args);
-
- // Helper methods for various bindings.
-
- // Unpacks date format object from corresponding JavaScript object.
- static icu::DecimalFormat* UnpackNumberFormat(v8::Handle<v8::Object> obj);
-
- // Release memory we allocated for the NumberFormat once the JS object that
- // holds the pointer gets garbage collected.
- static void DeleteNumberFormat(v8::Isolate* isolate,
- v8::Persistent<v8::Object>* object,
- void* param);
-
- // Formats number and returns corresponding string.
- static void JSInternalFormat(const v8::FunctionCallbackInfo<v8::Value>& args);
-
- // Parses a string and returns a number.
- static void JSInternalParse(const v8::FunctionCallbackInfo<v8::Value>& args);
-
- private:
- NumberFormat();
-};
-
-} // namespace v8_i18n
-
-#endif // V8_EXTENSIONS_I18N_NUMBER_FORMAT_H_
* Useful for subclassing.
*/
function initializeNumberFormat(numberFormat, locales, options) {
- native function NativeJSCreateNumberFormat();
-
if (numberFormat.hasOwnProperty('__initializedIntlObject')) {
throw new TypeError('Trying to re-initialize NumberFormat object.');
}
if (internalOptions.hasOwnProperty('maximumSignificantDigits')) {
defineWEProperty(resolved, 'maximumSignificantDigits', undefined);
}
- var formatter = NativeJSCreateNumberFormat(requestedLocale,
- internalOptions,
- resolved);
+ var formatter = %CreateNumberFormat(requestedLocale,
+ internalOptions,
+ resolved);
// We can't get information about number or currency style from ICU, so we
// assume user request was fulfilled.
* NumberFormat.
*/
function formatNumber(formatter, value) {
- native function NativeJSInternalNumberFormat();
-
// Spec treats -0 and +0 as 0.
var number = Number(value);
if (number === -0) {
number = 0;
}
- return NativeJSInternalNumberFormat(formatter.formatter, number);
+ return %InternalNumberFormat(formatter.formatter, number);
}
* Returns a Number that represents string value that was passed in.
*/
function parseNumber(formatter, value) {
- native function NativeJSInternalNumberParse();
-
- return NativeJSInternalNumberParse(formatter.formatter, String(value));
+ return %InternalNumberParse(formatter.formatter, String(value));
}
#include "i18n.h"
#include "unicode/calendar.h"
+#include "unicode/curramt.h"
+#include "unicode/dcfmtsym.h"
+#include "unicode/decimfmt.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/uchar.h"
+#include "unicode/ucurr.h"
+#include "unicode/unum.h"
+#include "unicode/uversion.h"
namespace v8 {
namespace internal {
namespace {
+bool ExtractStringSetting(Isolate* isolate,
+ Handle<JSObject> options,
+ const char* key,
+ icu::UnicodeString* setting) {
+ MaybeObject* maybe_object = options->GetProperty(
+ *isolate->factory()->NewStringFromAscii(CStrVector(key)));
+ Object* object;
+ if (maybe_object->ToObject(&object) && object->IsString()) {
+ v8::String::Utf8Value utf8_string(
+ v8::Utils::ToLocal(Handle<String>(String::cast(object))));
+ *setting = icu::UnicodeString::fromUTF8(*utf8_string);
+ return true;
+ }
+ return false;
+}
+
+
+bool ExtractIntegerSetting(Isolate* isolate,
+ Handle<JSObject> options,
+ const char* key,
+ int32_t* value) {
+ MaybeObject* maybe_object = options->GetProperty(
+ *isolate->factory()->NewStringFromAscii(CStrVector(key)));
+ Object* object;
+ if (maybe_object->ToObject(&object) && object->IsNumber()) {
+ object->ToInt32(value);
+ return true;
+ }
+ return false;
+}
+
+
+bool ExtractBooleanSetting(Isolate* isolate,
+ Handle<JSObject> options,
+ const char* key,
+ bool* value) {
+ MaybeObject* maybe_object = options->GetProperty(
+ *isolate->factory()->NewStringFromAscii(CStrVector(key)));
+ Object* object;
+ if (maybe_object->ToObject(&object) && object->IsBoolean()) {
+ *value = object->BooleanValue();
+ return true;
+ }
+ return false;
+}
+
+
icu::SimpleDateFormat* CreateICUDateFormat(
Isolate* isolate,
const icu::Locale& icu_locale,
- Handle<Object> options) {
+ Handle<JSObject> 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);
+ icu::UnicodeString timezone;
+ if (ExtractStringSetting(isolate, options, "timeZone", &timezone)) {
+ tz = icu::TimeZone::createTimeZone(timezone);
} else {
tz = icu::TimeZone::createDefault();
}
// 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::UnicodeString skeleton;
+ if (ExtractStringSetting(isolate, options, "skeleton", &skeleton)) {
icu::DateTimePatternGenerator* generator =
icu::DateTimePatternGenerator::createInstance(icu_locale, status);
icu::UnicodeString pattern;
if (U_SUCCESS(status)) {
- pattern = generator->getBestPattern(u_skeleton, status);
+ pattern = generator->getBestPattern(skeleton, status);
delete generator;
}
}
-void SetResolvedSettings(Isolate* isolate,
- const icu::Locale& icu_locale,
- icu::SimpleDateFormat* date_format,
- Handle<JSObject> resolved) {
+void SetResolvedDateSettings(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);
field));
}
+
+icu::DecimalFormat* CreateICUNumberFormat(
+ Isolate* isolate,
+ const icu::Locale& icu_locale,
+ Handle<JSObject> options) {
+ // Make formatter from options. Numbering system is added
+ // to the locale as Unicode extension (if it was specified at all).
+ UErrorCode status = U_ZERO_ERROR;
+ icu::DecimalFormat* number_format = NULL;
+ icu::UnicodeString style;
+ icu::UnicodeString currency;
+ if (ExtractStringSetting(isolate, options, "style", &style)) {
+ if (style == UNICODE_STRING_SIMPLE("currency")) {
+ icu::UnicodeString display;
+ ExtractStringSetting(isolate, options, "currency", ¤cy);
+ ExtractStringSetting(isolate, options, "currencyDisplay", &display);
+
+#if (U_ICU_VERSION_MAJOR_NUM == 4) && (U_ICU_VERSION_MINOR_NUM <= 6)
+ icu::NumberFormat::EStyles format_style;
+ if (display == UNICODE_STRING_SIMPLE("code")) {
+ format_style = icu::NumberFormat::kIsoCurrencyStyle;
+ } else if (display == UNICODE_STRING_SIMPLE("name")) {
+ format_style = icu::NumberFormat::kPluralCurrencyStyle;
+ } else {
+ format_style = icu::NumberFormat::kCurrencyStyle;
+ }
+#else // ICU version is 4.8 or above (we ignore versions below 4.0).
+ UNumberFormatStyle format_style;
+ if (display == UNICODE_STRING_SIMPLE("code")) {
+ format_style = UNUM_CURRENCY_ISO;
+ } else if (display == UNICODE_STRING_SIMPLE("name")) {
+ format_style = UNUM_CURRENCY_PLURAL;
+ } else {
+ format_style = UNUM_CURRENCY;
+ }
+#endif
+
+ number_format = static_cast<icu::DecimalFormat*>(
+ icu::NumberFormat::createInstance(icu_locale, format_style, status));
+ } else if (style == UNICODE_STRING_SIMPLE("percent")) {
+ number_format = static_cast<icu::DecimalFormat*>(
+ icu::NumberFormat::createPercentInstance(icu_locale, status));
+ if (U_FAILURE(status)) {
+ delete number_format;
+ return NULL;
+ }
+ // Make sure 1.1% doesn't go into 2%.
+ number_format->setMinimumFractionDigits(1);
+ } else {
+ // Make a decimal instance by default.
+ number_format = static_cast<icu::DecimalFormat*>(
+ icu::NumberFormat::createInstance(icu_locale, status));
+ }
+ }
+
+ if (U_FAILURE(status)) {
+ delete number_format;
+ return NULL;
+ }
+
+ // Set all options.
+ if (!currency.isEmpty()) {
+ number_format->setCurrency(currency.getBuffer(), status);
+ }
+
+ int32_t digits;
+ if (ExtractIntegerSetting(
+ isolate, options, "minimumIntegerDigits", &digits)) {
+ number_format->setMinimumIntegerDigits(digits);
+ }
+
+ if (ExtractIntegerSetting(
+ isolate, options, "minimumFractionDigits", &digits)) {
+ number_format->setMinimumFractionDigits(digits);
+ }
+
+ if (ExtractIntegerSetting(
+ isolate, options, "maximumFractionDigits", &digits)) {
+ number_format->setMaximumFractionDigits(digits);
+ }
+
+ bool significant_digits_used = false;
+ if (ExtractIntegerSetting(
+ isolate, options, "minimumSignificantDigits", &digits)) {
+ number_format->setMinimumSignificantDigits(digits);
+ significant_digits_used = true;
+ }
+
+ if (ExtractIntegerSetting(
+ isolate, options, "maximumSignificantDigits", &digits)) {
+ number_format->setMaximumSignificantDigits(digits);
+ significant_digits_used = true;
+ }
+
+ number_format->setSignificantDigitsUsed(significant_digits_used);
+
+ bool grouping;
+ if (ExtractBooleanSetting(isolate, options, "useGrouping", &grouping)) {
+ number_format->setGroupingUsed(grouping);
+ }
+
+ // Set rounding mode.
+ number_format->setRoundingMode(icu::DecimalFormat::kRoundHalfUp);
+
+ return number_format;
+}
+
+
+void SetResolvedNumberSettings(Isolate* isolate,
+ const icu::Locale& icu_locale,
+ icu::DecimalFormat* number_format,
+ Handle<JSObject> resolved) {
+ icu::UnicodeString pattern;
+ number_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 resolved currency code in options.currency if not empty.
+ icu::UnicodeString currency(number_format->getCurrency());
+ if (!currency.isEmpty()) {
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("currency")),
+ isolate->factory()->NewStringFromTwoByte(
+ Vector<const uint16_t>(
+ reinterpret_cast<const uint16_t*>(currency.getBuffer()),
+ currency.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.
+ UErrorCode 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;
+
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(CStrVector("useGrouping")),
+ isolate->factory()->ToBoolean(number_format->isGroupingUsed()),
+ NONE,
+ kNonStrictMode);
+
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(
+ CStrVector("minimumIntegerDigits")),
+ isolate->factory()->NewNumberFromInt(
+ number_format->getMinimumIntegerDigits()),
+ NONE,
+ kNonStrictMode);
+
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(
+ CStrVector("minimumFractionDigits")),
+ isolate->factory()->NewNumberFromInt(
+ number_format->getMinimumFractionDigits()),
+ NONE,
+ kNonStrictMode);
+
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(
+ CStrVector("maximumFractionDigits")),
+ isolate->factory()->NewNumberFromInt(
+ number_format->getMaximumFractionDigits()),
+ NONE,
+ kNonStrictMode);
+
+ if (resolved->HasLocalProperty(*isolate->factory()->NewStringFromAscii(
+ CStrVector("minimumSignificantDigits")))) {
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(
+ CStrVector("minimumSignificantDigits")),
+ isolate->factory()->NewNumberFromInt(
+ number_format->getMinimumSignificantDigits()),
+ NONE,
+ kNonStrictMode);
+ }
+
+ if (resolved->HasLocalProperty(*isolate->factory()->NewStringFromAscii(
+ CStrVector("maximumSignificantDigits")))) {
+ JSObject::SetProperty(
+ resolved,
+ isolate->factory()->NewStringFromAscii(
+ CStrVector("maximumSignificantDigits")),
+ isolate->factory()->NewNumberFromInt(
+ number_format->getMaximumSignificantDigits()),
+ NONE,
+ kNonStrictMode);
+ }
+
+ // 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);
+ }
+}
+
} // namespace
date_format = CreateICUDateFormat(isolate, no_extension_locale, options);
// Set resolved settings (pattern, numbering system, calendar).
- SetResolvedSettings(isolate, no_extension_locale, date_format, resolved);
+ SetResolvedDateSettings(
+ isolate, no_extension_locale, date_format, resolved);
} else {
- SetResolvedSettings(isolate, icu_locale, date_format, resolved);
+ SetResolvedDateSettings(isolate, icu_locale, date_format, resolved);
}
return date_format;
object->Dispose(isolate);
}
+
+icu::DecimalFormat* NumberFormat::InitializeNumberFormat(
+ 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::DecimalFormat* number_format =
+ CreateICUNumberFormat(isolate, icu_locale, options);
+ if (!number_format) {
+ // Remove extensions and try again.
+ icu::Locale no_extension_locale(icu_locale.getBaseName());
+ number_format = CreateICUNumberFormat(
+ isolate, no_extension_locale, options);
+
+ // Set resolved settings (pattern, numbering system).
+ SetResolvedNumberSettings(
+ isolate, no_extension_locale, number_format, resolved);
+ } else {
+ SetResolvedNumberSettings(isolate, icu_locale, number_format, resolved);
+ }
+
+ return number_format;
+}
+
+
+icu::DecimalFormat* NumberFormat::UnpackNumberFormat(
+ Isolate* isolate,
+ Handle<JSObject> obj) {
+ if (obj->HasLocalProperty(*isolate->factory()->NewStringFromAscii(
+ CStrVector("numberFormat")))) {
+ return reinterpret_cast<icu::DecimalFormat*>(obj->GetInternalField(0));
+ }
+
+ return NULL;
+}
+
+
+void NumberFormat::DeleteNumberFormat(v8::Isolate* isolate,
+ Persistent<v8::Object>* object,
+ void* param) {
+ // First delete the hidden C++ object.
+ delete reinterpret_cast<icu::DecimalFormat*>(Handle<JSObject>::cast(
+ v8::Utils::OpenPersistent(object))->GetInternalField(0));
+
+ // Then dispose of the persistent handle to JS object.
+ object->Dispose(isolate);
+}
+
} } // namespace v8::internal
#include "v8.h"
namespace U_ICU_NAMESPACE {
+class DecimalFormat;
class SimpleDateFormat;
}
I18N();
};
+
class DateFormat {
public:
// Create a formatter for the specificied locale and options. Returns the
DateFormat();
};
+
+class NumberFormat {
+ public:
+ // Create a formatter for the specificied locale and options. Returns the
+ // resolved settings for the locale / options.
+ static icu::DecimalFormat* InitializeNumberFormat(
+ Isolate* isolate,
+ Handle<String> locale,
+ Handle<JSObject> options,
+ Handle<JSObject> resolved);
+
+ // Unpacks number format object from corresponding JavaScript object.
+ static icu::DecimalFormat* UnpackNumberFormat(Isolate* isolate,
+ Handle<JSObject> obj);
+
+ // Release memory we allocated for the NumberFormat once the JS object that
+ // holds the pointer gets garbage collected.
+ static void DeleteNumberFormat(v8::Isolate* isolate,
+ Persistent<v8::Object>* object,
+ void* param);
+ private:
+ NumberFormat();
+};
+
} } // namespace v8::internal
#endif // V8_I18N_H_
#include "unicode/brkiter.h"
#include "unicode/calendar.h"
#include "unicode/coll.h"
+#include "unicode/curramt.h"
#include "unicode/datefmt.h"
+#include "unicode/dcfmtsym.h"
+#include "unicode/decimfmt.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"
+#include "unicode/uchar.h"
+#include "unicode/ucurr.h"
#include "unicode/uloc.h"
+#include "unicode/unum.h"
#include "unicode/uversion.h"
#endif
}
return *result;
}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateNumberFormat) {
+ 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> number_format_template =
+ I18N::GetTemplate(isolate);
+
+ // Create an empty object wrapper.
+ bool has_pending_exception = false;
+ Handle<JSObject> local_object = Execution::InstantiateObject(
+ number_format_template, &has_pending_exception);
+ if (has_pending_exception) {
+ ASSERT(isolate->has_pending_exception());
+ return Failure::Exception();
+ }
+
+ // Set number formatter as internal field of the resulting JS object.
+ icu::DecimalFormat* number_format = NumberFormat::InitializeNumberFormat(
+ isolate, locale, options, resolved);
+
+ if (!number_format) return isolate->ThrowIllegalOperation();
+
+ local_object->SetInternalField(0, reinterpret_cast<Smi*>(number_format));
+
+ RETURN_IF_EMPTY_HANDLE(isolate,
+ JSObject::SetLocalPropertyIgnoreAttributes(
+ local_object,
+ isolate->factory()->NewStringFromAscii(CStrVector("numberFormat")),
+ 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 number format once GC kicks
+ // in.
+ wrapper.MakeWeak<void>(NULL, &NumberFormat::DeleteNumberFormat);
+ Handle<Object> result = Utils::OpenPersistent(wrapper);
+ wrapper.ClearAndLeak();
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_InternalNumberFormat) {
+ HandleScope scope(isolate);
+
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, number_format_holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, number, 1);
+
+ bool has_pending_exception = false;
+ double value = Execution::ToNumber(number, &has_pending_exception)->Number();
+ if (has_pending_exception) {
+ ASSERT(isolate->has_pending_exception());
+ return Failure::Exception();
+ }
+
+ icu::DecimalFormat* number_format =
+ NumberFormat::UnpackNumberFormat(isolate, number_format_holder);
+ if (!number_format) return isolate->ThrowIllegalOperation();
+
+ icu::UnicodeString result;
+ number_format->format(value, result);
+
+ return *isolate->factory()->NewStringFromTwoByte(
+ Vector<const uint16_t>(
+ reinterpret_cast<const uint16_t*>(result.getBuffer()),
+ result.length()));
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_InternalNumberParse) {
+ HandleScope scope(isolate);
+
+ ASSERT(args.length() == 2);
+
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, number_format_holder, 0);
+ CONVERT_ARG_HANDLE_CHECKED(String, number_string, 1);
+
+ v8::String::Utf8Value utf8_number(v8::Utils::ToLocal(number_string));
+ icu::UnicodeString u_number(icu::UnicodeString::fromUTF8(*utf8_number));
+ icu::DecimalFormat* number_format =
+ NumberFormat::UnpackNumberFormat(isolate, number_format_holder);
+ if (!number_format) return isolate->ThrowIllegalOperation();
+
+ UErrorCode status = U_ZERO_ERROR;
+ icu::Formattable result;
+ // ICU 4.6 doesn't support parseCurrency call. We need to wait for ICU49
+ // to be part of Chrome.
+ // TODO(cira): Include currency parsing code using parseCurrency call.
+ // We need to check if the formatter parses all currencies or only the
+ // one it was constructed with (it will impact the API - how to return ISO
+ // code and the value).
+ number_format->parse(u_number, result, status);
+ if (U_FAILURE(status)) return isolate->heap()->undefined_value();
+
+ switch (result.getType()) {
+ case icu::Formattable::kDouble:
+ return *isolate->factory()->NewNumber(result.getDouble());
+ case icu::Formattable::kLong:
+ return *isolate->factory()->NewNumberFromInt(result.getLong());
+ case icu::Formattable::kInt64:
+ return *isolate->factory()->NewNumber(
+ static_cast<double>(result.getInt64()));
+ default:
+ return isolate->heap()->undefined_value();
+ }
+}
#endif // V8_I18N_SUPPORT
F(CreateDateTimeFormat, 3, 1) \
F(InternalDateFormat, 2, 1) \
F(InternalDateParse, 2, 1) \
+ \
+ /* Number format and parse. */ \
+ F(CreateNumberFormat, 3, 1) \
+ F(InternalNumberFormat, 2, 1) \
+ F(InternalNumberParse, 2, 1) \
#else
#define RUNTIME_FUNCTION_LIST_I18N_SUPPORT(F)
'../../src/extensions/i18n/i18n-extension.h',
'../../src/extensions/i18n/i18n-utils.cc',
'../../src/extensions/i18n/i18n-utils.h',
- '../../src/extensions/i18n/number-format.cc',
- '../../src/extensions/i18n/number-format.h',
],
'dependencies': [
'<(DEPTH)/third_party/icu/icu.gyp:icui18n',