From: cira@chromium.org Date: Thu, 14 Apr 2011 19:10:51 +0000 (+0000) Subject: Add v8Locale.Collator X-Git-Tag: upstream/4.7.83~19615 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=8aaa0c6b18eaff1a65099993f91200fd90957a1e;p=platform%2Fupstream%2Fv8.git Add v8Locale.Collator This is a partial implementation of Collator per what's agreed upon at the last ECMAScript meeting + mailing list. Only the following three options are implemented: ignoreAccent, ignoreCase, numeric. ChromeOS and Chrome need this feature for M12. This could be added as chrome extension API. Giiven that we have a rough agreement on the collation part of ECMAScript API, we thought it'd save us some duplicated work adding this to v8 (experimental i18n api) now rather than implementing it in Chrome now and moving it later. BUG=28604 TEST=http://i18nl10n.com/chrome/coll2.html Review URL: http://codereview.chromium.org/6673011 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7620 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/extensions/experimental/collator.cc b/src/extensions/experimental/collator.cc new file mode 100644 index 0000000..7d1a21d --- /dev/null +++ b/src/extensions/experimental/collator.cc @@ -0,0 +1,218 @@ +// Copyright 2011 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. + +#include "collator.h" + +#include "unicode/coll.h" +#include "unicode/locid.h" +#include "unicode/ucol.h" + +namespace v8 { +namespace internal { + +v8::Persistent Collator::collator_template_; + +icu::Collator* Collator::UnpackCollator(v8::Handle obj) { + if (collator_template_->HasInstance(obj)) { + return static_cast(obj->GetPointerFromInternalField(0)); + } + + return NULL; +} + +void Collator::DeleteCollator(v8::Persistent object, void* param) { + v8::Persistent persistent_object = + v8::Persistent::Cast(object); + + // 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. + delete UnpackCollator(persistent_object); + + // Then dispose of the persistent handle to JS object. + persistent_object.Dispose(); +} + +// 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."))); +} + +// Extract a boolean option named in |option| and set it to |result|. +// Return true if it's specified. Otherwise, return false. +static bool ExtractBooleanOption(const v8::Local& options, + const char* option, + bool* result) { + v8::HandleScope handle_scope; + v8::TryCatch try_catch; + v8::Handle value = options->Get(v8::String::New(option)); + if (try_catch.HasCaught()) { + return false; + } + // No need to check if |value| is empty because it's taken care of + // by TryCatch above. + if (!value->IsUndefined() && !value->IsNull()) { + if (value->IsBoolean()) { + *result = value->BooleanValue(); + return true; + } + } + return false; +} + +// 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))); +} + +v8::Handle Collator::CollatorCompare(const v8::Arguments& args) { + if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsString()) { + return v8::ThrowException(v8::Exception::SyntaxError( + v8::String::New("Two string arguments are required."))); + } + + icu::Collator* collator = UnpackCollator(args.Holder()); + if (!collator) { + return ThrowUnexpectedObjectError(); + } + + v8::String::Value string_value1(args[0]); + v8::String::Value string_value2(args[1]); + 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)) { + return ThrowExceptionForICUError( + "Unexpected failure in Collator.compare."); + } + + return v8::Int32::New(result); +} + +v8::Handle Collator::JSCollator(const v8::Arguments& args) { + v8::HandleScope handle_scope; + + if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsObject()) { + return v8::ThrowException(v8::Exception::SyntaxError( + v8::String::New("Locale and collation options are required."))); + } + + v8::String::AsciiValue locale(args[0]); + icu::Locale icu_locale(*locale); + + icu::Collator* collator = NULL; + UErrorCode status = U_ZERO_ERROR; + collator = icu::Collator::createInstance(icu_locale, status); + + if (U_FAILURE(status)) { + delete collator; + return ThrowExceptionForICUError("Failed to create collator."); + } + + v8::Local options(args[1]->ToObject()); + + // Below, we change collation options that are explicitly specified + // by a caller in JavaScript. Otherwise, we don't touch because + // we don't want to change the locale-dependent default value. + // The three options below are very likely to have the same default + // across locales, but I haven't checked them all. Others we may add + // in the future have certainly locale-dependent default (e.g. + // caseFirst is upperFirst for Danish while is off for most other locales). + + bool ignore_case, ignore_accents, numeric; + + if (ExtractBooleanOption(options, "ignoreCase", &ignore_case)) { + collator->setAttribute(UCOL_CASE_LEVEL, ignore_case ? UCOL_OFF : UCOL_ON, + status); + if (U_FAILURE(status)) { + delete collator; + return ThrowExceptionForICUError("Failed to set ignoreCase."); + } + } + + // Accents are taken into account with strength secondary or higher. + if (ExtractBooleanOption(options, "ignoreAccents", &ignore_accents)) { + if (!ignore_accents) { + collator->setStrength(icu::Collator::SECONDARY); + } else { + collator->setStrength(icu::Collator::PRIMARY); + } + } + + if (ExtractBooleanOption(options, "numeric", &numeric)) { + collator->setAttribute(UCOL_NUMERIC_COLLATION, + numeric ? UCOL_ON : UCOL_OFF, status); + if (U_FAILURE(status)) { + delete collator; + return ThrowExceptionForICUError("Failed to set numeric sort option."); + } + } + + if (collator_template_.IsEmpty()) { + v8::Local raw_template(v8::FunctionTemplate::New()); + raw_template->SetClassName(v8::String::New("v8Locale.Collator")); + + // Define internal field count on instance template. + v8::Local object_template = + raw_template->InstanceTemplate(); + + // Set aside internal fields for icu collator. + object_template->SetInternalFieldCount(1); + + // Define all of the prototype methods on prototype template. + v8::Local proto = raw_template->PrototypeTemplate(); + proto->Set(v8::String::New("compare"), + v8::FunctionTemplate::New(CollatorCompare)); + + collator_template_ = + v8::Persistent::New(raw_template); + } + + // Create an empty object wrapper. + v8::Local local_object = + collator_template_->GetFunction()->NewInstance(); + v8::Persistent wrapper = + v8::Persistent::New(local_object); + + // Set collator as internal field of the resulting JS object. + wrapper->SetPointerInInternalField(0, collator); + + // Make object handle weak so we can delete iterator once GC kicks in. + wrapper.MakeWeak(NULL, DeleteCollator); + + return wrapper; +} + +} } // namespace v8::internal + diff --git a/src/extensions/experimental/collator.h b/src/extensions/experimental/collator.h new file mode 100644 index 0000000..10d6ffb --- /dev/null +++ b/src/extensions/experimental/collator.h @@ -0,0 +1,69 @@ +// Copyright 2011 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. + +#ifndef V8_EXTENSIONS_EXPERIMENTAL_COLLATOR_H +#define V8_EXTENSIONS_EXPERIMENTAL_COLLATOR_H_ + +#include + +#include "unicode/uversion.h" + +namespace U_ICU_NAMESPACE { +class Collator; +class UnicodeString; +} + +namespace v8 { +namespace internal { + +class Collator { + public: + static v8::Handle JSCollator(const v8::Arguments& 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::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 v8::Handle CollatorCompare(const v8::Arguments& args); + + private: + Collator() {} + + static v8::Persistent collator_template_; +}; + +} } // namespace v8::internal + +#endif // V8_EXTENSIONS_EXPERIMENTAL_COLLATOR + diff --git a/src/extensions/experimental/experimental.gyp b/src/extensions/experimental/experimental.gyp index 473d64c..d02d5f8 100644 --- a/src/extensions/experimental/experimental.gyp +++ b/src/extensions/experimental/experimental.gyp @@ -39,11 +39,13 @@ 'sources': [ 'break-iterator.cc', 'break-iterator.h', + 'collator.cc', + 'collator.h', 'i18n-extension.cc', 'i18n-extension.h', - 'i18n-locale.cc', - 'i18n-locale.h', - '<(SHARED_INTERMEDIATE_DIR)/i18n-js.cc', + 'i18n-locale.cc', + 'i18n-locale.h', + '<(SHARED_INTERMEDIATE_DIR)/i18n-js.cc', ], 'include_dirs': [ '<(icu_src_dir)/public/common', @@ -51,7 +53,7 @@ ], 'dependencies': [ '<(icu_src_dir)/icu.gyp:*', - 'js2c_i18n#host', + 'js2c_i18n#host', '../../../tools/gyp/v8.gyp:v8', ], }, @@ -61,28 +63,28 @@ 'toolsets': ['host'], 'variables': { 'library_files': [ - 'i18n.js' - ], + 'i18n.js' + ], }, 'actions': [ { - 'action_name': 'js2c_i18n', - 'inputs': [ - '../../../tools/js2c.py', - '<@(library_files)', - ], - 'outputs': [ - '<(SHARED_INTERMEDIATE_DIR)/i18n-js.cc', - '<(SHARED_INTERMEDIATE_DIR)/i18n-js-empty.cc' - ], - 'action': [ - 'python', - '../../../tools/js2c.py', - '<@(_outputs)', - 'I18N', - '<@(library_files)' - ], - }, + 'action_name': 'js2c_i18n', + 'inputs': [ + '../../../tools/js2c.py', + '<@(library_files)', + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/i18n-js.cc', + '<(SHARED_INTERMEDIATE_DIR)/i18n-js-empty.cc' + ], + 'action': [ + 'python', + '../../../tools/js2c.py', + '<@(_outputs)', + 'I18N', + '<@(library_files)' + ], + }, ], }, ], # targets diff --git a/src/extensions/experimental/i18n-extension.cc b/src/extensions/experimental/i18n-extension.cc index e5a41f8..56bea23 100644 --- a/src/extensions/experimental/i18n-extension.cc +++ b/src/extensions/experimental/i18n-extension.cc @@ -1,4 +1,4 @@ -// Copyright 2010 the V8 project authors. All rights reserved. +// Copyright 2011 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: @@ -28,6 +28,7 @@ #include "i18n-extension.h" #include "break-iterator.h" +#include "collator.h" #include "i18n-locale.h" #include "natives.h" @@ -70,6 +71,8 @@ v8::Handle I18NExtension::GetNativeFunction( return v8::FunctionTemplate::New(I18NLocale::JSDisplayName); } else if (name->Equals(v8::String::New("NativeJSBreakIterator"))) { return v8::FunctionTemplate::New(BreakIterator::JSBreakIterator); + } else if (name->Equals(v8::String::New("NativeJSCollator"))) { + return v8::FunctionTemplate::New(Collator::JSCollator); } return v8::Handle(); diff --git a/src/extensions/experimental/i18n-extension.h b/src/extensions/experimental/i18n-extension.h index e3f22b6..b4dc7c3 100644 --- a/src/extensions/experimental/i18n-extension.h +++ b/src/extensions/experimental/i18n-extension.h @@ -1,4 +1,4 @@ -// Copyright 2010 the V8 project authors. All rights reserved. +// Copyright 2011 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: diff --git a/src/extensions/experimental/i18n.js b/src/extensions/experimental/i18n.js index baf3859..5a74905 100644 --- a/src/extensions/experimental/i18n.js +++ b/src/extensions/experimental/i18n.js @@ -101,3 +101,16 @@ v8Locale.v8BreakIterator.BreakType = { v8Locale.prototype.v8CreateBreakIterator = function(type) { return new v8Locale.v8BreakIterator(this.locale, type); }; + +// TODO(jungshik): Set |collator.options| to actually recognized / resolved +// values. +v8Locale.Collator = function(locale, options) { + native function NativeJSCollator(); + var collator = NativeJSCollator(locale, + options === undefined ? {} : options); + return collator; +}; + +v8Locale.prototype.createCollator = function(options) { + return new v8Locale.Collator(this.locale, options); +};