Move i18n's number-format C++ code to runtime
BUG=v8:2745 R=dcarney@chromium.org, mstarzinger@chromium.org Review URL: https://codereview.chromium.org/22266009 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16099 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
63defee477
commit
5a92a95676
@ -31,7 +31,6 @@
|
|||||||
#include "break-iterator.h"
|
#include "break-iterator.h"
|
||||||
#include "collator.h"
|
#include "collator.h"
|
||||||
#include "natives.h"
|
#include "natives.h"
|
||||||
#include "number-format.h"
|
|
||||||
|
|
||||||
using v8::internal::I18NNatives;
|
using v8::internal::I18NNatives;
|
||||||
|
|
||||||
@ -47,15 +46,6 @@ Extension::Extension()
|
|||||||
|
|
||||||
v8::Handle<v8::FunctionTemplate> Extension::GetNativeFunction(
|
v8::Handle<v8::FunctionTemplate> Extension::GetNativeFunction(
|
||||||
v8::Handle<v8::String> name) {
|
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.
|
// Collator.
|
||||||
if (name->Equals(v8::String::New("NativeJSCreateCollator"))) {
|
if (name->Equals(v8::String::New("NativeJSCreateCollator"))) {
|
||||||
return v8::FunctionTemplate::New(Collator::JSCreateCollator);
|
return v8::FunctionTemplate::New(Collator::JSCreateCollator);
|
||||||
|
@ -1,418 +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 "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
|
|
@ -1,69 +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.
|
|
||||||
|
|
||||||
#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_
|
|
@ -65,8 +65,6 @@ function getNumberOption(options, property, min, max, fallback) {
|
|||||||
* Useful for subclassing.
|
* Useful for subclassing.
|
||||||
*/
|
*/
|
||||||
function initializeNumberFormat(numberFormat, locales, options) {
|
function initializeNumberFormat(numberFormat, locales, options) {
|
||||||
native function NativeJSCreateNumberFormat();
|
|
||||||
|
|
||||||
if (numberFormat.hasOwnProperty('__initializedIntlObject')) {
|
if (numberFormat.hasOwnProperty('__initializedIntlObject')) {
|
||||||
throw new TypeError('Trying to re-initialize NumberFormat object.');
|
throw new TypeError('Trying to re-initialize NumberFormat object.');
|
||||||
}
|
}
|
||||||
@ -148,9 +146,9 @@ function initializeNumberFormat(numberFormat, locales, options) {
|
|||||||
if (internalOptions.hasOwnProperty('maximumSignificantDigits')) {
|
if (internalOptions.hasOwnProperty('maximumSignificantDigits')) {
|
||||||
defineWEProperty(resolved, 'maximumSignificantDigits', undefined);
|
defineWEProperty(resolved, 'maximumSignificantDigits', undefined);
|
||||||
}
|
}
|
||||||
var formatter = NativeJSCreateNumberFormat(requestedLocale,
|
var formatter = %CreateNumberFormat(requestedLocale,
|
||||||
internalOptions,
|
internalOptions,
|
||||||
resolved);
|
resolved);
|
||||||
|
|
||||||
// We can't get information about number or currency style from ICU, so we
|
// We can't get information about number or currency style from ICU, so we
|
||||||
// assume user request was fulfilled.
|
// assume user request was fulfilled.
|
||||||
@ -269,15 +267,13 @@ function initializeNumberFormat(numberFormat, locales, options) {
|
|||||||
* NumberFormat.
|
* NumberFormat.
|
||||||
*/
|
*/
|
||||||
function formatNumber(formatter, value) {
|
function formatNumber(formatter, value) {
|
||||||
native function NativeJSInternalNumberFormat();
|
|
||||||
|
|
||||||
// Spec treats -0 and +0 as 0.
|
// Spec treats -0 and +0 as 0.
|
||||||
var number = Number(value);
|
var number = Number(value);
|
||||||
if (number === -0) {
|
if (number === -0) {
|
||||||
number = 0;
|
number = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NativeJSInternalNumberFormat(formatter.formatter, number);
|
return %InternalNumberFormat(formatter.formatter, number);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -285,9 +281,7 @@ function formatNumber(formatter, value) {
|
|||||||
* Returns a Number that represents string value that was passed in.
|
* Returns a Number that represents string value that was passed in.
|
||||||
*/
|
*/
|
||||||
function parseNumber(formatter, value) {
|
function parseNumber(formatter, value) {
|
||||||
native function NativeJSInternalNumberParse();
|
return %InternalNumberParse(formatter.formatter, String(value));
|
||||||
|
|
||||||
return NativeJSInternalNumberParse(formatter.formatter, String(value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
398
src/i18n.cc
398
src/i18n.cc
@ -29,33 +29,83 @@
|
|||||||
#include "i18n.h"
|
#include "i18n.h"
|
||||||
|
|
||||||
#include "unicode/calendar.h"
|
#include "unicode/calendar.h"
|
||||||
|
#include "unicode/curramt.h"
|
||||||
|
#include "unicode/dcfmtsym.h"
|
||||||
|
#include "unicode/decimfmt.h"
|
||||||
#include "unicode/dtfmtsym.h"
|
#include "unicode/dtfmtsym.h"
|
||||||
#include "unicode/dtptngen.h"
|
#include "unicode/dtptngen.h"
|
||||||
#include "unicode/locid.h"
|
#include "unicode/locid.h"
|
||||||
|
#include "unicode/numfmt.h"
|
||||||
#include "unicode/numsys.h"
|
#include "unicode/numsys.h"
|
||||||
#include "unicode/smpdtfmt.h"
|
#include "unicode/smpdtfmt.h"
|
||||||
#include "unicode/timezone.h"
|
#include "unicode/timezone.h"
|
||||||
|
#include "unicode/uchar.h"
|
||||||
|
#include "unicode/ucurr.h"
|
||||||
|
#include "unicode/unum.h"
|
||||||
|
#include "unicode/uversion.h"
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
namespace {
|
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(
|
icu::SimpleDateFormat* CreateICUDateFormat(
|
||||||
Isolate* isolate,
|
Isolate* isolate,
|
||||||
const icu::Locale& icu_locale,
|
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
|
// Create time zone as specified by the user. We have to re-create time zone
|
||||||
// since calendar takes ownership.
|
// since calendar takes ownership.
|
||||||
icu::TimeZone* tz = NULL;
|
icu::TimeZone* tz = NULL;
|
||||||
MaybeObject* maybe_object = options->GetProperty(
|
icu::UnicodeString timezone;
|
||||||
*isolate->factory()->NewStringFromAscii(CStrVector("timeZone")));
|
if (ExtractStringSetting(isolate, options, "timeZone", &timezone)) {
|
||||||
Object* timezone;
|
tz = icu::TimeZone::createTimeZone(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 {
|
} else {
|
||||||
tz = icu::TimeZone::createDefault();
|
tz = icu::TimeZone::createDefault();
|
||||||
}
|
}
|
||||||
@ -68,18 +118,13 @@ icu::SimpleDateFormat* CreateICUDateFormat(
|
|||||||
// Make formatter from skeleton. Calendar and numbering system are added
|
// Make formatter from skeleton. Calendar and numbering system are added
|
||||||
// to the locale as Unicode extension (if they were specified at all).
|
// to the locale as Unicode extension (if they were specified at all).
|
||||||
icu::SimpleDateFormat* date_format = NULL;
|
icu::SimpleDateFormat* date_format = NULL;
|
||||||
Object* skeleton;
|
icu::UnicodeString skeleton;
|
||||||
maybe_object = options->GetProperty(
|
if (ExtractStringSetting(isolate, options, "skeleton", &skeleton)) {
|
||||||
*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* generator =
|
||||||
icu::DateTimePatternGenerator::createInstance(icu_locale, status);
|
icu::DateTimePatternGenerator::createInstance(icu_locale, status);
|
||||||
icu::UnicodeString pattern;
|
icu::UnicodeString pattern;
|
||||||
if (U_SUCCESS(status)) {
|
if (U_SUCCESS(status)) {
|
||||||
pattern = generator->getBestPattern(u_skeleton, status);
|
pattern = generator->getBestPattern(skeleton, status);
|
||||||
delete generator;
|
delete generator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,10 +144,10 @@ icu::SimpleDateFormat* CreateICUDateFormat(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void SetResolvedSettings(Isolate* isolate,
|
void SetResolvedDateSettings(Isolate* isolate,
|
||||||
const icu::Locale& icu_locale,
|
const icu::Locale& icu_locale,
|
||||||
icu::SimpleDateFormat* date_format,
|
icu::SimpleDateFormat* date_format,
|
||||||
Handle<JSObject> resolved) {
|
Handle<JSObject> resolved) {
|
||||||
UErrorCode status = U_ZERO_ERROR;
|
UErrorCode status = U_ZERO_ERROR;
|
||||||
icu::UnicodeString pattern;
|
icu::UnicodeString pattern;
|
||||||
date_format->toPattern(pattern);
|
date_format->toPattern(pattern);
|
||||||
@ -217,6 +262,249 @@ Handle<ObjectTemplateInfo> GetEternal(Isolate* isolate) {
|
|||||||
field));
|
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
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
@ -261,9 +549,10 @@ icu::SimpleDateFormat* DateFormat::InitializeDateTimeFormat(
|
|||||||
date_format = CreateICUDateFormat(isolate, no_extension_locale, options);
|
date_format = CreateICUDateFormat(isolate, no_extension_locale, options);
|
||||||
|
|
||||||
// Set resolved settings (pattern, numbering system, calendar).
|
// Set resolved settings (pattern, numbering system, calendar).
|
||||||
SetResolvedSettings(isolate, no_extension_locale, date_format, resolved);
|
SetResolvedDateSettings(
|
||||||
|
isolate, no_extension_locale, date_format, resolved);
|
||||||
} else {
|
} else {
|
||||||
SetResolvedSettings(isolate, icu_locale, date_format, resolved);
|
SetResolvedDateSettings(isolate, icu_locale, date_format, resolved);
|
||||||
}
|
}
|
||||||
|
|
||||||
return date_format;
|
return date_format;
|
||||||
@ -294,4 +583,67 @@ void DateFormat::DeleteDateFormat(v8::Isolate* isolate,
|
|||||||
object->Dispose(isolate);
|
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
|
} } // namespace v8::internal
|
||||||
|
26
src/i18n.h
26
src/i18n.h
@ -33,6 +33,7 @@
|
|||||||
#include "v8.h"
|
#include "v8.h"
|
||||||
|
|
||||||
namespace U_ICU_NAMESPACE {
|
namespace U_ICU_NAMESPACE {
|
||||||
|
class DecimalFormat;
|
||||||
class SimpleDateFormat;
|
class SimpleDateFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,6 +52,7 @@ class I18N {
|
|||||||
I18N();
|
I18N();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class DateFormat {
|
class DateFormat {
|
||||||
public:
|
public:
|
||||||
// Create a formatter for the specificied locale and options. Returns the
|
// Create a formatter for the specificied locale and options. Returns the
|
||||||
@ -74,6 +76,30 @@ class DateFormat {
|
|||||||
DateFormat();
|
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
|
} } // namespace v8::internal
|
||||||
|
|
||||||
#endif // V8_I18N_H_
|
#endif // V8_I18N_H_
|
||||||
|
121
src/runtime.cc
121
src/runtime.cc
@ -71,7 +71,10 @@
|
|||||||
#include "unicode/brkiter.h"
|
#include "unicode/brkiter.h"
|
||||||
#include "unicode/calendar.h"
|
#include "unicode/calendar.h"
|
||||||
#include "unicode/coll.h"
|
#include "unicode/coll.h"
|
||||||
|
#include "unicode/curramt.h"
|
||||||
#include "unicode/datefmt.h"
|
#include "unicode/datefmt.h"
|
||||||
|
#include "unicode/dcfmtsym.h"
|
||||||
|
#include "unicode/decimfmt.h"
|
||||||
#include "unicode/dtfmtsym.h"
|
#include "unicode/dtfmtsym.h"
|
||||||
#include "unicode/dtptngen.h"
|
#include "unicode/dtptngen.h"
|
||||||
#include "unicode/locid.h"
|
#include "unicode/locid.h"
|
||||||
@ -79,7 +82,10 @@
|
|||||||
#include "unicode/numsys.h"
|
#include "unicode/numsys.h"
|
||||||
#include "unicode/smpdtfmt.h"
|
#include "unicode/smpdtfmt.h"
|
||||||
#include "unicode/timezone.h"
|
#include "unicode/timezone.h"
|
||||||
|
#include "unicode/uchar.h"
|
||||||
|
#include "unicode/ucurr.h"
|
||||||
#include "unicode/uloc.h"
|
#include "unicode/uloc.h"
|
||||||
|
#include "unicode/unum.h"
|
||||||
#include "unicode/uversion.h"
|
#include "unicode/uversion.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -13665,6 +13671,121 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InternalDateParse) {
|
|||||||
}
|
}
|
||||||
return *result;
|
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
|
#endif // V8_I18N_SUPPORT
|
||||||
|
|
||||||
|
|
||||||
|
@ -548,6 +548,11 @@ namespace internal {
|
|||||||
F(CreateDateTimeFormat, 3, 1) \
|
F(CreateDateTimeFormat, 3, 1) \
|
||||||
F(InternalDateFormat, 2, 1) \
|
F(InternalDateFormat, 2, 1) \
|
||||||
F(InternalDateParse, 2, 1) \
|
F(InternalDateParse, 2, 1) \
|
||||||
|
\
|
||||||
|
/* Number format and parse. */ \
|
||||||
|
F(CreateNumberFormat, 3, 1) \
|
||||||
|
F(InternalNumberFormat, 2, 1) \
|
||||||
|
F(InternalNumberParse, 2, 1) \
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#define RUNTIME_FUNCTION_LIST_I18N_SUPPORT(F)
|
#define RUNTIME_FUNCTION_LIST_I18N_SUPPORT(F)
|
||||||
|
@ -833,8 +833,6 @@
|
|||||||
'../../src/extensions/i18n/i18n-extension.h',
|
'../../src/extensions/i18n/i18n-extension.h',
|
||||||
'../../src/extensions/i18n/i18n-utils.cc',
|
'../../src/extensions/i18n/i18n-utils.cc',
|
||||||
'../../src/extensions/i18n/i18n-utils.h',
|
'../../src/extensions/i18n/i18n-utils.h',
|
||||||
'../../src/extensions/i18n/number-format.cc',
|
|
||||||
'../../src/extensions/i18n/number-format.h',
|
|
||||||
],
|
],
|
||||||
'dependencies': [
|
'dependencies': [
|
||||||
'<(DEPTH)/third_party/icu/icu.gyp:icui18n',
|
'<(DEPTH)/third_party/icu/icu.gyp:icui18n',
|
||||||
|
Loading…
Reference in New Issue
Block a user