| Index: src/builtins/builtins-regexp.cc
|
| diff --git a/src/builtins/builtins-regexp.cc b/src/builtins/builtins-regexp.cc
|
| index f1e458860c7e96487ad9e79cf19ff0785d820e48..2dbfb0c3e6d805b574ce405ece694409007491fb 100644
|
| --- a/src/builtins/builtins-regexp.cc
|
| +++ b/src/builtins/builtins-regexp.cc
|
| @@ -148,5 +148,249 @@ BUILTIN(RegExpConstructor) {
|
| RegExpInitialize(isolate, regexp, pattern, flags));
|
| }
|
|
|
| +#define APPEND_CHAR_FOR_FLAG(flag, c) \
|
| + do { \
|
| + Handle<Object> property; \
|
| + Handle<Name> name = isolate->factory()->flag##_string(); \
|
| + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, property, \
|
| + JSReceiver::GetProperty(recv, name)); \
|
| + if (property->BooleanValue()) { \
|
| + builder.AppendCharacter(c); \
|
| + } \
|
| + } while (false);
|
| +
|
| +// ES6 21.2.5.3.
|
| +BUILTIN(RegExpPrototypeFlagsGetter) {
|
| + HandleScope scope(isolate);
|
| + CHECK_RECEIVER(JSReceiver, recv, "get RegExp.prototype.flags");
|
| +
|
| + IncrementalStringBuilder builder(isolate);
|
| +
|
| + APPEND_CHAR_FOR_FLAG(global, 'g');
|
| + APPEND_CHAR_FOR_FLAG(ignoreCase, 'i');
|
| + APPEND_CHAR_FOR_FLAG(multiline, 'm');
|
| + APPEND_CHAR_FOR_FLAG(unicode, 'u');
|
| + APPEND_CHAR_FOR_FLAG(sticky, 'y');
|
| +
|
| + RETURN_RESULT_OR_FAILURE(isolate, builder.Finish());
|
| +}
|
| +
|
| +#undef APPEND_CHAR_FOR_FLAG
|
| +
|
| +// ES6 21.2.5.10.
|
| +BUILTIN(RegExpPrototypeSourceGetter) {
|
| + HandleScope scope(isolate);
|
| +
|
| + Handle<Object> recv = args.receiver();
|
| + if (!recv->IsJSRegExp()) {
|
| + // TODO(littledan): Remove this RegExp compat workaround
|
| + Handle<JSFunction> regexp_fun = isolate->regexp_function();
|
| + if (*recv == regexp_fun->prototype()) {
|
| + isolate->CountUsage(v8::Isolate::kRegExpPrototypeSourceGetter);
|
| + return *isolate->factory()->NewStringFromAsciiChecked("(?:)");
|
| + }
|
| + THROW_NEW_ERROR_RETURN_FAILURE(
|
| + isolate, NewTypeError(MessageTemplate::kRegExpNonRegExp,
|
| + isolate->factory()->NewStringFromAsciiChecked(
|
| + "RegExp.prototype.source")));
|
| + }
|
| +
|
| + Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(recv);
|
| + return regexp->source();
|
| +}
|
| +
|
| +// ES6 21.2.4.2.
|
| +BUILTIN(RegExpPrototypeSpeciesGetter) {
|
| + HandleScope scope(isolate);
|
| + return *args.receiver();
|
| +}
|
| +
|
| +#define REGEXP_FLAG_GETTER(name, counter, getter) \
|
| + BUILTIN(RegExpPrototype##name##Getter) { \
|
| + HandleScope scope(isolate); \
|
| + Handle<Object> recv = args.receiver(); \
|
| + if (!recv->IsJSRegExp()) { \
|
| + /* TODO(littledan): Remove this RegExp compat workaround */ \
|
| + Handle<JSFunction> regexp_fun = isolate->regexp_function(); \
|
| + if (*recv == regexp_fun->prototype()) { \
|
| + isolate->CountUsage(v8::Isolate::kRegExpPrototype##counter##Getter); \
|
| + return isolate->heap()->undefined_value(); \
|
| + } \
|
| + THROW_NEW_ERROR_RETURN_FAILURE( \
|
| + isolate, NewTypeError(MessageTemplate::kRegExpNonRegExp, \
|
| + isolate->factory()->NewStringFromAsciiChecked( \
|
| + getter))); \
|
| + } \
|
| + Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(recv); \
|
| + const bool ret = (regexp->GetFlags() & JSRegExp::k##name) != 0; \
|
| + return *isolate->factory()->ToBoolean(ret); \
|
| + }
|
| +
|
| +// ES6 21.2.5.4.
|
| +REGEXP_FLAG_GETTER(Global, OldFlag, "RegExp.prototype.global")
|
| +
|
| +// ES6 21.2.5.5.
|
| +REGEXP_FLAG_GETTER(IgnoreCase, OldFlag, "RegExp.prototype.ignoreCase")
|
| +
|
| +// ES6 21.2.5.7.
|
| +REGEXP_FLAG_GETTER(Multiline, OldFlag, "RegExp.prototype.multiline")
|
| +
|
| +// ES6 21.2.5.12.
|
| +REGEXP_FLAG_GETTER(Sticky, Sticky, "RegExp.prototype.sticky")
|
| +
|
| +// ES6 21.2.5.15.
|
| +REGEXP_FLAG_GETTER(Unicode, Unicode, "RegExp.prototype.unicode")
|
| +
|
| +#undef REGEXP_FLAG_GETTER
|
| +
|
| +namespace {
|
| +
|
| +// Constants for accessing RegExpLastMatchInfo.
|
| +// TODO(jgruber): Currently, RegExpLastMatchInfo is still a JSObject maintained
|
| +// and accessed from JS. This is a crutch until all RegExp logic is ported, then
|
| +// we can take care of RegExpLastMatchInfo.
|
| +const int kNumberOfCapturesIndex = 0;
|
| +const int kLastSubjectIndex = 1;
|
| +const int kLastInputIndex = 2;
|
| +const int kFirstCaptureIndex = 3;
|
| +
|
| +Handle<Object> GetLastMatchField(Isolate* isolate, int index) {
|
| + Handle<JSFunction> global_regexp = isolate->regexp_function();
|
| + Handle<Object> last_match_info_obj = JSReceiver::GetDataProperty(
|
| + global_regexp, isolate->factory()->regexp_last_match_info_symbol());
|
| +
|
| + Handle<JSReceiver> last_match_info =
|
| + Handle<JSReceiver>::cast(last_match_info_obj);
|
| + return JSReceiver::GetElement(isolate, last_match_info, index)
|
| + .ToHandleChecked();
|
| +}
|
| +
|
| +void SetLastMatchField(Isolate* isolate, int index, Handle<Object> value) {
|
| + Handle<JSFunction> global_regexp = isolate->regexp_function();
|
| + Handle<Object> last_match_info_obj = JSReceiver::GetDataProperty(
|
| + global_regexp, isolate->factory()->regexp_last_match_info_symbol());
|
| +
|
| + Handle<JSReceiver> last_match_info =
|
| + Handle<JSReceiver>::cast(last_match_info_obj);
|
| + JSReceiver::SetElement(isolate, last_match_info, index, value, SLOPPY)
|
| + .ToHandleChecked();
|
| +}
|
| +
|
| +int GetLastMatchNumberOfCaptures(Isolate* isolate) {
|
| + Handle<Object> obj = GetLastMatchField(isolate, kNumberOfCapturesIndex);
|
| + return Handle<Smi>::cast(obj)->value();
|
| +}
|
| +
|
| +Handle<String> GetLastMatchSubject(Isolate* isolate) {
|
| + return Handle<String>::cast(GetLastMatchField(isolate, kLastSubjectIndex));
|
| +}
|
| +
|
| +Handle<Object> GetLastMatchInput(Isolate* isolate) {
|
| + return GetLastMatchField(isolate, kLastInputIndex);
|
| +}
|
| +
|
| +int GetLastMatchCapture(Isolate* isolate, int i) {
|
| + Handle<Object> obj = GetLastMatchField(isolate, kFirstCaptureIndex + i);
|
| + return Handle<Smi>::cast(obj)->value();
|
| +}
|
| +
|
| +Object* GenericCaptureGetter(Isolate* isolate, int capture) {
|
| + HandleScope scope(isolate);
|
| + const int index = capture * 2;
|
| + if (index >= GetLastMatchNumberOfCaptures(isolate)) {
|
| + return isolate->heap()->empty_string();
|
| + }
|
| +
|
| + const int match_start = GetLastMatchCapture(isolate, index);
|
| + const int match_end = GetLastMatchCapture(isolate, index + 1);
|
| + if (match_start == -1 || match_end == -1) {
|
| + return isolate->heap()->empty_string();
|
| + }
|
| +
|
| + Handle<String> last_subject = GetLastMatchSubject(isolate);
|
| + return *isolate->factory()->NewSubString(last_subject, match_start,
|
| + match_end);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// The properties $1..$9 are the first nine capturing substrings of the last
|
| +// successful match, or ''. The function RegExpMakeCaptureGetter will be
|
| +// called with indices from 1 to 9.
|
| +#define DEFINE_CAPTURE_GETTER(i) \
|
| + BUILTIN(RegExpPrototypeCapture##i##Getter) { \
|
| + HandleScope scope(isolate); \
|
| + return GenericCaptureGetter(isolate, i); \
|
| + }
|
| +DEFINE_CAPTURE_GETTER(1)
|
| +DEFINE_CAPTURE_GETTER(2)
|
| +DEFINE_CAPTURE_GETTER(3)
|
| +DEFINE_CAPTURE_GETTER(4)
|
| +DEFINE_CAPTURE_GETTER(5)
|
| +DEFINE_CAPTURE_GETTER(6)
|
| +DEFINE_CAPTURE_GETTER(7)
|
| +DEFINE_CAPTURE_GETTER(8)
|
| +DEFINE_CAPTURE_GETTER(9)
|
| +#undef DEFINE_CAPTURE_GETTER
|
| +
|
| +// The properties `input` and `$_` are aliases for each other. When this
|
| +// value is set the value it is set to is coerced to a string.
|
| +// Getter and setter for the input.
|
| +
|
| +BUILTIN(RegExpPrototypeInputGetter) {
|
| + HandleScope scope(isolate);
|
| + Handle<Object> obj = GetLastMatchInput(isolate);
|
| + return obj->IsUndefined(isolate) ? isolate->heap()->empty_string()
|
| + : String::cast(*obj);
|
| +}
|
| +
|
| +BUILTIN(RegExpPrototypeInputSetter) {
|
| + HandleScope scope(isolate);
|
| + Handle<Object> value = args.atOrUndefined(isolate, 1);
|
| + Handle<String> str;
|
| + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, str,
|
| + Object::ToString(isolate, value));
|
| + SetLastMatchField(isolate, kLastInputIndex, str);
|
| + return isolate->heap()->undefined_value();
|
| +}
|
| +
|
| +// Getters for the static properties lastMatch, lastParen, leftContext, and
|
| +// rightContext of the RegExp constructor. The properties are computed based
|
| +// on the captures array of the last successful match and the subject string
|
| +// of the last successful match.
|
| +BUILTIN(RegExpPrototypeLastMatchGetter) {
|
| + HandleScope scope(isolate);
|
| + return GenericCaptureGetter(isolate, 0);
|
| +}
|
| +
|
| +BUILTIN(RegExpPrototypeLastParenGetter) {
|
| + HandleScope scope(isolate);
|
| + const int length = GetLastMatchNumberOfCaptures(isolate);
|
| + if (length <= 2) return isolate->heap()->empty_string(); // No captures.
|
| +
|
| + DCHECK_EQ(0, length % 2);
|
| + const int last_capture = (length / 2) - 1;
|
| +
|
| + // We match the SpiderMonkey behavior: return the substring defined by the
|
| + // last pair (after the first pair) of elements of the capture array even if
|
| + // it is empty.
|
| + return GenericCaptureGetter(isolate, last_capture);
|
| +}
|
| +
|
| +BUILTIN(RegExpPrototypeLeftContextGetter) {
|
| + HandleScope scope(isolate);
|
| + const int start_index = GetLastMatchCapture(isolate, 0);
|
| + Handle<String> last_subject = GetLastMatchSubject(isolate);
|
| + return *isolate->factory()->NewSubString(last_subject, 0, start_index);
|
| +}
|
| +
|
| +BUILTIN(RegExpPrototypeRightContextGetter) {
|
| + HandleScope scope(isolate);
|
| + const int start_index = GetLastMatchCapture(isolate, 1);
|
| + Handle<String> last_subject = GetLastMatchSubject(isolate);
|
| + const int len = last_subject->length();
|
| + return *isolate->factory()->NewSubString(last_subject, start_index, len);
|
| +}
|
| +
|
| } // namespace internal
|
| } // namespace v8
|
|
|