Commit 4e219bb4 authored by jgruber's avatar jgruber Committed by Commit bot

[regexp] Port RegExp getters and setters

Flag getters are implemented as TurboFan stubs while the rest are written as
C++. This distinction is somewhat arbitrary and more getters could be ported to
TurboFan in the future.

BUG=v8:5339

Review-Url: https://codereview.chromium.org/2389233002
Cr-Commit-Position: refs/heads/master@{#40036}
parent a105dafa
......@@ -364,17 +364,6 @@ 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,
......@@ -460,17 +449,54 @@ 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, Builtins::Name call,
bool adapt) {
Handle<String> name,
Handle<Name> property_name,
Builtins::Name call, bool adapt) {
Isolate* const isolate = base->GetIsolate();
Handle<String> fun_name =
Handle<String> getter_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> 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);
}
Handle<JSFunction> SimpleInstallGetter(Handle<JSObject> base,
......@@ -1660,14 +1686,105 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
shared->DontAdaptArguments();
shared->set_length(2);
// RegExp.prototype setup.
// Install the "constructor" property on the {prototype}.
JSObject::AddProperty(prototype, factory->constructor_string(), regexp_fun,
DONT_ENUM);
{
// RegExp.prototype setup.
// Install the "constructor" property on the {prototype}.
JSObject::AddProperty(prototype, factory->constructor_string(),
regexp_fun, DONT_ENUM);
SimpleInstallFunction(prototype, "exec", Builtins::kRegExpPrototypeExec,
1, true, DONT_ENUM);
SimpleInstallGetter(prototype, factory->flags_string(),
Builtins::kRegExpPrototypeFlagsGetter, true);
SimpleInstallGetter(prototype, factory->global_string(),
Builtins::kRegExpPrototypeGlobalGetter, true);
SimpleInstallGetter(prototype, factory->ignoreCase_string(),
Builtins::kRegExpPrototypeIgnoreCaseGetter, true);
SimpleInstallGetter(prototype, factory->multiline_string(),
Builtins::kRegExpPrototypeMultilineGetter, true);
SimpleInstallGetter(prototype, factory->source_string(),
Builtins::kRegExpPrototypeSourceGetter, false);
SimpleInstallGetter(prototype, factory->sticky_string(),
Builtins::kRegExpPrototypeStickyGetter, true);
SimpleInstallGetter(prototype, factory->unicode_string(),
Builtins::kRegExpPrototypeUnicodeGetter, true);
}
SimpleInstallFunction(prototype, "exec", Builtins::kRegExpPrototypeExec, 1,
true, DONT_ENUM);
{
// 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.
// On the other hand, installing attributes as DONT_ENUM matches the draft
// specification at
// https://github.com/claudepache/es-regexp-legacy-static-properties.
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::kRegExpInputGetter,
Builtins::kRegExpInputSetter, DONT_DELETE);
SimpleInstallGetterSetter(
regexp_fun, factory->InternalizeUtf8String("$_"),
Builtins::kRegExpInputGetter, Builtins::kRegExpInputSetter, no_enum);
SimpleInstallGetterSetter(
regexp_fun, factory->InternalizeUtf8String("lastMatch"),
Builtins::kRegExpLastMatchGetter, Builtins::kEmptyFunction, no_enum);
SimpleInstallGetterSetter(
regexp_fun, factory->InternalizeUtf8String("$&"),
Builtins::kRegExpLastMatchGetter, Builtins::kEmptyFunction, no_enum);
SimpleInstallGetterSetter(
regexp_fun, factory->InternalizeUtf8String("lastParen"),
Builtins::kRegExpLastParenGetter, Builtins::kEmptyFunction, no_enum);
SimpleInstallGetterSetter(
regexp_fun, factory->InternalizeUtf8String("$+"),
Builtins::kRegExpLastParenGetter, Builtins::kEmptyFunction, no_enum);
SimpleInstallGetterSetter(regexp_fun,
factory->InternalizeUtf8String("leftContext"),
Builtins::kRegExpLeftContextGetter,
Builtins::kEmptyFunction, no_enum);
SimpleInstallGetterSetter(regexp_fun,
factory->InternalizeUtf8String("$`"),
Builtins::kRegExpLeftContextGetter,
Builtins::kEmptyFunction, no_enum);
SimpleInstallGetterSetter(regexp_fun,
factory->InternalizeUtf8String("rightContext"),
Builtins::kRegExpRightContextGetter,
Builtins::kEmptyFunction, no_enum);
SimpleInstallGetterSetter(regexp_fun,
factory->InternalizeUtf8String("$'"),
Builtins::kRegExpRightContextGetter,
Builtins::kEmptyFunction, no_enum);
#define INSTALL_CAPTURE_GETTER(i) \
SimpleInstallGetterSetter(regexp_fun, \
factory->InternalizeUtf8String("$" #i), \
Builtins::kRegExpCapture##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
}
DCHECK(regexp_fun->has_initial_map());
Handle<Map> initial_map(regexp_fun->initial_map());
......
This diff is collapsed.
......@@ -533,8 +533,31 @@ namespace internal {
CPP(ReflectSetPrototypeOf) \
\
/* RegExp */ \
CPP(RegExpCapture1Getter) \
CPP(RegExpCapture2Getter) \
CPP(RegExpCapture3Getter) \
CPP(RegExpCapture4Getter) \
CPP(RegExpCapture5Getter) \
CPP(RegExpCapture6Getter) \
CPP(RegExpCapture7Getter) \
CPP(RegExpCapture8Getter) \
CPP(RegExpCapture9Getter) \
CPP(RegExpConstructor) \
CPP(RegExpInputGetter) \
CPP(RegExpInputSetter) \
CPP(RegExpLastMatchGetter) \
CPP(RegExpLastParenGetter) \
CPP(RegExpLeftContextGetter) \
TFJ(RegExpPrototypeExec, 2) \
TFJ(RegExpPrototypeFlagsGetter, 1) \
TFJ(RegExpPrototypeGlobalGetter, 1) \
TFJ(RegExpPrototypeIgnoreCaseGetter, 1) \
TFJ(RegExpPrototypeMultilineGetter, 1) \
CPP(RegExpPrototypeSourceGetter) \
CPP(RegExpPrototypeSpeciesGetter) \
TFJ(RegExpPrototypeStickyGetter, 1) \
TFJ(RegExpPrototypeUnicodeGetter, 1) \
CPP(RegExpRightContextGetter) \
\
/* SharedArrayBuffer */ \
CPP(SharedArrayBufferPrototypeGetByteLength) \
......
......@@ -23,6 +23,7 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
V(BooleanMap, BooleanMap) \
V(empty_string, EmptyString) \
V(EmptyFixedArray, EmptyFixedArray) \
V(FalseValue, False) \
V(FixedArrayMap, FixedArrayMap) \
V(FixedCOWArrayMap, FixedCOWArrayMap) \
V(FixedDoubleArrayMap, FixedDoubleArrayMap) \
......@@ -31,6 +32,7 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
V(NanValue, Nan) \
V(NullValue, Null) \
V(TheHoleValue, TheHole) \
V(TrueValue, True) \
V(UndefinedValue, Undefined)
// Provides JavaScript-specific "macro-assembler" functionality on top of the
......
......@@ -140,6 +140,7 @@
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") \
......@@ -163,6 +164,7 @@
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") \
......
......@@ -777,173 +777,8 @@ 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)) {
if (this === GlobalRegExpPrototype) {
%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)) {
if (this === GlobalRegExpPrototype) {
%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)) {
if (this === GlobalRegExpPrototype) {
%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)) {
if (this === GlobalRegExpPrototype) {
%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)) {
if (this === GlobalRegExpPrototype) {
%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)) {
if (this === GlobalRegExpPrototype) {
%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, [
"test", RegExpSubclassTest,
"toString", RegExpToString,
......@@ -954,68 +789,6 @@ utils.InstallFunctions(GlobalRegExp.prototype, DONT_ENUM, [
splitSymbol, RegExpSubclassSplit,
]);
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);
};
// TODO(jgruber): All of these getters and setters were intended to be installed
// with various attributes (e.g. DONT_ENUM | DONT_DELETE), but
// InstallGetterSetter had a bug which ignored the passed attributes and
// simply installed as DONT_ENUM instead. We might want to change back
// to the intended attributes at some point.
// On the other hand, installing attributes as DONT_ENUM matches the draft
// specification at
// https://github.com/claudepache/es-regexp-legacy-static-properties
%OptimizeObjectForAddingMultipleProperties(GlobalRegExp, 22);
utils.InstallGetterSetter(GlobalRegExp, 'input', RegExpGetInput, RegExpSetInput,
DONT_ENUM);
utils.InstallGetterSetter(GlobalRegExp, '$_', RegExpGetInput, RegExpSetInput,
DONT_ENUM);
var NoOpSetter = function(ignored) {};
// Static properties set by a successful match.
utils.InstallGetterSetter(GlobalRegExp, 'lastMatch', RegExpGetLastMatch,
NoOpSetter, DONT_ENUM);
utils.InstallGetterSetter(GlobalRegExp, '$&', RegExpGetLastMatch, NoOpSetter,
DONT_ENUM);
utils.InstallGetterSetter(GlobalRegExp, 'lastParen', RegExpGetLastParen,
NoOpSetter, DONT_ENUM);
utils.InstallGetterSetter(GlobalRegExp, '$+', RegExpGetLastParen, NoOpSetter,
DONT_ENUM);
utils.InstallGetterSetter(GlobalRegExp, 'leftContext', RegExpGetLeftContext,
NoOpSetter, DONT_ENUM);
utils.InstallGetterSetter(GlobalRegExp, '$`', RegExpGetLeftContext, NoOpSetter,
DONT_ENUM);
utils.InstallGetterSetter(GlobalRegExp, 'rightContext', RegExpGetRightContext,
NoOpSetter, DONT_ENUM);
utils.InstallGetterSetter(GlobalRegExp, "$'", RegExpGetRightContext, NoOpSetter,
DONT_ENUM);
for (var i = 1; i < 10; ++i) {
utils.InstallGetterSetter(GlobalRegExp, '$' + i, RegExpMakeCaptureGetter(i),
NoOpSetter, DONT_ENUM);
}
%ToFastProperties(GlobalRegExp);
%InstallToContext(["regexp_last_match_info", RegExpLastMatchInfo]);
// -------------------------------------------------------------------
......
......@@ -100,6 +100,23 @@ RUNTIME_FUNCTION(Runtime_ThrowStackOverflow) {
return isolate->StackOverflow();
}
RUNTIME_FUNCTION(Runtime_ThrowTypeError) {
HandleScope scope(isolate);
DCHECK_LE(1, args.length());
CONVERT_SMI_ARG_CHECKED(message_id_smi, 0);
Handle<Object> undefined = isolate->factory()->undefined_value();
Handle<Object> arg0 = (args.length() > 1) ? args.at<Object>(1) : undefined;
Handle<Object> arg1 = (args.length() > 2) ? args.at<Object>(2) : undefined;
Handle<Object> arg2 = (args.length() > 3) ? args.at<Object>(3) : undefined;
MessageTemplate::Template message_id =
static_cast<MessageTemplate::Template>(message_id_smi);
THROW_NEW_ERROR_RETURN_FAILURE(isolate,
NewTypeError(message_id, arg0, arg1, arg2));
}
RUNTIME_FUNCTION(Runtime_ThrowWasmError) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
......
......@@ -325,6 +325,7 @@ namespace internal {
F(ThrowNotGeneric, 1, 1) \
F(ThrowReferenceError, 1, 1) \
F(ThrowStackOverflow, 0, 1) \
F(ThrowTypeError, -1 /* >= 1 */, 1) \
F(ThrowWasmError, 2, 1) \
F(ThrowUndefinedOrNullToObject, 1, 1) \
F(Typeof, 1, 1) \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment