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 |