ager@chromium.org | 9258b6b | 2008-09-11 09:11:10 +0000 | [diff] [blame] | 1 | // Copyright 2006-2008 the V8 project authors. All rights reserved. |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 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 "v8.h" |
| 29 | |
| 30 | #include "execution.h" |
| 31 | #include "factory.h" |
| 32 | #include "jsregexp.h" |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 33 | #include "platform.h" |
kasperl@chromium.org | 41044eb | 2008-10-06 08:24:46 +0000 | [diff] [blame] | 34 | #include "runtime.h" |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 35 | #include "top.h" |
kasperl@chromium.org | 9fe21c6 | 2008-10-28 08:53:51 +0000 | [diff] [blame^] | 36 | #include "compilation-cache.h" |
| 37 | |
| 38 | // Including pcre.h undefines DEBUG to avoid getting debug output from |
| 39 | // the JSCRE implementation. Make sure to redefine it in debug mode |
| 40 | // after having included the header file. |
| 41 | #ifdef DEBUG |
| 42 | #include "third_party/jscre/pcre.h" |
| 43 | #define DEBUG |
| 44 | #else |
| 45 | #include "third_party/jscre/pcre.h" |
| 46 | #endif |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 47 | |
| 48 | namespace v8 { namespace internal { |
| 49 | |
| 50 | |
| 51 | #define CAPTURE_INDEX 0 |
| 52 | #define INTERNAL_INDEX 1 |
| 53 | |
| 54 | static Failure* malloc_failure; |
| 55 | |
| 56 | static void* JSREMalloc(size_t size) { |
| 57 | Object* obj = Heap::AllocateByteArray(size); |
| 58 | |
| 59 | // If allocation failed, return a NULL pointer to JSRE, and jsRegExpCompile |
| 60 | // will return NULL to the caller, performs GC there. |
| 61 | // Also pass failure information to the caller. |
| 62 | if (obj->IsFailure()) { |
| 63 | malloc_failure = Failure::cast(obj); |
| 64 | return NULL; |
| 65 | } |
| 66 | |
| 67 | // Note: object is unrooted, the caller of jsRegExpCompile must |
| 68 | // create a handle for the return value before doing heap allocation. |
| 69 | return reinterpret_cast<void*>(ByteArray::cast(obj)->GetDataStartAddress()); |
| 70 | } |
| 71 | |
| 72 | |
| 73 | static void JSREFree(void* p) { |
| 74 | USE(p); // Do nothing, memory is garbage collected. |
| 75 | } |
| 76 | |
| 77 | |
| 78 | String* RegExpImpl::last_ascii_string_ = NULL; |
| 79 | String* RegExpImpl::two_byte_cached_string_ = NULL; |
| 80 | |
| 81 | |
| 82 | void RegExpImpl::NewSpaceCollectionPrologue() { |
| 83 | // The two byte string is always in the old space. The Ascii string may be |
| 84 | // in either place. If it is in the old space we don't need to do anything. |
| 85 | if (Heap::InNewSpace(last_ascii_string_)) { |
| 86 | // Invalidate the cache. |
| 87 | last_ascii_string_ = NULL; |
| 88 | two_byte_cached_string_ = NULL; |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | |
| 93 | void RegExpImpl::OldSpaceCollectionPrologue() { |
| 94 | last_ascii_string_ = NULL; |
| 95 | two_byte_cached_string_ = NULL; |
| 96 | } |
| 97 | |
| 98 | |
mads.s.ager@gmail.com | 9a4089a | 2008-09-01 08:55:01 +0000 | [diff] [blame] | 99 | Handle<Object> RegExpImpl::CreateRegExpLiteral(Handle<JSFunction> constructor, |
| 100 | Handle<String> pattern, |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 101 | Handle<String> flags, |
| 102 | bool* has_pending_exception) { |
mads.s.ager@gmail.com | 9a4089a | 2008-09-01 08:55:01 +0000 | [diff] [blame] | 103 | // Ensure that the constructor function has been loaded. |
| 104 | if (!constructor->IsLoaded()) { |
| 105 | LoadLazy(constructor, has_pending_exception); |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 106 | if (*has_pending_exception) return Handle<Object>(Failure::Exception()); |
| 107 | } |
| 108 | // Call the construct code with 2 arguments. |
| 109 | Object** argv[2] = { Handle<Object>::cast(pattern).location(), |
| 110 | Handle<Object>::cast(flags).location() }; |
mads.s.ager@gmail.com | 9a4089a | 2008-09-01 08:55:01 +0000 | [diff] [blame] | 111 | return Execution::New(constructor, 2, argv, has_pending_exception); |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 112 | } |
| 113 | |
| 114 | |
| 115 | // Converts a source string to a 16 bit flat string or a SlicedString containing |
| 116 | // a 16 bit flat string). |
| 117 | Handle<String> RegExpImpl::CachedStringToTwoByte(Handle<String> subject) { |
| 118 | if (*subject == last_ascii_string_) { |
| 119 | ASSERT(two_byte_cached_string_ != NULL); |
| 120 | return Handle<String>(String::cast(two_byte_cached_string_)); |
| 121 | } |
| 122 | Handle<String> two_byte_string = StringToTwoByte(subject); |
| 123 | last_ascii_string_ = *subject; |
| 124 | two_byte_cached_string_ = *two_byte_string; |
| 125 | return two_byte_string; |
| 126 | } |
| 127 | |
| 128 | |
| 129 | // Converts a source string to a 16 bit flat string or a SlicedString containing |
| 130 | // a 16 bit flat string). |
| 131 | Handle<String> RegExpImpl::StringToTwoByte(Handle<String> pattern) { |
| 132 | if (!pattern->IsFlat()) { |
| 133 | FlattenString(pattern); |
| 134 | } |
| 135 | Handle<String> flat_string(pattern->IsConsString() ? |
| 136 | String::cast(ConsString::cast(*pattern)->first()) : |
| 137 | *pattern); |
| 138 | ASSERT(!flat_string->IsConsString()); |
| 139 | ASSERT(flat_string->IsSeqString() || flat_string->IsSlicedString() || |
| 140 | flat_string->IsExternalString()); |
ager@chromium.org | 7c537e2 | 2008-10-16 08:43:32 +0000 | [diff] [blame] | 141 | if (!flat_string->IsAsciiRepresentation()) { |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 142 | return flat_string; |
| 143 | } |
| 144 | |
| 145 | Handle<String> two_byte_string = |
| 146 | Factory::NewRawTwoByteString(flat_string->length(), TENURED); |
| 147 | static StringInputBuffer convert_to_two_byte_buffer; |
| 148 | convert_to_two_byte_buffer.Reset(*flat_string); |
| 149 | for (int i = 0; convert_to_two_byte_buffer.has_more(); i++) { |
| 150 | two_byte_string->Set(i, convert_to_two_byte_buffer.GetNext()); |
| 151 | } |
| 152 | return two_byte_string; |
| 153 | } |
| 154 | |
| 155 | |
kasperl@chromium.org | 9fe21c6 | 2008-10-28 08:53:51 +0000 | [diff] [blame^] | 156 | static JSRegExp::Flags RegExpFlagsFromString(Handle<String> str) { |
| 157 | int flags = JSRegExp::NONE; |
| 158 | for (int i = 0; i < str->length(); i++) { |
| 159 | switch (str->Get(i)) { |
| 160 | case 'i': |
| 161 | flags |= JSRegExp::IGNORE_CASE; |
| 162 | break; |
| 163 | case 'g': |
| 164 | flags |= JSRegExp::GLOBAL; |
| 165 | break; |
| 166 | case 'm': |
| 167 | flags |= JSRegExp::MULTILINE; |
| 168 | break; |
| 169 | } |
| 170 | } |
| 171 | return JSRegExp::Flags(flags); |
| 172 | } |
| 173 | |
| 174 | |
kasperl@chromium.org | 41044eb | 2008-10-06 08:24:46 +0000 | [diff] [blame] | 175 | unibrow::Predicate<unibrow::RegExpSpecialChar, 128> is_reg_exp_special_char; |
| 176 | |
| 177 | |
| 178 | Handle<Object> RegExpImpl::Compile(Handle<JSRegExp> re, |
| 179 | Handle<String> pattern, |
kasperl@chromium.org | 9fe21c6 | 2008-10-28 08:53:51 +0000 | [diff] [blame^] | 180 | Handle<String> flag_str) { |
| 181 | JSRegExp::Flags flags = RegExpFlagsFromString(flag_str); |
| 182 | Handle<FixedArray> cached = CompilationCache::LookupRegExp(pattern, flags); |
| 183 | bool in_cache = !cached.is_null(); |
kasperl@chromium.org | 41044eb | 2008-10-06 08:24:46 +0000 | [diff] [blame] | 184 | Handle<Object> result; |
kasperl@chromium.org | 9fe21c6 | 2008-10-28 08:53:51 +0000 | [diff] [blame^] | 185 | if (in_cache) { |
| 186 | re->set_data(*cached); |
| 187 | result = re; |
kasperl@chromium.org | 41044eb | 2008-10-06 08:24:46 +0000 | [diff] [blame] | 188 | } else { |
kasperl@chromium.org | 9fe21c6 | 2008-10-28 08:53:51 +0000 | [diff] [blame^] | 189 | bool is_atom = !flags.is_ignore_case(); |
| 190 | for (int i = 0; is_atom && i < pattern->length(); i++) { |
| 191 | if (is_reg_exp_special_char.get(pattern->Get(i))) |
| 192 | is_atom = false; |
| 193 | } |
| 194 | if (is_atom) { |
| 195 | result = AtomCompile(re, pattern, flags); |
| 196 | } else { |
| 197 | result = JsreCompile(re, pattern, flags); |
| 198 | } |
| 199 | Object* data = re->data(); |
| 200 | if (data->IsFixedArray()) { |
| 201 | // If compilation succeeded then the data is set on the regexp |
| 202 | // and we can store it in the cache. |
| 203 | Handle<FixedArray> data(FixedArray::cast(re->data())); |
| 204 | CompilationCache::PutRegExp(pattern, flags, data); |
| 205 | } |
kasperl@chromium.org | 41044eb | 2008-10-06 08:24:46 +0000 | [diff] [blame] | 206 | } |
| 207 | |
kasperl@chromium.org | 9fe21c6 | 2008-10-28 08:53:51 +0000 | [diff] [blame^] | 208 | LOG(RegExpCompileEvent(re, in_cache)); |
kasperl@chromium.org | 41044eb | 2008-10-06 08:24:46 +0000 | [diff] [blame] | 209 | return result; |
| 210 | } |
| 211 | |
| 212 | |
| 213 | Handle<Object> RegExpImpl::Exec(Handle<JSRegExp> regexp, |
| 214 | Handle<String> subject, |
| 215 | Handle<Object> index) { |
kasperl@chromium.org | 9fe21c6 | 2008-10-28 08:53:51 +0000 | [diff] [blame^] | 216 | switch (regexp->TypeTag()) { |
kasperl@chromium.org | 41044eb | 2008-10-06 08:24:46 +0000 | [diff] [blame] | 217 | case JSRegExp::JSCRE: |
| 218 | return JsreExec(regexp, subject, index); |
| 219 | case JSRegExp::ATOM: |
| 220 | return AtomExec(regexp, subject, index); |
| 221 | default: |
| 222 | UNREACHABLE(); |
| 223 | return Handle<Object>(); |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | |
| 228 | Handle<Object> RegExpImpl::ExecGlobal(Handle<JSRegExp> regexp, |
| 229 | Handle<String> subject) { |
kasperl@chromium.org | 9fe21c6 | 2008-10-28 08:53:51 +0000 | [diff] [blame^] | 230 | switch (regexp->TypeTag()) { |
kasperl@chromium.org | 41044eb | 2008-10-06 08:24:46 +0000 | [diff] [blame] | 231 | case JSRegExp::JSCRE: |
| 232 | return JsreExecGlobal(regexp, subject); |
| 233 | case JSRegExp::ATOM: |
| 234 | return AtomExecGlobal(regexp, subject); |
| 235 | default: |
| 236 | UNREACHABLE(); |
| 237 | return Handle<Object>(); |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | |
| 242 | Handle<Object> RegExpImpl::AtomCompile(Handle<JSRegExp> re, |
kasperl@chromium.org | 9fe21c6 | 2008-10-28 08:53:51 +0000 | [diff] [blame^] | 243 | Handle<String> pattern, |
| 244 | JSRegExp::Flags flags) { |
| 245 | Factory::SetRegExpData(re, JSRegExp::ATOM, pattern, flags, pattern); |
kasperl@chromium.org | 41044eb | 2008-10-06 08:24:46 +0000 | [diff] [blame] | 246 | return re; |
| 247 | } |
| 248 | |
| 249 | |
| 250 | Handle<Object> RegExpImpl::AtomExec(Handle<JSRegExp> re, |
| 251 | Handle<String> subject, |
| 252 | Handle<Object> index) { |
kasperl@chromium.org | 9fe21c6 | 2008-10-28 08:53:51 +0000 | [diff] [blame^] | 253 | Handle<String> needle(String::cast(re->DataAt(JSRegExp::kAtomPatternIndex))); |
kasperl@chromium.org | 41044eb | 2008-10-06 08:24:46 +0000 | [diff] [blame] | 254 | |
| 255 | uint32_t start_index; |
| 256 | if (!Array::IndexFromObject(*index, &start_index)) { |
| 257 | return Handle<Smi>(Smi::FromInt(-1)); |
| 258 | } |
| 259 | |
| 260 | LOG(RegExpExecEvent(re, start_index, subject)); |
ager@chromium.org | 7c537e2 | 2008-10-16 08:43:32 +0000 | [diff] [blame] | 261 | int value = Runtime::StringMatch(subject, needle, start_index); |
kasperl@chromium.org | 41044eb | 2008-10-06 08:24:46 +0000 | [diff] [blame] | 262 | if (value == -1) return Factory::null_value(); |
ager@chromium.org | 7c537e2 | 2008-10-16 08:43:32 +0000 | [diff] [blame] | 263 | |
| 264 | Handle<FixedArray> array = Factory::NewFixedArray(2); |
kasperl@chromium.org | 5a8ca6c | 2008-10-23 13:57:19 +0000 | [diff] [blame] | 265 | array->set(0, |
| 266 | Smi::FromInt(value), |
| 267 | SKIP_WRITE_BARRIER); |
| 268 | array->set(1, |
| 269 | Smi::FromInt(value + needle->length()), |
| 270 | SKIP_WRITE_BARRIER); |
ager@chromium.org | 7c537e2 | 2008-10-16 08:43:32 +0000 | [diff] [blame] | 271 | return Factory::NewJSArrayWithElements(array); |
kasperl@chromium.org | 41044eb | 2008-10-06 08:24:46 +0000 | [diff] [blame] | 272 | } |
| 273 | |
| 274 | |
| 275 | Handle<Object> RegExpImpl::AtomExecGlobal(Handle<JSRegExp> re, |
| 276 | Handle<String> subject) { |
kasperl@chromium.org | 9fe21c6 | 2008-10-28 08:53:51 +0000 | [diff] [blame^] | 277 | Handle<String> needle(String::cast(re->DataAt(JSRegExp::kAtomPatternIndex))); |
kasperl@chromium.org | 41044eb | 2008-10-06 08:24:46 +0000 | [diff] [blame] | 278 | Handle<JSArray> result = Factory::NewJSArray(1); |
kasperl@chromium.org | 41044eb | 2008-10-06 08:24:46 +0000 | [diff] [blame] | 279 | int index = 0; |
| 280 | int match_count = 0; |
ager@chromium.org | 7c537e2 | 2008-10-16 08:43:32 +0000 | [diff] [blame] | 281 | int subject_length = subject->length(); |
kasperl@chromium.org | 41044eb | 2008-10-06 08:24:46 +0000 | [diff] [blame] | 282 | int needle_length = needle->length(); |
ager@chromium.org | 7c537e2 | 2008-10-16 08:43:32 +0000 | [diff] [blame] | 283 | while (true) { |
kasperl@chromium.org | 41044eb | 2008-10-06 08:24:46 +0000 | [diff] [blame] | 284 | LOG(RegExpExecEvent(re, index, subject)); |
ager@chromium.org | 7c537e2 | 2008-10-16 08:43:32 +0000 | [diff] [blame] | 285 | int value = -1; |
| 286 | if (index + needle_length <= subject_length) { |
| 287 | value = Runtime::StringMatch(subject, needle, index); |
| 288 | } |
kasperl@chromium.org | 41044eb | 2008-10-06 08:24:46 +0000 | [diff] [blame] | 289 | if (value == -1) break; |
| 290 | HandleScope scope; |
| 291 | int end = value + needle_length; |
ager@chromium.org | 7c537e2 | 2008-10-16 08:43:32 +0000 | [diff] [blame] | 292 | |
| 293 | Handle<FixedArray> array = Factory::NewFixedArray(2); |
kasperl@chromium.org | 5a8ca6c | 2008-10-23 13:57:19 +0000 | [diff] [blame] | 294 | array->set(0, |
| 295 | Smi::FromInt(value), |
| 296 | SKIP_WRITE_BARRIER); |
| 297 | array->set(1, |
| 298 | Smi::FromInt(end), |
| 299 | SKIP_WRITE_BARRIER); |
ager@chromium.org | 7c537e2 | 2008-10-16 08:43:32 +0000 | [diff] [blame] | 300 | Handle<JSArray> pair = Factory::NewJSArrayWithElements(array); |
kasperl@chromium.org | 41044eb | 2008-10-06 08:24:46 +0000 | [diff] [blame] | 301 | SetElement(result, match_count, pair); |
| 302 | match_count++; |
| 303 | index = end; |
ager@chromium.org | 7c537e2 | 2008-10-16 08:43:32 +0000 | [diff] [blame] | 304 | if (needle_length == 0) index++; |
kasperl@chromium.org | 41044eb | 2008-10-06 08:24:46 +0000 | [diff] [blame] | 305 | } |
| 306 | return result; |
| 307 | } |
| 308 | |
| 309 | |
ager@chromium.org | 236ad96 | 2008-09-25 09:45:57 +0000 | [diff] [blame] | 310 | Handle<Object> RegExpImpl::JsreCompile(Handle<JSRegExp> re, |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 311 | Handle<String> pattern, |
kasperl@chromium.org | 9fe21c6 | 2008-10-28 08:53:51 +0000 | [diff] [blame^] | 312 | JSRegExp::Flags flags) { |
| 313 | JSRegExpIgnoreCaseOption case_option = flags.is_ignore_case() |
| 314 | ? JSRegExpIgnoreCase |
| 315 | : JSRegExpDoNotIgnoreCase; |
| 316 | JSRegExpMultilineOption multiline_option = flags.is_multiline() |
| 317 | ? JSRegExpMultiline |
| 318 | : JSRegExpSingleLine; |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 319 | |
| 320 | Handle<String> two_byte_pattern = StringToTwoByte(pattern); |
| 321 | |
| 322 | unsigned number_of_captures; |
| 323 | const char* error_message = NULL; |
| 324 | |
ager@chromium.org | 7c537e2 | 2008-10-16 08:43:32 +0000 | [diff] [blame] | 325 | JscreRegExp* code = NULL; |
| 326 | FlattenString(pattern); |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 327 | |
ager@chromium.org | 7c537e2 | 2008-10-16 08:43:32 +0000 | [diff] [blame] | 328 | bool first_time = true; |
| 329 | |
| 330 | while (true) { |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 331 | malloc_failure = Failure::Exception(); |
| 332 | code = jsRegExpCompile(two_byte_pattern->GetTwoByteData(), |
| 333 | pattern->length(), case_option, |
| 334 | multiline_option, &number_of_captures, |
| 335 | &error_message, &JSREMalloc, &JSREFree); |
ager@chromium.org | 7c537e2 | 2008-10-16 08:43:32 +0000 | [diff] [blame] | 336 | if (code == NULL) { |
| 337 | if (first_time && malloc_failure->IsRetryAfterGC()) { |
| 338 | first_time = false; |
| 339 | if (!Heap::CollectGarbage(malloc_failure->requested(), |
| 340 | malloc_failure->allocation_space())) { |
| 341 | // TODO(1181417): Fix this. |
| 342 | V8::FatalProcessOutOfMemory("RegExpImpl::JsreCompile"); |
| 343 | } |
| 344 | continue; |
| 345 | } |
| 346 | if (malloc_failure->IsRetryAfterGC() || |
| 347 | malloc_failure->IsOutOfMemoryFailure()) { |
| 348 | // TODO(1181417): Fix this. |
| 349 | V8::FatalProcessOutOfMemory("RegExpImpl::JsreCompile"); |
| 350 | } else { |
| 351 | // Throw an exception. |
| 352 | Handle<JSArray> array = Factory::NewJSArray(2); |
| 353 | SetElement(array, 0, pattern); |
| 354 | SetElement(array, 1, Factory::NewStringFromUtf8(CStrVector( |
| 355 | (error_message == NULL) ? "Unknown regexp error" : error_message))); |
| 356 | Handle<Object> regexp_err = |
| 357 | Factory::NewSyntaxError("malformed_regexp", array); |
| 358 | return Handle<Object>(Top::Throw(*regexp_err)); |
| 359 | } |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 360 | } |
ager@chromium.org | 7c537e2 | 2008-10-16 08:43:32 +0000 | [diff] [blame] | 361 | |
| 362 | ASSERT(code != NULL); |
| 363 | // Convert the return address to a ByteArray pointer. |
| 364 | Handle<ByteArray> internal( |
| 365 | ByteArray::FromDataStartAddress(reinterpret_cast<Address>(code))); |
| 366 | |
| 367 | Handle<FixedArray> value = Factory::NewFixedArray(2); |
| 368 | value->set(CAPTURE_INDEX, Smi::FromInt(number_of_captures)); |
| 369 | value->set(INTERNAL_INDEX, *internal); |
kasperl@chromium.org | 9fe21c6 | 2008-10-28 08:53:51 +0000 | [diff] [blame^] | 370 | Factory::SetRegExpData(re, JSRegExp::JSCRE, pattern, flags, value); |
ager@chromium.org | 7c537e2 | 2008-10-16 08:43:32 +0000 | [diff] [blame] | 371 | |
| 372 | return re; |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 373 | } |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 374 | } |
| 375 | |
| 376 | |
ager@chromium.org | 236ad96 | 2008-09-25 09:45:57 +0000 | [diff] [blame] | 377 | Handle<Object> RegExpImpl::JsreExecOnce(Handle<JSRegExp> regexp, |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 378 | int num_captures, |
| 379 | Handle<String> subject, |
| 380 | int previous_index, |
| 381 | const uc16* two_byte_subject, |
| 382 | int* offsets_vector, |
| 383 | int offsets_vector_length) { |
| 384 | int rc; |
| 385 | { |
| 386 | AssertNoAllocation a; |
| 387 | ByteArray* internal = JsreInternal(regexp); |
ager@chromium.org | 236ad96 | 2008-09-25 09:45:57 +0000 | [diff] [blame] | 388 | const JscreRegExp* js_regexp = |
| 389 | reinterpret_cast<JscreRegExp*>(internal->GetDataStartAddress()); |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 390 | |
kasperl@chromium.org | b912362 | 2008-09-17 14:05:56 +0000 | [diff] [blame] | 391 | LOG(RegExpExecEvent(regexp, previous_index, subject)); |
| 392 | |
ager@chromium.org | 236ad96 | 2008-09-25 09:45:57 +0000 | [diff] [blame] | 393 | rc = jsRegExpExecute(js_regexp, |
| 394 | two_byte_subject, |
| 395 | subject->length(), |
| 396 | previous_index, |
| 397 | offsets_vector, |
| 398 | offsets_vector_length); |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 399 | } |
| 400 | |
| 401 | // The KJS JavaScript engine returns null (ie, a failed match) when |
| 402 | // JSRE's internal match limit is exceeded. We duplicate that behavior here. |
| 403 | if (rc == JSRegExpErrorNoMatch |
| 404 | || rc == JSRegExpErrorHitLimit) { |
| 405 | return Factory::null_value(); |
| 406 | } |
| 407 | |
| 408 | // Other JSRE errors: |
| 409 | if (rc < 0) { |
| 410 | // Throw an exception. |
| 411 | Handle<Object> code(Smi::FromInt(rc)); |
| 412 | Handle<Object> args[2] = { Factory::LookupAsciiSymbol("jsre_exec"), code }; |
| 413 | Handle<Object> regexp_err( |
| 414 | Factory::NewTypeError("jsre_error", HandleVector(args, 2))); |
| 415 | return Handle<Object>(Top::Throw(*regexp_err)); |
| 416 | } |
| 417 | |
ager@chromium.org | 7c537e2 | 2008-10-16 08:43:32 +0000 | [diff] [blame] | 418 | Handle<FixedArray> array = Factory::NewFixedArray(2 * (num_captures+1)); |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 419 | // The captures come in (start, end+1) pairs. |
| 420 | for (int i = 0; i < 2 * (num_captures+1); i += 2) { |
kasperl@chromium.org | 5a8ca6c | 2008-10-23 13:57:19 +0000 | [diff] [blame] | 421 | array->set(i, |
| 422 | Smi::FromInt(offsets_vector[i]), |
| 423 | SKIP_WRITE_BARRIER); |
| 424 | array->set(i+1, |
| 425 | Smi::FromInt(offsets_vector[i+1]), |
| 426 | SKIP_WRITE_BARRIER); |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 427 | } |
ager@chromium.org | 7c537e2 | 2008-10-16 08:43:32 +0000 | [diff] [blame] | 428 | return Factory::NewJSArrayWithElements(array); |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 429 | } |
| 430 | |
| 431 | |
| 432 | class OffsetsVector { |
| 433 | public: |
| 434 | inline OffsetsVector(int num_captures) { |
| 435 | offsets_vector_length_ = (num_captures + 1) * 3; |
| 436 | if (offsets_vector_length_ > kStaticOffsetsVectorSize) { |
| 437 | vector_ = NewArray<int>(offsets_vector_length_); |
| 438 | } else { |
| 439 | vector_ = static_offsets_vector_; |
| 440 | } |
| 441 | } |
| 442 | |
| 443 | |
| 444 | inline ~OffsetsVector() { |
| 445 | if (offsets_vector_length_ > kStaticOffsetsVectorSize) { |
| 446 | DeleteArray(vector_); |
| 447 | vector_ = NULL; |
| 448 | } |
| 449 | } |
| 450 | |
| 451 | |
| 452 | inline int* vector() { |
| 453 | return vector_; |
| 454 | } |
| 455 | |
| 456 | |
| 457 | inline int length() { |
| 458 | return offsets_vector_length_; |
| 459 | } |
| 460 | |
| 461 | private: |
| 462 | int* vector_; |
| 463 | int offsets_vector_length_; |
| 464 | static const int kStaticOffsetsVectorSize = 30; |
| 465 | static int static_offsets_vector_[kStaticOffsetsVectorSize]; |
| 466 | }; |
| 467 | |
| 468 | |
| 469 | int OffsetsVector::static_offsets_vector_[ |
| 470 | OffsetsVector::kStaticOffsetsVectorSize]; |
| 471 | |
| 472 | |
ager@chromium.org | 236ad96 | 2008-09-25 09:45:57 +0000 | [diff] [blame] | 473 | Handle<Object> RegExpImpl::JsreExec(Handle<JSRegExp> regexp, |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 474 | Handle<String> subject, |
| 475 | Handle<Object> index) { |
| 476 | // Prepare space for the return values. |
| 477 | int num_captures = JsreCapture(regexp); |
| 478 | |
| 479 | OffsetsVector offsets(num_captures); |
| 480 | |
| 481 | int previous_index = static_cast<int>(DoubleToInteger(index->Number())); |
| 482 | |
| 483 | Handle<String> subject16 = CachedStringToTwoByte(subject); |
| 484 | |
| 485 | Handle<Object> result(JsreExecOnce(regexp, num_captures, subject, |
| 486 | previous_index, |
| 487 | subject16->GetTwoByteData(), |
| 488 | offsets.vector(), offsets.length())); |
| 489 | |
| 490 | return result; |
| 491 | } |
| 492 | |
| 493 | |
ager@chromium.org | 236ad96 | 2008-09-25 09:45:57 +0000 | [diff] [blame] | 494 | Handle<Object> RegExpImpl::JsreExecGlobal(Handle<JSRegExp> regexp, |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 495 | Handle<String> subject) { |
| 496 | // Prepare space for the return values. |
| 497 | int num_captures = JsreCapture(regexp); |
| 498 | |
| 499 | OffsetsVector offsets(num_captures); |
| 500 | |
| 501 | int previous_index = 0; |
| 502 | |
ager@chromium.org | 7c537e2 | 2008-10-16 08:43:32 +0000 | [diff] [blame] | 503 | Handle<JSArray> result = Factory::NewJSArray(0); |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 504 | int i = 0; |
| 505 | Handle<Object> matches; |
| 506 | |
| 507 | Handle<String> subject16 = CachedStringToTwoByte(subject); |
| 508 | |
| 509 | do { |
| 510 | if (previous_index > subject->length() || previous_index < 0) { |
| 511 | // Per ECMA-262 15.10.6.2, if the previous index is greater than the |
| 512 | // string length, there is no match. |
| 513 | matches = Factory::null_value(); |
| 514 | } else { |
| 515 | matches = JsreExecOnce(regexp, num_captures, subject, previous_index, |
| 516 | subject16->GetTwoByteData(), |
| 517 | offsets.vector(), offsets.length()); |
| 518 | |
| 519 | if (matches->IsJSArray()) { |
| 520 | SetElement(result, i, matches); |
| 521 | i++; |
| 522 | previous_index = offsets.vector()[1]; |
| 523 | if (offsets.vector()[0] == offsets.vector()[1]) { |
| 524 | previous_index++; |
| 525 | } |
| 526 | } |
| 527 | } |
| 528 | } while (matches->IsJSArray()); |
| 529 | |
| 530 | // If we exited the loop with an exception, throw it. |
| 531 | if (matches->IsNull()) { // Exited loop normally. |
| 532 | return result; |
| 533 | } else { // Exited loop with the exception in matches. |
| 534 | return matches; |
| 535 | } |
| 536 | } |
| 537 | |
| 538 | |
ager@chromium.org | 236ad96 | 2008-09-25 09:45:57 +0000 | [diff] [blame] | 539 | int RegExpImpl::JsreCapture(Handle<JSRegExp> re) { |
kasperl@chromium.org | 9fe21c6 | 2008-10-28 08:53:51 +0000 | [diff] [blame^] | 540 | FixedArray* value = FixedArray::cast(re->DataAt(JSRegExp::kJscreDataIndex)); |
| 541 | return Smi::cast(value->get(CAPTURE_INDEX))->value(); |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 542 | } |
| 543 | |
| 544 | |
ager@chromium.org | 236ad96 | 2008-09-25 09:45:57 +0000 | [diff] [blame] | 545 | ByteArray* RegExpImpl::JsreInternal(Handle<JSRegExp> re) { |
kasperl@chromium.org | 9fe21c6 | 2008-10-28 08:53:51 +0000 | [diff] [blame^] | 546 | FixedArray* value = FixedArray::cast(re->DataAt(JSRegExp::kJscreDataIndex)); |
| 547 | return ByteArray::cast(value->get(INTERNAL_INDEX)); |
christian.plesner.hansen | 43d26ec | 2008-07-03 15:10:15 +0000 | [diff] [blame] | 548 | } |
| 549 | |
| 550 | }} // namespace v8::internal |