Commit 4a5de9d9 authored by yangguo's avatar yangguo Committed by Commit bot

Eagerly escape RegExp.source.

Escaping used to happen lazily, implemented in an accessor property.
However, native implementation of RegExp methods use .source as well.
This leads to performance regressions. Now we do it eagerly instead.

R=jkummerow@chromium.org
BUG=chromium:436447
LOG=N

Review URL: https://codereview.chromium.org/1070093002

Cr-Commit-Position: refs/heads/master@{#27705}
parent 107dbc96
......@@ -312,98 +312,6 @@ Handle<AccessorInfo> Accessors::StringLengthInfo(
}
template <typename Char>
inline int CountRequiredEscapes(Handle<String> source) {
DisallowHeapAllocation no_gc;
int escapes = 0;
Vector<const Char> src = source->GetCharVector<Char>();
for (int i = 0; i < src.length(); i++) {
if (src[i] == '/' && (i == 0 || src[i - 1] != '\\')) escapes++;
}
return escapes;
}
template <typename Char, typename StringType>
inline Handle<StringType> WriteEscapedRegExpSource(Handle<String> source,
Handle<StringType> result) {
DisallowHeapAllocation no_gc;
Vector<const Char> src = source->GetCharVector<Char>();
Vector<Char> dst(result->GetChars(), result->length());
int s = 0;
int d = 0;
while (s < src.length()) {
if (src[s] == '/' && (s == 0 || src[s - 1] != '\\')) dst[d++] = '\\';
dst[d++] = src[s++];
}
DCHECK_EQ(result->length(), d);
return result;
}
MaybeHandle<String> EscapeRegExpSource(Isolate* isolate,
Handle<String> source) {
String::Flatten(source);
if (source->length() == 0) return isolate->factory()->query_colon_string();
bool one_byte = source->IsOneByteRepresentationUnderneath();
int escapes = one_byte ? CountRequiredEscapes<uint8_t>(source)
: CountRequiredEscapes<uc16>(source);
if (escapes == 0) return source;
int length = source->length() + escapes;
if (one_byte) {
Handle<SeqOneByteString> result;
ASSIGN_RETURN_ON_EXCEPTION(isolate, result,
isolate->factory()->NewRawOneByteString(length),
String);
return WriteEscapedRegExpSource<uint8_t>(source, result);
} else {
Handle<SeqTwoByteString> result;
ASSIGN_RETURN_ON_EXCEPTION(isolate, result,
isolate->factory()->NewRawTwoByteString(length),
String);
return WriteEscapedRegExpSource<uc16>(source, result);
}
}
// Implements ECMA262 ES6 draft 21.2.5.9
void Accessors::RegExpSourceGetter(
v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
HandleScope scope(isolate);
Handle<Object> holder =
Utils::OpenHandle(*v8::Local<v8::Value>(info.Holder()));
Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(holder);
Handle<String> result;
if (regexp->TypeTag() == JSRegExp::NOT_COMPILED) {
result = isolate->factory()->empty_string();
} else {
Handle<String> pattern(regexp->Pattern(), isolate);
MaybeHandle<String> maybe = EscapeRegExpSource(isolate, pattern);
if (!maybe.ToHandle(&result)) {
isolate->OptionalRescheduleException(false);
return;
}
}
info.GetReturnValue().Set(Utils::ToLocal(result));
}
void Accessors::RegExpSourceSetter(v8::Local<v8::Name> name,
v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<void>& info) {
UNREACHABLE();
}
Handle<AccessorInfo> Accessors::RegExpSourceInfo(
Isolate* isolate, PropertyAttributes attributes) {
return MakeAccessor(isolate, isolate->factory()->source_string(),
&RegExpSourceGetter, &RegExpSourceSetter, attributes);
}
//
// Accessors::ScriptColumnOffset
//
......
......@@ -21,7 +21,6 @@ namespace internal {
V(FunctionName) \
V(FunctionLength) \
V(FunctionPrototype) \
V(RegExpSource) \
V(ScriptColumnOffset) \
V(ScriptCompilationType) \
V(ScriptContextData) \
......
......@@ -1066,11 +1066,10 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> global_object,
{
// ECMA-262, section 15.10.7.1.
Handle<AccessorInfo> regexp_source(
Accessors::RegExpSourceInfo(isolate, final));
AccessorConstantDescriptor d(factory->source_string(), regexp_source,
final);
initial_map->AppendDescriptor(&d);
DataDescriptor field(factory->source_string(),
JSRegExp::kSourceFieldIndex, final,
Representation::Tagged());
initial_map->AppendDescriptor(&field);
}
{
// ECMA-262, section 15.10.7.2.
......@@ -1114,6 +1113,8 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> global_object,
Handle<Map> proto_map = Map::Copy(initial_map, "RegExpPrototype");
DCHECK(proto_map->prototype() == *isolate->initial_object_prototype());
Handle<JSObject> proto = factory->NewJSObjectFromMap(proto_map);
proto->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex,
heap->query_colon_string());
proto->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex,
heap->false_value());
proto->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex,
......
......@@ -8120,11 +8120,12 @@ class JSRegExp: public JSObject {
FixedArray::kHeaderSize + kIrregexpCaptureCountIndex * kPointerSize;
// In-object fields.
static const int kGlobalFieldIndex = 0;
static const int kIgnoreCaseFieldIndex = 1;
static const int kMultilineFieldIndex = 2;
static const int kLastIndexFieldIndex = 3;
static const int kInObjectFieldCount = 4;
static const int kSourceFieldIndex = 0;
static const int kGlobalFieldIndex = 1;
static const int kIgnoreCaseFieldIndex = 2;
static const int kMultilineFieldIndex = 3;
static const int kLastIndexFieldIndex = 4;
static const int kInObjectFieldCount = 5;
// The uninitialized value for a regexp code object.
static const int kUninitializedValue = -1;
......
......@@ -844,6 +844,60 @@ static JSRegExp::Flags RegExpFlagsFromString(Handle<String> flags,
}
template <typename Char>
inline int CountRequiredEscapes(Handle<String> source) {
DisallowHeapAllocation no_gc;
int escapes = 0;
Vector<const Char> src = source->GetCharVector<Char>();
for (int i = 0; i < src.length(); i++) {
if (src[i] == '/' && (i == 0 || src[i - 1] != '\\')) escapes++;
}
return escapes;
}
template <typename Char, typename StringType>
inline Handle<StringType> WriteEscapedRegExpSource(Handle<String> source,
Handle<StringType> result) {
DisallowHeapAllocation no_gc;
Vector<const Char> src = source->GetCharVector<Char>();
Vector<Char> dst(result->GetChars(), result->length());
int s = 0;
int d = 0;
while (s < src.length()) {
if (src[s] == '/' && (s == 0 || src[s - 1] != '\\')) dst[d++] = '\\';
dst[d++] = src[s++];
}
DCHECK_EQ(result->length(), d);
return result;
}
MaybeHandle<String> EscapeRegExpSource(Isolate* isolate,
Handle<String> source) {
String::Flatten(source);
if (source->length() == 0) return isolate->factory()->query_colon_string();
bool one_byte = source->IsOneByteRepresentationUnderneath();
int escapes = one_byte ? CountRequiredEscapes<uint8_t>(source)
: CountRequiredEscapes<uc16>(source);
if (escapes == 0) return source;
int length = source->length() + escapes;
if (one_byte) {
Handle<SeqOneByteString> result;
ASSIGN_RETURN_ON_EXCEPTION(isolate, result,
isolate->factory()->NewRawOneByteString(length),
String);
return WriteEscapedRegExpSource<uint8_t>(source, result);
} else {
Handle<SeqTwoByteString> result;
ASSIGN_RETURN_ON_EXCEPTION(isolate, result,
isolate->factory()->NewRawTwoByteString(length),
String);
return WriteEscapedRegExpSource<uc16>(source, result);
}
}
RUNTIME_FUNCTION(Runtime_RegExpInitializeAndCompile) {
HandleScope scope(isolate);
DCHECK(args.length() == 3);
......@@ -865,6 +919,10 @@ RUNTIME_FUNCTION(Runtime_RegExpInitializeAndCompile) {
isolate, NewSyntaxError("invalid_regexp_flags", args));
}
Handle<String> escaped_source;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, escaped_source,
EscapeRegExpSource(isolate, source));
Handle<Object> global = factory->ToBoolean(flags.is_global());
Handle<Object> ignore_case = factory->ToBoolean(flags.is_ignore_case());
Handle<Object> multiline = factory->ToBoolean(flags.is_multiline());
......@@ -877,6 +935,7 @@ RUNTIME_FUNCTION(Runtime_RegExpInitializeAndCompile) {
constructor->IsJSFunction() &&
JSFunction::cast(constructor)->initial_map() == map) {
// If we still have the original map, set in-object properties directly.
regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, *escaped_source);
// Both true and false are immovable immortal objects so no need for write
// barrier.
regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, *global,
......@@ -898,6 +957,8 @@ RUNTIME_FUNCTION(Runtime_RegExpInitializeAndCompile) {
PropertyAttributes writable =
static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
Handle<Object> zero(Smi::FromInt(0), isolate);
JSObject::SetOwnPropertyIgnoreAttributes(regexp, factory->source_string(),
escaped_source, final).Check();
JSObject::SetOwnPropertyIgnoreAttributes(regexp, factory->global_string(),
global, final).Check();
JSObject::SetOwnPropertyIgnoreAttributes(
......
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