Revert of [regexp] Port RegExp getters and setters (patchset #5 id:80001 of https://codereview.chromium.org/2305573002/ )

Reason for revert:
Performance regressions: crbug.com/644087
Clusterfuzz: crbug.com/644074

We'll reland all regexp changes at once when the port is complete and at least performance-neutral, since the partial port requires slow workarounds.

Original issue's description:
> [regexp] Port RegExp getters and setters
>
> BUG=v8:5339
>
> Committed: https://crrev.com/ac0eb5e05af40e16ae9402bb8a62600b32cc2ec9
> Committed: https://crrev.com/7711b1a16f864ed6ea56fa40274ff3f6287bbe34
> Cr-Original-Commit-Position: refs/heads/master@{#39076}
> Cr-Commit-Position: refs/heads/master@{#39088}

TBR=bmeurer@chromium.org
# Not skipping CQ checks because original CL landed more than 1 days ago.
BUG=v8:5339

Review-Url: https://codereview.chromium.org/2313713002
Cr-Commit-Position: refs/heads/master@{#39176}
This commit is contained in:
jgruber 2016-09-05 06:48:23 -07:00 committed by Commit bot
parent f30075bb70
commit ee7f14cb54
9 changed files with 259 additions and 428 deletions

View File

@ -365,6 +365,17 @@ void InstallFunction(Handle<JSObject> target, Handle<JSFunction> function,
InstallFunction(target, name, function, name_string, attributes);
}
Handle<JSFunction> InstallGetter(Handle<JSObject> target,
Handle<Name> property_name,
Handle<JSFunction> getter,
PropertyAttributes attributes = DONT_ENUM) {
Handle<Object> setter = target->GetIsolate()->factory()->undefined_value();
JSObject::DefineAccessor(target, property_name, getter, setter, attributes)
.Check();
getter->shared()->set_native(true);
return getter;
}
Handle<JSFunction> CreateFunction(Isolate* isolate, Handle<String> name,
InstanceType type, int instance_size,
MaybeHandle<JSObject> maybe_prototype,
@ -450,54 +461,17 @@ Handle<JSFunction> SimpleInstallFunction(Handle<JSObject> base,
return fun;
}
void SimpleInstallGetterSetter(Handle<JSObject> base, Handle<String> name,
Builtins::Name call_getter,
Builtins::Name call_setter,
PropertyAttributes attribs) {
Isolate* const isolate = base->GetIsolate();
Handle<String> getter_name =
Name::ToFunctionName(name, isolate->factory()->get_string())
.ToHandleChecked();
Handle<JSFunction> getter =
SimpleCreateFunction(isolate, getter_name, call_getter, 0, false);
getter->shared()->set_native(true);
Handle<String> setter_name =
Name::ToFunctionName(name, isolate->factory()->set_string())
.ToHandleChecked();
Handle<JSFunction> setter =
SimpleCreateFunction(isolate, setter_name, call_setter, 0, false);
setter->shared()->set_native(true);
JSObject::DefineAccessor(base, name, getter, setter, attribs).Check();
}
Handle<JSFunction> SimpleInstallGetter(Handle<JSObject> base,
Handle<String> name,
Handle<Name> property_name,
Builtins::Name call, bool adapt) {
Isolate* const isolate = base->GetIsolate();
Handle<String> getter_name =
Name::ToFunctionName(name, isolate->factory()->get_string())
.ToHandleChecked();
Handle<JSFunction> getter =
SimpleCreateFunction(isolate, getter_name, call, 0, adapt);
getter->shared()->set_native(true);
Handle<Object> setter = isolate->factory()->undefined_value();
JSObject::DefineAccessor(base, property_name, getter, setter, DONT_ENUM)
.Check();
return getter;
}
Handle<JSFunction> SimpleInstallGetter(Handle<JSObject> base,
Handle<String> name, Builtins::Name call,
bool adapt) {
return SimpleInstallGetter(base, name, name, call, adapt);
Isolate* const isolate = base->GetIsolate();
Handle<String> fun_name =
Name::ToFunctionName(name, isolate->factory()->get_string())
.ToHandleChecked();
Handle<JSFunction> fun =
SimpleCreateFunction(isolate, fun_name, call, 0, adapt);
InstallGetter(base, name, fun);
return fun;
}
Handle<JSFunction> SimpleInstallGetter(Handle<JSObject> base,
@ -1613,109 +1587,11 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
shared->DontAdaptArguments();
shared->set_length(2);
{
// RegExp.prototype setup.
Handle<JSObject> proto =
factory->NewJSObject(isolate->object_function(), TENURED);
JSObject::AddProperty(proto, factory->constructor_string(), regexp_fun,
DONT_ENUM);
Accessors::FunctionSetPrototype(regexp_fun, proto).Assert();
SimpleInstallGetter(proto, factory->flags_string(),
Builtins::kRegExpPrototypeFlagsGetter, false);
SimpleInstallGetter(proto, factory->global_string(),
Builtins::kRegExpPrototypeGlobalGetter, false);
SimpleInstallGetter(proto, factory->ignoreCase_string(),
Builtins::kRegExpPrototypeIgnoreCaseGetter, false);
SimpleInstallGetter(proto, factory->multiline_string(),
Builtins::kRegExpPrototypeMultilineGetter, false);
SimpleInstallGetter(proto, factory->source_string(),
Builtins::kRegExpPrototypeSourceGetter, false);
SimpleInstallGetter(proto, factory->sticky_string(),
Builtins::kRegExpPrototypeStickyGetter, false);
SimpleInstallGetter(proto, factory->unicode_string(),
Builtins::kRegExpPrototypeUnicodeGetter, false);
}
{
// RegExp getters and setters.
// TODO(jgruber): This should really be DONT_ENUM | DONT_DELETE.
// However, that currently breaks layout test expectations. Note that
// Firefox sets a couple of these as enumerable.
const PropertyAttributes no_enum = DONT_ENUM;
SimpleInstallGetter(regexp_fun,
factory->InternalizeUtf8String("[Symbol.species]"),
factory->species_symbol(),
Builtins::kRegExpPrototypeSpeciesGetter, false);
// Static properties set by a successful match.
SimpleInstallGetterSetter(regexp_fun, factory->input_string(),
Builtins::kRegExpPrototypeInputGetter,
Builtins::kRegExpPrototypeInputSetter,
DONT_DELETE);
SimpleInstallGetterSetter(regexp_fun,
factory->InternalizeUtf8String("$_"),
Builtins::kRegExpPrototypeInputGetter,
Builtins::kRegExpPrototypeInputSetter, no_enum);
SimpleInstallGetterSetter(regexp_fun,
factory->InternalizeUtf8String("lastMatch"),
Builtins::kRegExpPrototypeLastMatchGetter,
Builtins::kEmptyFunction, no_enum);
SimpleInstallGetterSetter(regexp_fun,
factory->InternalizeUtf8String("$&"),
Builtins::kRegExpPrototypeLastMatchGetter,
Builtins::kEmptyFunction, no_enum);
SimpleInstallGetterSetter(regexp_fun,
factory->InternalizeUtf8String("lastParen"),
Builtins::kRegExpPrototypeLastParenGetter,
Builtins::kEmptyFunction, no_enum);
SimpleInstallGetterSetter(regexp_fun,
factory->InternalizeUtf8String("$+"),
Builtins::kRegExpPrototypeLastParenGetter,
Builtins::kEmptyFunction, no_enum);
SimpleInstallGetterSetter(regexp_fun,
factory->InternalizeUtf8String("leftContext"),
Builtins::kRegExpPrototypeLeftContextGetter,
Builtins::kEmptyFunction, no_enum);
SimpleInstallGetterSetter(regexp_fun,
factory->InternalizeUtf8String("$`"),
Builtins::kRegExpPrototypeLeftContextGetter,
Builtins::kEmptyFunction, no_enum);
SimpleInstallGetterSetter(regexp_fun,
factory->InternalizeUtf8String("rightContext"),
Builtins::kRegExpPrototypeRightContextGetter,
Builtins::kEmptyFunction, no_enum);
SimpleInstallGetterSetter(regexp_fun,
factory->InternalizeUtf8String("$'"),
Builtins::kRegExpPrototypeRightContextGetter,
Builtins::kEmptyFunction, no_enum);
#define INSTALL_CAPTURE_GETTER(i) \
SimpleInstallGetterSetter(regexp_fun, \
factory->InternalizeUtf8String("$" #i), \
Builtins::kRegExpPrototypeCapture##i##Getter, \
Builtins::kEmptyFunction, DONT_DELETE)
INSTALL_CAPTURE_GETTER(1);
INSTALL_CAPTURE_GETTER(2);
INSTALL_CAPTURE_GETTER(3);
INSTALL_CAPTURE_GETTER(4);
INSTALL_CAPTURE_GETTER(5);
INSTALL_CAPTURE_GETTER(6);
INSTALL_CAPTURE_GETTER(7);
INSTALL_CAPTURE_GETTER(8);
INSTALL_CAPTURE_GETTER(9);
#undef INSTALL_CAPTURE_GETTER
}
// TODO(jgruber): shared->set_force_inline on getters.
Handle<JSObject> proto =
factory->NewJSObject(isolate->object_function(), TENURED);
JSObject::AddProperty(proto, factory->constructor_string(), regexp_fun,
DONT_ENUM);
Accessors::FunctionSetPrototype(regexp_fun, proto).Assert();
DCHECK(regexp_fun->has_initial_map());
Handle<Map> initial_map(regexp_fun->initial_map());

View File

@ -148,249 +148,5 @@ 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

View File

@ -510,29 +510,6 @@ namespace internal {
\
/* RegExp */ \
CPP(RegExpConstructor) \
CPP(RegExpPrototypeCapture1Getter) \
CPP(RegExpPrototypeCapture2Getter) \
CPP(RegExpPrototypeCapture3Getter) \
CPP(RegExpPrototypeCapture4Getter) \
CPP(RegExpPrototypeCapture5Getter) \
CPP(RegExpPrototypeCapture6Getter) \
CPP(RegExpPrototypeCapture7Getter) \
CPP(RegExpPrototypeCapture8Getter) \
CPP(RegExpPrototypeCapture9Getter) \
CPP(RegExpPrototypeFlagsGetter) \
CPP(RegExpPrototypeGlobalGetter) \
CPP(RegExpPrototypeIgnoreCaseGetter) \
CPP(RegExpPrototypeInputGetter) \
CPP(RegExpPrototypeInputSetter) \
CPP(RegExpPrototypeLastMatchGetter) \
CPP(RegExpPrototypeLastParenGetter) \
CPP(RegExpPrototypeLeftContextGetter) \
CPP(RegExpPrototypeMultilineGetter) \
CPP(RegExpPrototypeRightContextGetter) \
CPP(RegExpPrototypeSourceGetter) \
CPP(RegExpPrototypeSpeciesGetter) \
CPP(RegExpPrototypeStickyGetter) \
CPP(RegExpPrototypeUnicodeGetter) \
\
/* SharedArrayBuffer */ \
CPP(SharedArrayBufferPrototypeGetByteLength) \

View File

@ -75,7 +75,6 @@
V(get_string, "get") \
V(global_string, "global") \
V(has_string, "has") \
V(ignoreCase_string, "ignoreCase") \
V(illegal_access_string, "illegal access") \
V(illegal_argument_string, "illegal argument") \
V(index_string, "index") \
@ -98,7 +97,6 @@
V(message_string, "message") \
V(minus_infinity_string, "-Infinity") \
V(minus_zero_string, "-0") \
V(multiline_string, "multiline") \
V(name_string, "name") \
V(nan_string, "NaN") \
V(next_string, "next") \
@ -132,7 +130,6 @@
V(source_url_string, "source_url") \
V(stack_string, "stack") \
V(stackTraceLimit_string, "stackTraceLimit") \
V(sticky_string, "sticky") \
V(strict_compare_ic_string, "===") \
V(string_string, "string") \
V(String_string, "String") \
@ -154,7 +151,6 @@
V(Uint8x16_string, "Uint8x16") \
V(undefined_string, "undefined") \
V(undefined_to_string, "[object Undefined]") \
V(unicode_string, "unicode") \
V(URIError_string, "URIError") \
V(valueOf_string, "valueOf") \
V(values_string, "values") \
@ -199,7 +195,6 @@
V(promise_reject_reactions_symbol) \
V(promise_result_symbol) \
V(promise_state_symbol) \
V(regexp_last_match_info_symbol) \
V(sealed_symbol) \
V(stack_trace_symbol) \
V(strict_function_transition_symbol) \

View File

@ -17,7 +17,6 @@ var InternalArray = utils.InternalArray;
var InternalPackedArray = utils.InternalPackedArray;
var MaxSimple;
var MinSimple;
var lastMatchInfoSymbol = utils.ImportNow("regexp_last_match_info_symbol");
var matchSymbol = utils.ImportNow("match_symbol");
var replaceSymbol = utils.ImportNow("replace_symbol");
var searchSymbol = utils.ImportNow("search_symbol");
@ -924,8 +923,180 @@ function RegExpSubclassSearch(string) {
%FunctionRemovePrototype(RegExpSubclassSearch);
// 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.
function RegExpGetLastMatch() {
var regExpSubject = LAST_SUBJECT(RegExpLastMatchInfo);
return %_SubString(regExpSubject,
RegExpLastMatchInfo[CAPTURE0],
RegExpLastMatchInfo[CAPTURE1]);
}
function RegExpGetLastParen() {
var length = NUMBER_OF_CAPTURES(RegExpLastMatchInfo);
if (length <= 2) return ''; // There were no captures.
// 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.
var regExpSubject = LAST_SUBJECT(RegExpLastMatchInfo);
var start = RegExpLastMatchInfo[CAPTURE(length - 2)];
var end = RegExpLastMatchInfo[CAPTURE(length - 1)];
if (start != -1 && end != -1) {
return %_SubString(regExpSubject, start, end);
}
return "";
}
function RegExpGetLeftContext() {
var start_index;
var subject;
start_index = RegExpLastMatchInfo[CAPTURE0];
subject = LAST_SUBJECT(RegExpLastMatchInfo);
return %_SubString(subject, 0, start_index);
}
function RegExpGetRightContext() {
var start_index;
var subject;
start_index = RegExpLastMatchInfo[CAPTURE1];
subject = LAST_SUBJECT(RegExpLastMatchInfo);
return %_SubString(subject, start_index, subject.length);
}
// 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.
function RegExpMakeCaptureGetter(n) {
return function foo() {
var index = n * 2;
if (index >= NUMBER_OF_CAPTURES(RegExpLastMatchInfo)) return '';
var matchStart = RegExpLastMatchInfo[CAPTURE(index)];
var matchEnd = RegExpLastMatchInfo[CAPTURE(index + 1)];
if (matchStart == -1 || matchEnd == -1) return '';
return %_SubString(LAST_SUBJECT(RegExpLastMatchInfo), matchStart, matchEnd);
};
}
// ES6 21.2.5.3.
function RegExpGetFlags() {
if (!IS_RECEIVER(this)) {
throw %make_type_error(
kRegExpNonObject, "RegExp.prototype.flags", TO_STRING(this));
}
var result = '';
if (this.global) result += 'g';
if (this.ignoreCase) result += 'i';
if (this.multiline) result += 'm';
if (this.unicode) result += 'u';
if (this.sticky) result += 'y';
return result;
}
// ES6 21.2.5.4.
function RegExpGetGlobal() {
if (!IS_REGEXP(this)) {
// TODO(littledan): Remove this RegExp compat workaround
if (this === GlobalRegExp.prototype) {
%IncrementUseCounter(kRegExpPrototypeOldFlagGetter);
return UNDEFINED;
}
throw %make_type_error(kRegExpNonRegExp, "RegExp.prototype.global");
}
return TO_BOOLEAN(REGEXP_GLOBAL(this));
}
%SetForceInlineFlag(RegExpGetGlobal);
// ES6 21.2.5.5.
function RegExpGetIgnoreCase() {
if (!IS_REGEXP(this)) {
// TODO(littledan): Remove this RegExp compat workaround
if (this === GlobalRegExp.prototype) {
%IncrementUseCounter(kRegExpPrototypeOldFlagGetter);
return UNDEFINED;
}
throw %make_type_error(kRegExpNonRegExp, "RegExp.prototype.ignoreCase");
}
return TO_BOOLEAN(REGEXP_IGNORE_CASE(this));
}
// ES6 21.2.5.7.
function RegExpGetMultiline() {
if (!IS_REGEXP(this)) {
// TODO(littledan): Remove this RegExp compat workaround
if (this === GlobalRegExp.prototype) {
%IncrementUseCounter(kRegExpPrototypeOldFlagGetter);
return UNDEFINED;
}
throw %make_type_error(kRegExpNonRegExp, "RegExp.prototype.multiline");
}
return TO_BOOLEAN(REGEXP_MULTILINE(this));
}
// ES6 21.2.5.10.
function RegExpGetSource() {
if (!IS_REGEXP(this)) {
// TODO(littledan): Remove this RegExp compat workaround
if (this === GlobalRegExp.prototype) {
%IncrementUseCounter(kRegExpPrototypeSourceGetter);
return "(?:)";
}
throw %make_type_error(kRegExpNonRegExp, "RegExp.prototype.source");
}
return REGEXP_SOURCE(this);
}
// ES6 21.2.5.12.
function RegExpGetSticky() {
if (!IS_REGEXP(this)) {
// Compat fix: RegExp.prototype.sticky == undefined; UseCounter tracks it
// TODO(littledan): Remove this workaround or standardize it
if (this === GlobalRegExp.prototype) {
%IncrementUseCounter(kRegExpPrototypeStickyGetter);
return UNDEFINED;
}
throw %make_type_error(kRegExpNonRegExp, "RegExp.prototype.sticky");
}
return TO_BOOLEAN(REGEXP_STICKY(this));
}
%SetForceInlineFlag(RegExpGetSticky);
// ES6 21.2.5.15.
function RegExpGetUnicode() {
if (!IS_REGEXP(this)) {
// TODO(littledan): Remove this RegExp compat workaround
if (this === GlobalRegExp.prototype) {
%IncrementUseCounter(kRegExpPrototypeUnicodeGetter);
return UNDEFINED;
}
throw %make_type_error(kRegExpNonRegExp, "RegExp.prototype.unicode");
}
return TO_BOOLEAN(REGEXP_UNICODE(this));
}
%SetForceInlineFlag(RegExpGetUnicode);
function RegExpSpecies() {
return this;
}
// -------------------------------------------------------------------
utils.InstallGetter(GlobalRegExp, speciesSymbol, RegExpSpecies);
utils.InstallFunctions(GlobalRegExp.prototype, DONT_ENUM, [
"exec", RegExpSubclassExecJS,
"test", RegExpSubclassTest,
@ -937,8 +1108,58 @@ utils.InstallFunctions(GlobalRegExp.prototype, DONT_ENUM, [
splitSymbol, RegExpSubclassSplit,
]);
// Temporary until all RegExpLastMatchInfo accesses are ported to C++.
SET_PRIVATE(GlobalRegExp, lastMatchInfoSymbol, RegExpLastMatchInfo);
utils.InstallGetter(GlobalRegExp.prototype, 'flags', RegExpGetFlags);
utils.InstallGetter(GlobalRegExp.prototype, 'global', RegExpGetGlobal);
utils.InstallGetter(GlobalRegExp.prototype, 'ignoreCase', RegExpGetIgnoreCase);
utils.InstallGetter(GlobalRegExp.prototype, 'multiline', RegExpGetMultiline);
utils.InstallGetter(GlobalRegExp.prototype, 'source', RegExpGetSource);
utils.InstallGetter(GlobalRegExp.prototype, 'sticky', RegExpGetSticky);
utils.InstallGetter(GlobalRegExp.prototype, 'unicode', RegExpGetUnicode);
// 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.
var RegExpGetInput = function() {
var regExpInput = LAST_INPUT(RegExpLastMatchInfo);
return IS_UNDEFINED(regExpInput) ? "" : regExpInput;
};
var RegExpSetInput = function(string) {
LAST_INPUT(RegExpLastMatchInfo) = TO_STRING(string);
};
%OptimizeObjectForAddingMultipleProperties(GlobalRegExp, 22);
utils.InstallGetterSetter(GlobalRegExp, 'input', RegExpGetInput, RegExpSetInput,
DONT_DELETE);
utils.InstallGetterSetter(GlobalRegExp, '$_', RegExpGetInput, RegExpSetInput,
DONT_ENUM | DONT_DELETE);
var NoOpSetter = function(ignored) {};
// Static properties set by a successful match.
utils.InstallGetterSetter(GlobalRegExp, 'lastMatch', RegExpGetLastMatch,
NoOpSetter, DONT_DELETE);
utils.InstallGetterSetter(GlobalRegExp, '$&', RegExpGetLastMatch, NoOpSetter,
DONT_ENUM | DONT_DELETE);
utils.InstallGetterSetter(GlobalRegExp, 'lastParen', RegExpGetLastParen,
NoOpSetter, DONT_DELETE);
utils.InstallGetterSetter(GlobalRegExp, '$+', RegExpGetLastParen, NoOpSetter,
DONT_ENUM | DONT_DELETE);
utils.InstallGetterSetter(GlobalRegExp, 'leftContext', RegExpGetLeftContext,
NoOpSetter, DONT_DELETE);
utils.InstallGetterSetter(GlobalRegExp, '$`', RegExpGetLeftContext, NoOpSetter,
DONT_ENUM | DONT_DELETE);
utils.InstallGetterSetter(GlobalRegExp, 'rightContext', RegExpGetRightContext,
NoOpSetter, DONT_DELETE);
utils.InstallGetterSetter(GlobalRegExp, "$'", RegExpGetRightContext, NoOpSetter,
DONT_ENUM | DONT_DELETE);
for (var i = 1; i < 10; ++i) {
utils.InstallGetterSetter(GlobalRegExp, '$' + i, RegExpMakeCaptureGetter(i),
NoOpSetter, DONT_DELETE);
}
%ToFastProperties(GlobalRegExp);
// -------------------------------------------------------------------
// Internal

View File

@ -418,6 +418,7 @@ class ErrorUtils : public AllStatic {
T(ReduceNoInitial, "Reduce of empty array with no initial value") \
T(RegExpFlags, \
"Cannot supply flags when constructing one RegExp from another") \
T(RegExpNonObject, "% getter called on non-object %") \
T(RegExpNonRegExp, "% getter called on non-RegExp object") \
T(ReinitializeIntl, "Trying to re-initialize % object.") \
T(ResolvedOptionsCalledOnNonObject, \

View File

@ -87,7 +87,7 @@ bytecodes: [
B(TestEqualStrict), R(12), U8(20),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), U16(128),
B(Wide), B(LdaSmi), U16(129),
B(Star), R(12),
B(LdaConstant), U8(9),
B(Star), R(13),
@ -226,7 +226,7 @@ bytecodes: [
B(TestEqualStrict), R(13), U8(20),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), U16(128),
B(Wide), B(LdaSmi), U16(129),
B(Star), R(13),
B(LdaConstant), U8(9),
B(Star), R(14),
@ -379,7 +379,7 @@ bytecodes: [
B(TestEqualStrict), R(12), U8(22),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), U16(128),
B(Wide), B(LdaSmi), U16(129),
B(Star), R(12),
B(LdaConstant), U8(9),
B(Star), R(13),
@ -521,7 +521,7 @@ bytecodes: [
B(TestEqualStrict), R(11), U8(24),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), U16(128),
B(Wide), B(LdaSmi), U16(129),
B(Star), R(11),
B(LdaConstant), U8(11),
B(Star), R(12),

View File

@ -476,7 +476,7 @@ bytecodes: [
B(TestEqualStrict), R(11), U8(20),
B(JumpIfFalse), U8(4),
B(Jump), U8(18),
B(Wide), B(LdaSmi), U16(128),
B(Wide), B(LdaSmi), U16(129),
B(Star), R(11),
B(LdaConstant), U8(15),
B(Star), R(12),

View File

@ -123,6 +123,11 @@ test(function() {
}, "First argument to String.prototype.startsWith " +
"must not be a regular expression", TypeError);
// kFlagsGetterNonObject
test(function() {
Object.getOwnPropertyDescriptor(RegExp.prototype, "flags").get.call(1);
}, "RegExp.prototype.flags getter called on non-object 1", TypeError);
// kFunctionBind
test(function() {
Function.prototype.bind.call(1);