From: jochen@chromium.org Date: Fri, 9 Aug 2013 09:51:09 +0000 (+0000) Subject: Move i18n collator code to runtime. X-Git-Tag: upstream/4.7.83~12989 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ebadc421e412dbdff7ec422ad9a7a713806742b3;p=platform%2Fupstream%2Fv8.git Move i18n collator code to runtime. BUG=v8:2745 R=mstarzinger@chromium.org Review URL: https://codereview.chromium.org/22671002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16126 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/extensions/i18n/collator.cc b/src/extensions/i18n/collator.cc deleted file mode 100644 index 61b1d63e5..000000000 --- a/src/extensions/i18n/collator.cc +++ /dev/null @@ -1,366 +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 "collator.h" - -#include "i18n-utils.h" -#include "unicode/coll.h" -#include "unicode/locid.h" -#include "unicode/ucol.h" - -namespace v8_i18n { - -static icu::Collator* InitializeCollator( - v8::Handle, v8::Handle, v8::Handle); - -static icu::Collator* CreateICUCollator( - const icu::Locale&, v8::Handle); - -static bool SetBooleanAttribute( - UColAttribute, const char*, v8::Handle, icu::Collator*); - -static void SetResolvedSettings( - const icu::Locale&, icu::Collator*, v8::Handle); - -static void SetBooleanSetting( - UColAttribute, icu::Collator*, const char*, v8::Handle); - -icu::Collator* Collator::UnpackCollator(v8::Handle obj) { - v8::HandleScope handle_scope; - - if (obj->HasOwnProperty(v8::String::New("collator"))) { - return static_cast( - obj->GetAlignedPointerFromInternalField(0)); - } - - return NULL; -} - -void Collator::DeleteCollator(v8::Isolate* isolate, - v8::Persistent* 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 collator. - v8::HandleScope handle_scope(isolate); - v8::Local handle = v8::Local::New(isolate, *object); - delete UnpackCollator(handle); - - // Then dispose of the persistent handle to JS object. - object->Dispose(isolate); -} - - -// Throws a JavaScript exception. -static v8::Handle ThrowUnexpectedObjectError() { - // Returns undefined, and schedules an exception to be thrown. - return v8::ThrowException(v8::Exception::Error( - v8::String::New("Collator method called on an object " - "that is not a Collator."))); -} - - -// When there's an ICU error, throw a JavaScript error with |message|. -static v8::Handle ThrowExceptionForICUError(const char* message) { - return v8::ThrowException(v8::Exception::Error(v8::String::New(message))); -} - - -// static -void Collator::JSInternalCompare( - const v8::FunctionCallbackInfo& args) { - if (args.Length() != 3 || !args[0]->IsObject() || - !args[1]->IsString() || !args[2]->IsString()) { - v8::ThrowException(v8::Exception::SyntaxError( - v8::String::New("Collator and two string arguments are required."))); - return; - } - - icu::Collator* collator = UnpackCollator(args[0]->ToObject()); - if (!collator) { - ThrowUnexpectedObjectError(); - return; - } - - v8::String::Value string_value1(args[1]); - v8::String::Value string_value2(args[2]); - const UChar* string1 = reinterpret_cast(*string_value1); - const UChar* string2 = reinterpret_cast(*string_value2); - UErrorCode status = U_ZERO_ERROR; - UCollationResult result = collator->compare( - string1, string_value1.length(), string2, string_value2.length(), status); - - if (U_FAILURE(status)) { - ThrowExceptionForICUError( - "Internal error. Unexpected failure in Collator.compare."); - return; - } - - args.GetReturnValue().Set(result); -} - -void Collator::JSCreateCollator( - const v8::FunctionCallbackInfo& args) { - if (args.Length() != 3 || !args[0]->IsString() || !args[1]->IsObject() || - !args[2]->IsObject()) { - v8::ThrowException(v8::Exception::SyntaxError( - v8::String::New("Internal error, wrong parameters."))); - return; - } - - v8::Isolate* isolate = args.GetIsolate(); - v8::Local intl_collator_template = - Utils::GetTemplate(isolate); - - // Create an empty object wrapper. - v8::Local local_object = intl_collator_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 collator as internal field of the resulting JS object. - icu::Collator* collator = InitializeCollator( - args[0]->ToString(), args[1]->ToObject(), args[2]->ToObject()); - - if (!collator) { - v8::ThrowException(v8::Exception::Error(v8::String::New( - "Internal error. Couldn't create ICU collator."))); - return; - } else { - local_object->SetAlignedPointerInInternalField(0, collator); - - // Make it safer to unpack later on. - v8::TryCatch try_catch; - local_object->Set(v8::String::New("collator"), 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 wrapper(isolate, local_object); - // Make object handle weak so we can delete iterator once GC kicks in. - wrapper.MakeWeak(NULL, &DeleteCollator); - args.GetReturnValue().Set(wrapper); - wrapper.ClearAndLeak(); -} - -static icu::Collator* InitializeCollator(v8::Handle locale, - v8::Handle options, - v8::Handle 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::Collator* collator = CreateICUCollator(icu_locale, options); - if (!collator) { - // Remove extensions and try again. - icu::Locale no_extension_locale(icu_locale.getBaseName()); - collator = CreateICUCollator(no_extension_locale, options); - - // Set resolved settings (pattern, numbering system). - SetResolvedSettings(no_extension_locale, collator, resolved); - } else { - SetResolvedSettings(icu_locale, collator, resolved); - } - - return collator; -} - -static icu::Collator* CreateICUCollator( - const icu::Locale& icu_locale, v8::Handle options) { - // Make collator from options. - icu::Collator* collator = NULL; - UErrorCode status = U_ZERO_ERROR; - collator = icu::Collator::createInstance(icu_locale, status); - - if (U_FAILURE(status)) { - delete collator; - return NULL; - } - - // Set flags first, and then override them with sensitivity if necessary. - SetBooleanAttribute(UCOL_NUMERIC_COLLATION, "numeric", options, collator); - - // Normalization is always on, by the spec. We are free to optimize - // if the strings are already normalized (but we don't have a way to tell - // that right now). - collator->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status); - - icu::UnicodeString case_first; - if (Utils::ExtractStringSetting(options, "caseFirst", &case_first)) { - if (case_first == UNICODE_STRING_SIMPLE("upper")) { - collator->setAttribute(UCOL_CASE_FIRST, UCOL_UPPER_FIRST, status); - } else if (case_first == UNICODE_STRING_SIMPLE("lower")) { - collator->setAttribute(UCOL_CASE_FIRST, UCOL_LOWER_FIRST, status); - } else { - // Default (false/off). - collator->setAttribute(UCOL_CASE_FIRST, UCOL_OFF, status); - } - } - - icu::UnicodeString sensitivity; - if (Utils::ExtractStringSetting(options, "sensitivity", &sensitivity)) { - if (sensitivity == UNICODE_STRING_SIMPLE("base")) { - collator->setStrength(icu::Collator::PRIMARY); - } else if (sensitivity == UNICODE_STRING_SIMPLE("accent")) { - collator->setStrength(icu::Collator::SECONDARY); - } else if (sensitivity == UNICODE_STRING_SIMPLE("case")) { - collator->setStrength(icu::Collator::PRIMARY); - collator->setAttribute(UCOL_CASE_LEVEL, UCOL_ON, status); - } else { - // variant (default) - collator->setStrength(icu::Collator::TERTIARY); - } - } - - bool ignore; - if (Utils::ExtractBooleanSetting(options, "ignorePunctuation", &ignore)) { - if (ignore) { - collator->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_SHIFTED, status); - } - } - - return collator; -} - -static bool SetBooleanAttribute(UColAttribute attribute, - const char* name, - v8::Handle options, - icu::Collator* collator) { - UErrorCode status = U_ZERO_ERROR; - bool result; - if (Utils::ExtractBooleanSetting(options, name, &result)) { - collator->setAttribute(attribute, result ? UCOL_ON : UCOL_OFF, status); - if (U_FAILURE(status)) { - return false; - } - } - - return true; -} - -static void SetResolvedSettings(const icu::Locale& icu_locale, - icu::Collator* collator, - v8::Handle resolved) { - SetBooleanSetting(UCOL_NUMERIC_COLLATION, collator, "numeric", resolved); - - UErrorCode status = U_ZERO_ERROR; - - switch (collator->getAttribute(UCOL_CASE_FIRST, status)) { - case UCOL_LOWER_FIRST: - resolved->Set(v8::String::New("caseFirst"), v8::String::New("lower")); - break; - case UCOL_UPPER_FIRST: - resolved->Set(v8::String::New("caseFirst"), v8::String::New("upper")); - break; - default: - resolved->Set(v8::String::New("caseFirst"), v8::String::New("false")); - } - - switch (collator->getAttribute(UCOL_STRENGTH, status)) { - case UCOL_PRIMARY: { - resolved->Set(v8::String::New("strength"), v8::String::New("primary")); - - // case level: true + s1 -> case, s1 -> base. - if (UCOL_ON == collator->getAttribute(UCOL_CASE_LEVEL, status)) { - resolved->Set(v8::String::New("sensitivity"), v8::String::New("case")); - } else { - resolved->Set(v8::String::New("sensitivity"), v8::String::New("base")); - } - break; - } - case UCOL_SECONDARY: - resolved->Set(v8::String::New("strength"), v8::String::New("secondary")); - resolved->Set(v8::String::New("sensitivity"), v8::String::New("accent")); - break; - case UCOL_TERTIARY: - resolved->Set(v8::String::New("strength"), v8::String::New("tertiary")); - resolved->Set(v8::String::New("sensitivity"), v8::String::New("variant")); - break; - case UCOL_QUATERNARY: - // We shouldn't get quaternary and identical from ICU, but if we do - // put them into variant. - resolved->Set(v8::String::New("strength"), v8::String::New("quaternary")); - resolved->Set(v8::String::New("sensitivity"), v8::String::New("variant")); - break; - default: - resolved->Set(v8::String::New("strength"), v8::String::New("identical")); - resolved->Set(v8::String::New("sensitivity"), v8::String::New("variant")); - } - - if (UCOL_SHIFTED == collator->getAttribute(UCOL_ALTERNATE_HANDLING, status)) { - resolved->Set(v8::String::New("ignorePunctuation"), - v8::Boolean::New(true)); - } else { - resolved->Set(v8::String::New("ignorePunctuation"), - v8::Boolean::New(false)); - } - - // 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")); - } -} - -static void SetBooleanSetting(UColAttribute attribute, - icu::Collator* collator, - const char* property, - v8::Handle resolved) { - UErrorCode status = U_ZERO_ERROR; - if (UCOL_ON == collator->getAttribute(attribute, status)) { - resolved->Set(v8::String::New(property), v8::Boolean::New(true)); - } else { - resolved->Set(v8::String::New(property), v8::Boolean::New(false)); - } -} - -} // namespace v8_i18n diff --git a/src/extensions/i18n/collator.h b/src/extensions/i18n/collator.h deleted file mode 100644 index a3991b9ed..000000000 --- a/src/extensions/i18n/collator.h +++ /dev/null @@ -1,68 +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_COLLATOR_H_ -#define V8_EXTENSIONS_I18N_COLLATOR_H_ - -#include "unicode/uversion.h" -#include "v8.h" - -namespace U_ICU_NAMESPACE { -class Collator; -class UnicodeString; -} - -namespace v8_i18n { - -class Collator { - public: - static void JSCreateCollator(const v8::FunctionCallbackInfo& args); - - // Helper methods for various bindings. - - // Unpacks collator object from corresponding JavaScript object. - static icu::Collator* UnpackCollator(v8::Handle obj); - - // Release memory we allocated for the Collator once the JS object that - // holds the pointer gets garbage collected. - static void DeleteCollator(v8::Isolate* isolate, - v8::Persistent* object, - void* param); - - // Compare two strings and returns -1, 0 and 1 depending on - // whether string1 is smaller than, equal to or larger than string2. - static void JSInternalCompare( - const v8::FunctionCallbackInfo& args); - - private: - Collator() {} -}; - -} // namespace v8_i18n - -#endif // V8_EXTENSIONS_I18N_COLLATOR_H_ diff --git a/src/extensions/i18n/collator.js b/src/extensions/i18n/collator.js index 3483515be..d8d247b36 100644 --- a/src/extensions/i18n/collator.js +++ b/src/extensions/i18n/collator.js @@ -35,8 +35,6 @@ * Useful for subclassing. */ function initializeCollator(collator, locales, options) { - native function NativeJSCreateCollator(); - if (collator.hasOwnProperty('__initializedIntlObject')) { throw new TypeError('Trying to re-initialize Collator object.'); } @@ -103,9 +101,9 @@ function initializeCollator(collator, locales, options) { usage: {value: internalOptions.usage, writable: true} }); - var internalCollator = NativeJSCreateCollator(requestedLocale, - internalOptions, - resolved); + var internalCollator = %CreateCollator(requestedLocale, + internalOptions, + resolved); // Writable, configurable and enumerable are set to false by default. Object.defineProperty(collator, 'collator', {value: internalCollator}); @@ -204,8 +202,7 @@ function initializeCollator(collator, locales, options) { * the sort order, or x comes after y in the sort order, respectively. */ function compare(collator, x, y) { - native function NativeJSInternalCompare(); - return NativeJSInternalCompare(collator.collator, String(x), String(y)); + return %InternalCompare(collator.collator, String(x), String(y)); }; diff --git a/src/extensions/i18n/i18n-extension.cc b/src/extensions/i18n/i18n-extension.cc index ef39d4523..e2cba8eb9 100644 --- a/src/extensions/i18n/i18n-extension.cc +++ b/src/extensions/i18n/i18n-extension.cc @@ -29,7 +29,6 @@ #include "i18n-extension.h" #include "break-iterator.h" -#include "collator.h" #include "natives.h" using v8::internal::I18NNatives; @@ -46,13 +45,6 @@ Extension::Extension() v8::Handle Extension::GetNativeFunction( v8::Handle name) { - // Collator. - if (name->Equals(v8::String::New("NativeJSCreateCollator"))) { - return v8::FunctionTemplate::New(Collator::JSCreateCollator); - } else if (name->Equals(v8::String::New("NativeJSInternalCompare"))) { - return v8::FunctionTemplate::New(Collator::JSInternalCompare); - } - // Break iterator. if (name->Equals(v8::String::New("NativeJSCreateBreakIterator"))) { return v8::FunctionTemplate::New(BreakIterator::JSCreateBreakIterator); diff --git a/src/i18n.cc b/src/i18n.cc index bd9d9c31a..e58e86823 100644 --- a/src/i18n.cc +++ b/src/i18n.cc @@ -29,6 +29,7 @@ #include "i18n.h" #include "unicode/calendar.h" +#include "unicode/coll.h" #include "unicode/curramt.h" #include "unicode/dcfmtsym.h" #include "unicode/decimfmt.h" @@ -40,6 +41,7 @@ #include "unicode/smpdtfmt.h" #include "unicode/timezone.h" #include "unicode/uchar.h" +#include "unicode/ucol.h" #include "unicode/ucurr.h" #include "unicode/unum.h" #include "unicode/uversion.h" @@ -505,6 +507,228 @@ void SetResolvedNumberSettings(Isolate* isolate, } } + +icu::Collator* CreateICUCollator( + Isolate* isolate, + const icu::Locale& icu_locale, + Handle options) { + // Make collator from options. + icu::Collator* collator = NULL; + UErrorCode status = U_ZERO_ERROR; + collator = icu::Collator::createInstance(icu_locale, status); + + if (U_FAILURE(status)) { + delete collator; + return NULL; + } + + // Set flags first, and then override them with sensitivity if necessary. + bool numeric; + if (ExtractBooleanSetting(isolate, options, "numeric", &numeric)) { + collator->setAttribute( + UCOL_NUMERIC_COLLATION, numeric ? UCOL_ON : UCOL_OFF, status); + } + + // Normalization is always on, by the spec. We are free to optimize + // if the strings are already normalized (but we don't have a way to tell + // that right now). + collator->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status); + + icu::UnicodeString case_first; + if (ExtractStringSetting(isolate, options, "caseFirst", &case_first)) { + if (case_first == UNICODE_STRING_SIMPLE("upper")) { + collator->setAttribute(UCOL_CASE_FIRST, UCOL_UPPER_FIRST, status); + } else if (case_first == UNICODE_STRING_SIMPLE("lower")) { + collator->setAttribute(UCOL_CASE_FIRST, UCOL_LOWER_FIRST, status); + } else { + // Default (false/off). + collator->setAttribute(UCOL_CASE_FIRST, UCOL_OFF, status); + } + } + + icu::UnicodeString sensitivity; + if (ExtractStringSetting(isolate, options, "sensitivity", &sensitivity)) { + if (sensitivity == UNICODE_STRING_SIMPLE("base")) { + collator->setStrength(icu::Collator::PRIMARY); + } else if (sensitivity == UNICODE_STRING_SIMPLE("accent")) { + collator->setStrength(icu::Collator::SECONDARY); + } else if (sensitivity == UNICODE_STRING_SIMPLE("case")) { + collator->setStrength(icu::Collator::PRIMARY); + collator->setAttribute(UCOL_CASE_LEVEL, UCOL_ON, status); + } else { + // variant (default) + collator->setStrength(icu::Collator::TERTIARY); + } + } + + bool ignore; + if (ExtractBooleanSetting(isolate, options, "ignorePunctuation", &ignore)) { + if (ignore) { + collator->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_SHIFTED, status); + } + } + + return collator; +} + + +void SetResolvedCollatorSettings(Isolate* isolate, + const icu::Locale& icu_locale, + icu::Collator* collator, + Handle resolved) { + UErrorCode status = U_ZERO_ERROR; + + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("numeric")), + isolate->factory()->ToBoolean( + collator->getAttribute(UCOL_NUMERIC_COLLATION, status) == UCOL_ON), + NONE, + kNonStrictMode); + + switch (collator->getAttribute(UCOL_CASE_FIRST, status)) { + case UCOL_LOWER_FIRST: + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("caseFirst")), + isolate->factory()->NewStringFromAscii(CStrVector("lower")), + NONE, + kNonStrictMode); + break; + case UCOL_UPPER_FIRST: + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("caseFirst")), + isolate->factory()->NewStringFromAscii(CStrVector("upper")), + NONE, + kNonStrictMode); + break; + default: + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("caseFirst")), + isolate->factory()->NewStringFromAscii(CStrVector("false")), + NONE, + kNonStrictMode); + } + + switch (collator->getAttribute(UCOL_STRENGTH, status)) { + case UCOL_PRIMARY: { + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("strength")), + isolate->factory()->NewStringFromAscii(CStrVector("primary")), + NONE, + kNonStrictMode); + + // case level: true + s1 -> case, s1 -> base. + if (UCOL_ON == collator->getAttribute(UCOL_CASE_LEVEL, status)) { + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("sensitivity")), + isolate->factory()->NewStringFromAscii(CStrVector("case")), + NONE, + kNonStrictMode); + } else { + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("sensitivity")), + isolate->factory()->NewStringFromAscii(CStrVector("base")), + NONE, + kNonStrictMode); + } + break; + } + case UCOL_SECONDARY: + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("strength")), + isolate->factory()->NewStringFromAscii(CStrVector("secondary")), + NONE, + kNonStrictMode); + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("sensitivity")), + isolate->factory()->NewStringFromAscii(CStrVector("accent")), + NONE, + kNonStrictMode); + break; + case UCOL_TERTIARY: + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("strength")), + isolate->factory()->NewStringFromAscii(CStrVector("tertiary")), + NONE, + kNonStrictMode); + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("sensitivity")), + isolate->factory()->NewStringFromAscii(CStrVector("variant")), + NONE, + kNonStrictMode); + break; + case UCOL_QUATERNARY: + // We shouldn't get quaternary and identical from ICU, but if we do + // put them into variant. + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("strength")), + isolate->factory()->NewStringFromAscii(CStrVector("quaternary")), + NONE, + kNonStrictMode); + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("sensitivity")), + isolate->factory()->NewStringFromAscii(CStrVector("variant")), + NONE, + kNonStrictMode); + break; + default: + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("strength")), + isolate->factory()->NewStringFromAscii(CStrVector("identical")), + NONE, + kNonStrictMode); + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("sensitivity")), + isolate->factory()->NewStringFromAscii(CStrVector("variant")), + NONE, + kNonStrictMode); + } + + JSObject::SetProperty( + resolved, + isolate->factory()->NewStringFromAscii(CStrVector("ignorePunctuation")), + isolate->factory()->ToBoolean(collator->getAttribute( + UCOL_ALTERNATE_HANDLING, status) == UCOL_SHIFTED), + 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 @@ -646,4 +870,64 @@ void NumberFormat::DeleteNumberFormat(v8::Isolate* isolate, object->Dispose(isolate); } + +icu::Collator* Collator::InitializeCollator( + Isolate* isolate, + Handle locale, + Handle options, + Handle 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::Collator* collator = CreateICUCollator(isolate, icu_locale, options); + if (!collator) { + // Remove extensions and try again. + icu::Locale no_extension_locale(icu_locale.getBaseName()); + collator = CreateICUCollator(isolate, no_extension_locale, options); + + // Set resolved settings (pattern, numbering system). + SetResolvedCollatorSettings( + isolate, no_extension_locale, collator, resolved); + } else { + SetResolvedCollatorSettings(isolate, icu_locale, collator, resolved); + } + + return collator; +} + + +icu::Collator* Collator::UnpackCollator(Isolate* isolate, + Handle obj) { + if (obj->HasLocalProperty(*isolate->factory()->NewStringFromAscii( + CStrVector("collator")))) { + return reinterpret_cast(obj->GetInternalField(0)); + } + + return NULL; +} + + +void Collator::DeleteCollator(v8::Isolate* isolate, + Persistent* object, + void* param) { + // First delete the hidden C++ object. + delete reinterpret_cast(Handle::cast( + v8::Utils::OpenPersistent(object))->GetInternalField(0)); + + // Then dispose of the persistent handle to JS object. + object->Dispose(isolate); +} + } } // namespace v8::internal diff --git a/src/i18n.h b/src/i18n.h index b9cbed1bb..5825ab6c6 100644 --- a/src/i18n.h +++ b/src/i18n.h @@ -33,6 +33,7 @@ #include "v8.h" namespace U_ICU_NAMESPACE { +class Collator; class DecimalFormat; class SimpleDateFormat; } @@ -100,6 +101,29 @@ class NumberFormat { NumberFormat(); }; + +class Collator { + public: + // Create a collator for the specificied locale and options. Returns the + // resolved settings for the locale / options. + static icu::Collator* InitializeCollator( + Isolate* isolate, + Handle locale, + Handle options, + Handle resolved); + + // Unpacks collator object from corresponding JavaScript object. + static icu::Collator* UnpackCollator(Isolate* isolate, Handle obj); + + // Release memory we allocated for the Collator once the JS object that holds + // the pointer gets garbage collected. + static void DeleteCollator(v8::Isolate* isolate, + Persistent* object, + void* param); + private: + Collator(); +}; + } } // namespace v8::internal #endif // V8_I18N_H_ diff --git a/src/runtime.cc b/src/runtime.cc index a9121ce85..621359415 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -83,6 +83,7 @@ #include "unicode/smpdtfmt.h" #include "unicode/timezone.h" #include "unicode/uchar.h" +#include "unicode/ucol.h" #include "unicode/ucurr.h" #include "unicode/uloc.h" #include "unicode/unum.h" @@ -13795,6 +13796,79 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InternalNumberParse) { return isolate->heap()->undefined_value(); } } + + +RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateCollator) { + 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 collator_template = I18N::GetTemplate(isolate); + + // Create an empty object wrapper. + bool has_pending_exception = false; + Handle local_object = Execution::InstantiateObject( + collator_template, &has_pending_exception); + if (has_pending_exception) { + ASSERT(isolate->has_pending_exception()); + return Failure::Exception(); + } + + // Set collator as internal field of the resulting JS object. + icu::Collator* collator = Collator::InitializeCollator( + isolate, locale, options, resolved); + + if (!collator) return isolate->ThrowIllegalOperation(); + + local_object->SetInternalField(0, reinterpret_cast(collator)); + + RETURN_IF_EMPTY_HANDLE(isolate, + JSObject::SetLocalPropertyIgnoreAttributes( + local_object, + isolate->factory()->NewStringFromAscii(CStrVector("collator")), + isolate->factory()->NewStringFromAscii(CStrVector("valid")), + NONE)); + + Persistent wrapper(reinterpret_cast(isolate), + v8::Utils::ToLocal(local_object)); + // Make object handle weak so we can delete the collator once GC kicks in. + wrapper.MakeWeak(NULL, &Collator::DeleteCollator); + Handle result = Utils::OpenPersistent(wrapper); + wrapper.ClearAndLeak(); + return *result; +} + + +RUNTIME_FUNCTION(MaybeObject*, Runtime_InternalCompare) { + HandleScope scope(isolate); + + ASSERT(args.length() == 3); + + CONVERT_ARG_HANDLE_CHECKED(JSObject, collator_holder, 0); + CONVERT_ARG_HANDLE_CHECKED(String, string1, 1); + CONVERT_ARG_HANDLE_CHECKED(String, string2, 2); + + icu::Collator* collator = Collator::UnpackCollator(isolate, collator_holder); + if (!collator) return isolate->ThrowIllegalOperation(); + + v8::String::Value string_value1(v8::Utils::ToLocal(string1)); + v8::String::Value string_value2(v8::Utils::ToLocal(string2)); + const UChar* u_string1 = reinterpret_cast(*string_value1); + const UChar* u_string2 = reinterpret_cast(*string_value2); + UErrorCode status = U_ZERO_ERROR; + UCollationResult result = collator->compare(u_string1, + string_value1.length(), + u_string2, + string_value2.length(), + status); + if (U_FAILURE(status)) return isolate->ThrowIllegalOperation(); + + return *isolate->factory()->NewNumberFromInt(result); +} #endif // V8_I18N_SUPPORT diff --git a/src/runtime.h b/src/runtime.h index 9abec6282..23e91f2ae 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -553,6 +553,10 @@ namespace internal { F(CreateNumberFormat, 3, 1) \ F(InternalNumberFormat, 2, 1) \ F(InternalNumberParse, 2, 1) \ + \ + /* Collator. */ \ + F(CreateCollator, 3, 1) \ + F(InternalCompare, 3, 1) \ #else #define RUNTIME_FUNCTION_LIST_I18N_SUPPORT(F) diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp index 14efabdc2..66376c17d 100644 --- a/tools/gyp/v8.gyp +++ b/tools/gyp/v8.gyp @@ -827,8 +827,6 @@ '../../src/i18n.h', '../../src/extensions/i18n/break-iterator.cc', '../../src/extensions/i18n/break-iterator.h', - '../../src/extensions/i18n/collator.cc', - '../../src/extensions/i18n/collator.h', '../../src/extensions/i18n/i18n-extension.cc', '../../src/extensions/i18n/i18n-extension.h', '../../src/extensions/i18n/i18n-utils.cc',