| 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" | |
| 9 #include "src/string-builder.h" | 8 #include "src/string-builder.h" |
| 10 | 9 |
| 11 namespace v8 { | 10 namespace v8 { |
| 12 namespace internal { | 11 namespace internal { |
| 13 | 12 |
| 14 // ----------------------------------------------------------------------------- | 13 // ----------------------------------------------------------------------------- |
| 15 // ES6 section 21.2 RegExp Objects | 14 // ES6 section 21.2 RegExp Objects |
| 16 | 15 |
| 17 namespace { | 16 namespace { |
| 18 | 17 |
| (...skipping 296 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 315 | 314 |
| 316 // Constants for accessing RegExpLastMatchInfo. | 315 // Constants for accessing RegExpLastMatchInfo. |
| 317 // TODO(jgruber): Currently, RegExpLastMatchInfo is still a JSObject maintained | 316 // TODO(jgruber): Currently, RegExpLastMatchInfo is still a JSObject maintained |
| 318 // and accessed from JS. This is a crutch until all RegExp logic is ported, then | 317 // and accessed from JS. This is a crutch until all RegExp logic is ported, then |
| 319 // we can take care of RegExpLastMatchInfo. | 318 // we can take care of RegExpLastMatchInfo. |
| 320 const int kNumberOfCapturesIndex = 0; | 319 const int kNumberOfCapturesIndex = 0; |
| 321 const int kLastSubjectIndex = 1; | 320 const int kLastSubjectIndex = 1; |
| 322 const int kLastInputIndex = 2; | 321 const int kLastInputIndex = 2; |
| 323 const int kFirstCaptureIndex = 3; | 322 const int kFirstCaptureIndex = 3; |
| 324 | 323 |
| 325 Handle<JSObject> GetLastMatchInfo(Isolate* isolate) { | 324 Handle<Object> GetLastMatchField(Isolate* isolate, int index) { |
| 326 Handle<JSFunction> global_regexp = isolate->regexp_function(); | 325 Handle<JSFunction> global_regexp = isolate->regexp_function(); |
| 327 Handle<Object> last_match_info_obj = JSReceiver::GetDataProperty( | 326 Handle<Object> last_match_info_obj = JSReceiver::GetDataProperty( |
| 328 global_regexp, isolate->factory()->regexp_last_match_info_symbol()); | 327 global_regexp, isolate->factory()->regexp_last_match_info_symbol()); |
| 329 | 328 |
| 330 return Handle<JSObject>::cast(last_match_info_obj); | 329 Handle<JSReceiver> last_match_info = |
| 331 } | 330 Handle<JSReceiver>::cast(last_match_info_obj); |
| 332 | |
| 333 Handle<Object> GetLastMatchField(Isolate* isolate, int index) { | |
| 334 Handle<JSObject> last_match_info = GetLastMatchInfo(isolate); | |
| 335 return JSReceiver::GetElement(isolate, last_match_info, index) | 331 return JSReceiver::GetElement(isolate, last_match_info, index) |
| 336 .ToHandleChecked(); | 332 .ToHandleChecked(); |
| 337 } | 333 } |
| 338 | 334 |
| 339 void SetLastMatchField(Isolate* isolate, int index, Handle<Object> value) { | 335 void SetLastMatchField(Isolate* isolate, int index, Handle<Object> value) { |
| 340 Handle<JSFunction> global_regexp = isolate->regexp_function(); | 336 Handle<JSFunction> global_regexp = isolate->regexp_function(); |
| 341 Handle<Object> last_match_info_obj = JSReceiver::GetDataProperty( | 337 Handle<Object> last_match_info_obj = JSReceiver::GetDataProperty( |
| 342 global_regexp, isolate->factory()->regexp_last_match_info_symbol()); | 338 global_regexp, isolate->factory()->regexp_last_match_info_symbol()); |
| 343 | 339 |
| 344 Handle<JSReceiver> last_match_info = | 340 Handle<JSReceiver> last_match_info = |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 456 } | 452 } |
| 457 | 453 |
| 458 BUILTIN(RegExpPrototypeRightContextGetter) { | 454 BUILTIN(RegExpPrototypeRightContextGetter) { |
| 459 HandleScope scope(isolate); | 455 HandleScope scope(isolate); |
| 460 const int start_index = GetLastMatchCapture(isolate, 1); | 456 const int start_index = GetLastMatchCapture(isolate, 1); |
| 461 Handle<String> last_subject = GetLastMatchSubject(isolate); | 457 Handle<String> last_subject = GetLastMatchSubject(isolate); |
| 462 const int len = last_subject->length(); | 458 const int len = last_subject->length(); |
| 463 return *isolate->factory()->NewSubString(last_subject, start_index, len); | 459 return *isolate->factory()->NewSubString(last_subject, start_index, len); |
| 464 } | 460 } |
| 465 | 461 |
| 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 | |
| 595 } // namespace internal | 462 } // namespace internal |
| 596 } // namespace v8 | 463 } // namespace v8 |
| OLD | NEW |