OLD | NEW |
1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/builtins/builtins.h" | 5 #include "src/builtins/builtins.h" |
6 #include "src/builtins/builtins-utils.h" | 6 #include "src/builtins/builtins-utils.h" |
7 | 7 |
| 8 #include "src/regexp/jsregexp.h" |
8 #include "src/string-builder.h" | 9 #include "src/string-builder.h" |
9 | 10 |
10 namespace v8 { | 11 namespace v8 { |
11 namespace internal { | 12 namespace internal { |
12 | 13 |
13 // ----------------------------------------------------------------------------- | 14 // ----------------------------------------------------------------------------- |
14 // ES6 section 21.2 RegExp Objects | 15 // ES6 section 21.2 RegExp Objects |
15 | 16 |
16 namespace { | 17 namespace { |
17 | 18 |
(...skipping 296 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
314 | 315 |
315 // Constants for accessing RegExpLastMatchInfo. | 316 // Constants for accessing RegExpLastMatchInfo. |
316 // TODO(jgruber): Currently, RegExpLastMatchInfo is still a JSObject maintained | 317 // TODO(jgruber): Currently, RegExpLastMatchInfo is still a JSObject maintained |
317 // and accessed from JS. This is a crutch until all RegExp logic is ported, then | 318 // and accessed from JS. This is a crutch until all RegExp logic is ported, then |
318 // we can take care of RegExpLastMatchInfo. | 319 // we can take care of RegExpLastMatchInfo. |
319 const int kNumberOfCapturesIndex = 0; | 320 const int kNumberOfCapturesIndex = 0; |
320 const int kLastSubjectIndex = 1; | 321 const int kLastSubjectIndex = 1; |
321 const int kLastInputIndex = 2; | 322 const int kLastInputIndex = 2; |
322 const int kFirstCaptureIndex = 3; | 323 const int kFirstCaptureIndex = 3; |
323 | 324 |
324 Handle<Object> GetLastMatchField(Isolate* isolate, int index) { | 325 Handle<JSObject> GetLastMatchInfo(Isolate* isolate) { |
325 Handle<JSFunction> global_regexp = isolate->regexp_function(); | 326 Handle<JSFunction> global_regexp = isolate->regexp_function(); |
326 Handle<Object> last_match_info_obj = JSReceiver::GetDataProperty( | 327 Handle<Object> last_match_info_obj = JSReceiver::GetDataProperty( |
327 global_regexp, isolate->factory()->regexp_last_match_info_symbol()); | 328 global_regexp, isolate->factory()->regexp_last_match_info_symbol()); |
328 | 329 |
329 Handle<JSReceiver> last_match_info = | 330 return Handle<JSObject>::cast(last_match_info_obj); |
330 Handle<JSReceiver>::cast(last_match_info_obj); | 331 } |
| 332 |
| 333 Handle<Object> GetLastMatchField(Isolate* isolate, int index) { |
| 334 Handle<JSObject> last_match_info = GetLastMatchInfo(isolate); |
331 return JSReceiver::GetElement(isolate, last_match_info, index) | 335 return JSReceiver::GetElement(isolate, last_match_info, index) |
332 .ToHandleChecked(); | 336 .ToHandleChecked(); |
333 } | 337 } |
334 | 338 |
335 void SetLastMatchField(Isolate* isolate, int index, Handle<Object> value) { | 339 void SetLastMatchField(Isolate* isolate, int index, Handle<Object> value) { |
336 Handle<JSFunction> global_regexp = isolate->regexp_function(); | 340 Handle<JSFunction> global_regexp = isolate->regexp_function(); |
337 Handle<Object> last_match_info_obj = JSReceiver::GetDataProperty( | 341 Handle<Object> last_match_info_obj = JSReceiver::GetDataProperty( |
338 global_regexp, isolate->factory()->regexp_last_match_info_symbol()); | 342 global_regexp, isolate->factory()->regexp_last_match_info_symbol()); |
339 | 343 |
340 Handle<JSReceiver> last_match_info = | 344 Handle<JSReceiver> last_match_info = |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
452 } | 456 } |
453 | 457 |
454 BUILTIN(RegExpPrototypeRightContextGetter) { | 458 BUILTIN(RegExpPrototypeRightContextGetter) { |
455 HandleScope scope(isolate); | 459 HandleScope scope(isolate); |
456 const int start_index = GetLastMatchCapture(isolate, 1); | 460 const int start_index = GetLastMatchCapture(isolate, 1); |
457 Handle<String> last_subject = GetLastMatchSubject(isolate); | 461 Handle<String> last_subject = GetLastMatchSubject(isolate); |
458 const int len = last_subject->length(); | 462 const int len = last_subject->length(); |
459 return *isolate->factory()->NewSubString(last_subject, start_index, len); | 463 return *isolate->factory()->NewSubString(last_subject, start_index, len); |
460 } | 464 } |
461 | 465 |
| 466 namespace { |
| 467 |
| 468 MaybeHandle<Object> SetLastIndex(Isolate* isolate, Handle<JSRegExp> regexp, |
| 469 int value) { |
| 470 return Object::SetProperty(regexp, isolate->factory()->lastIndex_string(), |
| 471 handle(Smi::FromInt(value), isolate), SLOPPY); |
| 472 } |
| 473 |
| 474 Handle<JSArray> ConstructResult(Isolate* isolate, int size, int index, |
| 475 Handle<String> input) { |
| 476 Handle<FixedArray> elements = isolate->factory()->NewFixedArray(size); |
| 477 Handle<Map> regexp_map(isolate->native_context()->regexp_result_map()); |
| 478 Handle<JSObject> object = |
| 479 isolate->factory()->NewJSObjectFromMap(regexp_map, NOT_TENURED); |
| 480 Handle<JSArray> array = Handle<JSArray>::cast(object); |
| 481 array->set_elements(*elements); |
| 482 array->set_length(Smi::FromInt(size)); |
| 483 // Write in-object properties after the length of the array. |
| 484 array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, |
| 485 Smi::FromInt(index)); |
| 486 array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, *input); |
| 487 return array; |
| 488 } |
| 489 |
| 490 Handle<Object> ReturnNewResultFromMatchInfo(Isolate* isolate, |
| 491 Handle<Object> match_info, |
| 492 Handle<String> string) { |
| 493 const int num_captures = GetLastMatchNumberOfCaptures(isolate); |
| 494 DCHECK_EQ(0, num_captures % 2); |
| 495 |
| 496 const int num_results = num_captures / 2; |
| 497 int start = GetLastMatchCapture(isolate, 0); |
| 498 int end = GetLastMatchCapture(isolate, 1); |
| 499 |
| 500 // Calculate the substring of the first match before creating the result array |
| 501 // to avoid an unnecessary write barrier storing the first result. |
| 502 Handle<String> first = isolate->factory()->NewSubString(string, start, end); |
| 503 Handle<JSArray> result = ConstructResult(isolate, num_results, start, string); |
| 504 |
| 505 Handle<FixedArray> elems = |
| 506 handle(FixedArray::cast(result->elements()), isolate); |
| 507 elems->set(0, *first); |
| 508 |
| 509 for (int i = 1; i < num_results; i++) { |
| 510 start = GetLastMatchCapture(isolate, i * 2); |
| 511 if (start != -1) { |
| 512 end = GetLastMatchCapture(isolate, i * 2 + 1); |
| 513 Handle<String> capture = |
| 514 isolate->factory()->NewSubString(string, start, end); |
| 515 elems->set(i, *capture); |
| 516 } |
| 517 } |
| 518 |
| 519 return result; |
| 520 } |
| 521 |
| 522 MaybeHandle<Object> RegExpExecJS(Isolate* isolate, Handle<JSRegExp> regexp, |
| 523 Handle<String> string) { |
| 524 Handle<Object> last_index_obj; |
| 525 ASSIGN_RETURN_ON_EXCEPTION( |
| 526 isolate, last_index_obj, |
| 527 Object::GetProperty(regexp, isolate->factory()->lastIndex_string()), |
| 528 Object); |
| 529 |
| 530 // Conversion is required by the ES2015 specification (RegExpBuiltinExec |
| 531 // algorithm, step 4) even if the value is discarded for non-global RegExps. |
| 532 ASSIGN_RETURN_ON_EXCEPTION(isolate, last_index_obj, |
| 533 Object::ToLength(isolate, last_index_obj), Object); |
| 534 |
| 535 int last_index = Handle<Smi>::cast(last_index_obj)->value(); |
| 536 |
| 537 const int flags = regexp->GetFlags(); |
| 538 const bool global = (flags & JSRegExp::kGlobal) != 0; |
| 539 const bool sticky = (flags & JSRegExp::kSticky) != 0; |
| 540 const bool update_last_index = (global || sticky); |
| 541 |
| 542 if (update_last_index) { |
| 543 if (last_index > string->length()) { |
| 544 RETURN_ON_EXCEPTION(isolate, SetLastIndex(isolate, regexp, 0), Object); |
| 545 return isolate->factory()->null_value(); |
| 546 } |
| 547 } else { |
| 548 last_index = 0; |
| 549 } |
| 550 |
| 551 Handle<JSObject> last_match_info = GetLastMatchInfo(isolate); |
| 552 |
| 553 // matchIndices is either null or the RegExpLastMatchInfo array. |
| 554 // TODO(littledan): Whether a RegExp is sticky is compiled into the RegExp |
| 555 // itself, but ES2015 allows monkey-patching this property to differ from |
| 556 // the internal flags. If it differs, recompile a different RegExp? |
| 557 // TODO(jgruber): The result of Exec does not need to be a JSArray. |
| 558 Handle<Object> match_indices; |
| 559 ASSIGN_RETURN_ON_EXCEPTION( |
| 560 isolate, match_indices, |
| 561 RegExpImpl::Exec(regexp, string, last_index, last_match_info), Object); |
| 562 |
| 563 if (match_indices->IsNull(isolate)) { |
| 564 RETURN_ON_EXCEPTION(isolate, SetLastIndex(isolate, regexp, 0), Object); |
| 565 return isolate->factory()->null_value(); |
| 566 } |
| 567 |
| 568 // Successful match. |
| 569 if (update_last_index) { |
| 570 last_index = GetLastMatchCapture(isolate, 1); |
| 571 RETURN_ON_EXCEPTION(isolate, SetLastIndex(isolate, regexp, last_index), |
| 572 Object); |
| 573 } |
| 574 |
| 575 return ReturnNewResultFromMatchInfo(isolate, match_indices, string); |
| 576 } |
| 577 |
| 578 } // namespace |
| 579 |
| 580 // ES#sec-regexp.prototype.exec |
| 581 // RegExp.prototype.exec ( string ) |
| 582 BUILTIN(RegExpPrototypeExec) { |
| 583 HandleScope scope(isolate); |
| 584 CHECK_RECEIVER(JSRegExp, regexp, "RegExp.prototype.exec"); |
| 585 |
| 586 Handle<Object> string_obj = args.atOrUndefined(isolate, 1); |
| 587 |
| 588 Handle<String> string; |
| 589 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, string, |
| 590 Object::ToString(isolate, string_obj)); |
| 591 |
| 592 RETURN_RESULT_OR_FAILURE(isolate, RegExpExecJS(isolate, regexp, string)); |
| 593 } |
| 594 |
462 } // namespace internal | 595 } // namespace internal |
463 } // namespace v8 | 596 } // namespace v8 |
OLD | NEW |