1 // Copyright 2011 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 #include "break-iterator.h" 29 30 #include "unicode/brkiter.h" 31 #include "unicode/locid.h" 32 #include "unicode/rbbi.h" 33 34 namespace v8 { 35 namespace internal { 36 37 v8::Persistent<v8::FunctionTemplate> BreakIterator::break_iterator_template_; 38 39 icu::BreakIterator* BreakIterator::UnpackBreakIterator( 40 v8::Handle<v8::Object> obj) { 41 if (break_iterator_template_->HasInstance(obj)) { 42 return static_cast<icu::BreakIterator*>( 43 obj->GetPointerFromInternalField(0)); 44 } 45 46 return NULL; 47 } 48 49 icu::UnicodeString* BreakIterator::ResetAdoptedText( 50 v8::Handle<v8::Object> obj, v8::Handle<v8::Value> value) { 51 // Get the previous value from the internal field. 52 icu::UnicodeString* text = static_cast<icu::UnicodeString*>( 53 obj->GetPointerFromInternalField(1)); 54 delete text; 55 56 // Assign new value to the internal pointer. 57 v8::String::Value text_value(value); 58 text = new icu::UnicodeString( 59 reinterpret_cast<const UChar*>(*text_value), text_value.length()); 60 obj->SetPointerInInternalField(1, text); 61 62 // Return new unicode string pointer. 63 return text; 64 } 65 66 void BreakIterator::DeleteBreakIterator(v8::Persistent<v8::Value> object, 67 void* param) { 68 v8::Persistent<v8::Object> persistent_object = 69 v8::Persistent<v8::Object>::Cast(object); 70 71 // First delete the hidden C++ object. 72 // Unpacking should never return NULL here. That would only happen if 73 // this method is used as the weak callback for persistent handles not 74 // pointing to a break iterator. 75 delete UnpackBreakIterator(persistent_object); 76 77 delete static_cast<icu::UnicodeString*>( 78 persistent_object->GetPointerFromInternalField(1)); 79 80 // Then dispose of the persistent handle to JS object. 81 persistent_object.Dispose(); 82 } 83 84 // Throws a JavaScript exception. 85 static v8::Handle<v8::Value> ThrowUnexpectedObjectError() { 86 // Returns undefined, and schedules an exception to be thrown. 87 return v8::ThrowException(v8::Exception::Error( 88 v8::String::New("BreakIterator method called on an object " 89 "that is not a BreakIterator."))); 90 } 91 92 v8::Handle<v8::Value> BreakIterator::BreakIteratorAdoptText( 93 const v8::Arguments& args) { 94 if (args.Length() != 1 || !args[0]->IsString()) { 95 return v8::ThrowException(v8::Exception::SyntaxError( 96 v8::String::New("Text input is required."))); 97 } 98 99 icu::BreakIterator* break_iterator = UnpackBreakIterator(args.Holder()); 100 if (!break_iterator) { 101 return ThrowUnexpectedObjectError(); 102 } 103 104 break_iterator->setText(*ResetAdoptedText(args.Holder(), args[0])); 105 106 return v8::Undefined(); 107 } 108 109 v8::Handle<v8::Value> BreakIterator::BreakIteratorFirst( 110 const v8::Arguments& args) { 111 icu::BreakIterator* break_iterator = UnpackBreakIterator(args.Holder()); 112 if (!break_iterator) { 113 return ThrowUnexpectedObjectError(); 114 } 115 116 return v8::Int32::New(break_iterator->first()); 117 } 118 119 v8::Handle<v8::Value> BreakIterator::BreakIteratorNext( 120 const v8::Arguments& args) { 121 icu::BreakIterator* break_iterator = UnpackBreakIterator(args.Holder()); 122 if (!break_iterator) { 123 return ThrowUnexpectedObjectError(); 124 } 125 126 return v8::Int32::New(break_iterator->next()); 127 } 128 129 v8::Handle<v8::Value> BreakIterator::BreakIteratorCurrent( 130 const v8::Arguments& args) { 131 icu::BreakIterator* break_iterator = UnpackBreakIterator(args.Holder()); 132 if (!break_iterator) { 133 return ThrowUnexpectedObjectError(); 134 } 135 136 return v8::Int32::New(break_iterator->current()); 137 } 138 139 v8::Handle<v8::Value> BreakIterator::BreakIteratorBreakType( 140 const v8::Arguments& args) { 141 icu::BreakIterator* break_iterator = UnpackBreakIterator(args.Holder()); 142 if (!break_iterator) { 143 return ThrowUnexpectedObjectError(); 144 } 145 146 // TODO(cira): Remove cast once ICU fixes base BreakIterator class. 147 icu::RuleBasedBreakIterator* rule_based_iterator = 148 static_cast<icu::RuleBasedBreakIterator*>(break_iterator); 149 int32_t status = rule_based_iterator->getRuleStatus(); 150 // Keep return values in sync with JavaScript BreakType enum. 151 if (status >= UBRK_WORD_NONE && status < UBRK_WORD_NONE_LIMIT) { 152 return v8::Int32::New(UBRK_WORD_NONE); 153 } else if (status >= UBRK_WORD_NUMBER && status < UBRK_WORD_NUMBER_LIMIT) { 154 return v8::Int32::New(UBRK_WORD_NUMBER); 155 } else if (status >= UBRK_WORD_LETTER && status < UBRK_WORD_LETTER_LIMIT) { 156 return v8::Int32::New(UBRK_WORD_LETTER); 157 } else if (status >= UBRK_WORD_KANA && status < UBRK_WORD_KANA_LIMIT) { 158 return v8::Int32::New(UBRK_WORD_KANA); 159 } else if (status >= UBRK_WORD_IDEO && status < UBRK_WORD_IDEO_LIMIT) { 160 return v8::Int32::New(UBRK_WORD_IDEO); 161 } else { 162 return v8::Int32::New(-1); 163 } 164 } 165 166 v8::Handle<v8::Value> BreakIterator::JSBreakIterator( 167 const v8::Arguments& args) { 168 v8::HandleScope handle_scope; 169 170 if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsString()) { 171 return v8::ThrowException(v8::Exception::SyntaxError( 172 v8::String::New("Locale and iterator type are required."))); 173 } 174 175 v8::String::Utf8Value locale(args[0]); 176 icu::Locale icu_locale(*locale); 177 178 UErrorCode status = U_ZERO_ERROR; 179 icu::BreakIterator* break_iterator = NULL; 180 v8::String::Utf8Value type(args[1]); 181 if (!strcmp(*type, "character")) { 182 break_iterator = 183 icu::BreakIterator::createCharacterInstance(icu_locale, status); 184 } else if (!strcmp(*type, "word")) { 185 break_iterator = 186 icu::BreakIterator::createWordInstance(icu_locale, status); 187 } else if (!strcmp(*type, "sentence")) { 188 break_iterator = 189 icu::BreakIterator::createSentenceInstance(icu_locale, status); 190 } else if (!strcmp(*type, "line")) { 191 break_iterator = 192 icu::BreakIterator::createLineInstance(icu_locale, status); 193 } else { 194 return v8::ThrowException(v8::Exception::SyntaxError( 195 v8::String::New("Invalid iterator type."))); 196 } 197 198 if (U_FAILURE(status)) { 199 delete break_iterator; 200 return v8::ThrowException(v8::Exception::Error( 201 v8::String::New("Failed to create break iterator."))); 202 } 203 204 if (break_iterator_template_.IsEmpty()) { 205 v8::Local<v8::FunctionTemplate> raw_template(v8::FunctionTemplate::New()); 206 207 raw_template->SetClassName(v8::String::New("v8Locale.v8BreakIterator")); 208 209 // Define internal field count on instance template. 210 v8::Local<v8::ObjectTemplate> object_template = 211 raw_template->InstanceTemplate(); 212 213 // Set aside internal fields for icu break iterator and adopted text. 214 object_template->SetInternalFieldCount(2); 215 216 // Define all of the prototype methods on prototype template. 217 v8::Local<v8::ObjectTemplate> proto = raw_template->PrototypeTemplate(); 218 proto->Set(v8::String::New("adoptText"), 219 v8::FunctionTemplate::New(BreakIteratorAdoptText)); 220 proto->Set(v8::String::New("first"), 221 v8::FunctionTemplate::New(BreakIteratorFirst)); 222 proto->Set(v8::String::New("next"), 223 v8::FunctionTemplate::New(BreakIteratorNext)); 224 proto->Set(v8::String::New("current"), 225 v8::FunctionTemplate::New(BreakIteratorCurrent)); 226 proto->Set(v8::String::New("breakType"), 227 v8::FunctionTemplate::New(BreakIteratorBreakType)); 228 229 break_iterator_template_ = 230 v8::Persistent<v8::FunctionTemplate>::New(raw_template); 231 } 232 233 // Create an empty object wrapper. 234 v8::Local<v8::Object> local_object = 235 break_iterator_template_->GetFunction()->NewInstance(); 236 v8::Persistent<v8::Object> wrapper = 237 v8::Persistent<v8::Object>::New(local_object); 238 239 // Set break iterator as internal field of the resulting JS object. 240 wrapper->SetPointerInInternalField(0, break_iterator); 241 // Make sure that the pointer to adopted text is NULL. 242 wrapper->SetPointerInInternalField(1, NULL); 243 244 // Make object handle weak so we can delete iterator once GC kicks in. 245 wrapper.MakeWeak(NULL, DeleteBreakIterator); 246 247 return wrapper; 248 } 249 250 } } // namespace v8::internal 251