Introduce code flushing of RegExp code.
Due to issues relating mostly to chrome extensions we have lately been running into OOMs that are caused by our executable space running out. This change introduces flushing of code from regexps if we have not used the code for 5 mark sweeps. The approach is different from the normal function code flusing. Here we make a copy of the code inside the data array, and exchange the original code with a smi determined by the sweep_generation (a new heap variable increased everytime we do mark sweep/compact). If we encounter a smi in EnsureCompiled we simply reinstate the code object. If, in the marking phase of mark sweep, we find a regexp that already have a smi in the code field, and this is more than 5 generations old we flush the code from the saved index. Review URL: http://codereview.chromium.org/7282026 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8532 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
792a6652ad
commit
0f682143d9
@ -4413,9 +4413,8 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
||||
|
||||
// Check that the irregexp code has been generated for the actual string
|
||||
// encoding. If it has, the field contains a code object otherwise it contains
|
||||
// the hole.
|
||||
__ CompareObjectType(r7, r0, r0, CODE_TYPE);
|
||||
__ b(ne, &runtime);
|
||||
// a smi (code flushing support).
|
||||
__ JumpIfSmi(r7, &runtime);
|
||||
|
||||
// r3: encoding of subject string (1 if ASCII, 0 if two_byte);
|
||||
// r7: code
|
||||
|
@ -1221,12 +1221,14 @@ void Factory::SetRegExpIrregexpData(Handle<JSRegExp> regexp,
|
||||
JSRegExp::Flags flags,
|
||||
int capture_count) {
|
||||
Handle<FixedArray> store = NewFixedArray(JSRegExp::kIrregexpDataSize);
|
||||
|
||||
Smi* uninitialized = Smi::FromInt(JSRegExp::kUninitializedValue);
|
||||
store->set(JSRegExp::kTagIndex, Smi::FromInt(type));
|
||||
store->set(JSRegExp::kSourceIndex, *source);
|
||||
store->set(JSRegExp::kFlagsIndex, Smi::FromInt(flags.value()));
|
||||
store->set(JSRegExp::kIrregexpASCIICodeIndex, HEAP->the_hole_value());
|
||||
store->set(JSRegExp::kIrregexpUC16CodeIndex, HEAP->the_hole_value());
|
||||
store->set(JSRegExp::kIrregexpASCIICodeIndex, uninitialized);
|
||||
store->set(JSRegExp::kIrregexpUC16CodeIndex, uninitialized);
|
||||
store->set(JSRegExp::kIrregexpASCIICodeSavedIndex, uninitialized);
|
||||
store->set(JSRegExp::kIrregexpUC16CodeSavedIndex, uninitialized);
|
||||
store->set(JSRegExp::kIrregexpMaxRegisterCountIndex, Smi::FromInt(0));
|
||||
store->set(JSRegExp::kIrregexpCaptureCountIndex,
|
||||
Smi::FromInt(capture_count));
|
||||
|
@ -97,6 +97,7 @@ Heap::Heap()
|
||||
// Will be 4 * reserved_semispace_size_ to ensure that young
|
||||
// generation can be aligned to its size.
|
||||
survived_since_last_expansion_(0),
|
||||
sweep_generation_(0),
|
||||
always_allocate_scope_depth_(0),
|
||||
linear_allocation_scope_depth_(0),
|
||||
contexts_disposed_(0),
|
||||
@ -742,7 +743,7 @@ bool Heap::PerformGarbageCollection(GarbageCollector collector,
|
||||
if (collector == MARK_COMPACTOR) {
|
||||
// Perform mark-sweep with optional compaction.
|
||||
MarkCompact(tracer);
|
||||
|
||||
sweep_generation_++;
|
||||
bool high_survival_rate_during_scavenges = IsHighSurvivalRate() &&
|
||||
IsStableOrIncreasingSurvivalTrend();
|
||||
|
||||
@ -1305,6 +1306,10 @@ class ScavengingVisitor : public StaticVisitorBase {
|
||||
&ObjectEvacuationStrategy<POINTER_OBJECT>::
|
||||
template VisitSpecialized<SharedFunctionInfo::kSize>);
|
||||
|
||||
table_.Register(kVisitJSRegExp,
|
||||
&ObjectEvacuationStrategy<POINTER_OBJECT>::
|
||||
Visit);
|
||||
|
||||
table_.Register(kVisitJSFunction,
|
||||
&ObjectEvacuationStrategy<POINTER_OBJECT>::
|
||||
template VisitSpecialized<JSFunction::kSize>);
|
||||
|
@ -1256,6 +1256,11 @@ class Heap {
|
||||
return &external_string_table_;
|
||||
}
|
||||
|
||||
// Returns the current sweep generation.
|
||||
int sweep_generation() {
|
||||
return sweep_generation_;
|
||||
}
|
||||
|
||||
inline Isolate* isolate();
|
||||
bool is_safe_to_read_maps() { return is_safe_to_read_maps_; }
|
||||
|
||||
@ -1285,6 +1290,9 @@ class Heap {
|
||||
// scavenge since last new space expansion.
|
||||
int survived_since_last_expansion_;
|
||||
|
||||
// For keeping track on when to flush RegExp code.
|
||||
int sweep_generation_;
|
||||
|
||||
int always_allocate_scope_depth_;
|
||||
int linear_allocation_scope_depth_;
|
||||
|
||||
|
@ -3376,9 +3376,8 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
||||
__ bind(&check_code);
|
||||
// Check that the irregexp code has been generated for the actual string
|
||||
// encoding. If it has, the field contains a code object otherwise it contains
|
||||
// the hole.
|
||||
__ CmpObjectType(edx, CODE_TYPE, ebx);
|
||||
__ j(not_equal, &runtime);
|
||||
// a smi (code flushing support).
|
||||
__ JumpIfSmi(edx, &runtime);
|
||||
|
||||
// eax: subject string
|
||||
// edx: code
|
||||
|
@ -295,23 +295,62 @@ bool RegExpImpl::EnsureCompiledIrregexp(Handle<JSRegExp> re, bool is_ascii) {
|
||||
#else // V8_INTERPRETED_REGEXP (RegExp native code)
|
||||
if (compiled_code->IsCode()) return true;
|
||||
#endif
|
||||
// We could potentially have marked this as flushable, but have kept
|
||||
// a saved version if we did not flush it yet.
|
||||
Object* saved_code = re->DataAt(JSRegExp::saved_code_index(is_ascii));
|
||||
if (saved_code->IsCode()) {
|
||||
// Reinstate the code in the original place.
|
||||
re->SetDataAt(JSRegExp::code_index(is_ascii), saved_code);
|
||||
ASSERT(compiled_code->IsSmi());
|
||||
return true;
|
||||
}
|
||||
return CompileIrregexp(re, is_ascii);
|
||||
}
|
||||
|
||||
|
||||
static bool CreateRegExpErrorObjectAndThrow(Handle<JSRegExp> re,
|
||||
bool is_ascii,
|
||||
Handle<String> error_message,
|
||||
Isolate* isolate) {
|
||||
Factory* factory = isolate->factory();
|
||||
Handle<FixedArray> elements = factory->NewFixedArray(2);
|
||||
elements->set(0, re->Pattern());
|
||||
elements->set(1, *error_message);
|
||||
Handle<JSArray> array = factory->NewJSArrayWithElements(elements);
|
||||
Handle<Object> regexp_err =
|
||||
factory->NewSyntaxError("malformed_regexp", array);
|
||||
isolate->Throw(*regexp_err);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool RegExpImpl::CompileIrregexp(Handle<JSRegExp> re, bool is_ascii) {
|
||||
// Compile the RegExp.
|
||||
Isolate* isolate = re->GetIsolate();
|
||||
ZoneScope zone_scope(isolate, DELETE_ON_EXIT);
|
||||
PostponeInterruptsScope postpone(isolate);
|
||||
// If we had a compilation error the last time this is saved at the
|
||||
// saved code index.
|
||||
Object* entry = re->DataAt(JSRegExp::code_index(is_ascii));
|
||||
if (entry->IsJSObject()) {
|
||||
// If it's a JSObject, a previous compilation failed and threw this object.
|
||||
// Re-throw the object without trying again.
|
||||
isolate->Throw(entry);
|
||||
// When arriving here entry can only be a smi, either representing an
|
||||
// uncompiled regexp, a previous compilation error, or code that has
|
||||
// been flushed.
|
||||
ASSERT(entry->IsSmi());
|
||||
int entry_value = Smi::cast(entry)->value();
|
||||
ASSERT(entry_value == JSRegExp::kUninitializedValue ||
|
||||
entry_value == JSRegExp::kCompilationErrorValue ||
|
||||
(entry_value < JSRegExp::kCodeAgeMask && entry_value >= 0));
|
||||
|
||||
if (entry_value == JSRegExp::kCompilationErrorValue) {
|
||||
// A previous compilation failed and threw an error which we store in
|
||||
// the saved code index (we store the error message, not the actual
|
||||
// error). Recreate the error object and throw it.
|
||||
Object* error_string = re->DataAt(JSRegExp::saved_code_index(is_ascii));
|
||||
ASSERT(error_string->IsString());
|
||||
Handle<String> error_message(String::cast(error_string));
|
||||
CreateRegExpErrorObjectAndThrow(re, is_ascii, error_message, isolate);
|
||||
return false;
|
||||
}
|
||||
ASSERT(entry->IsTheHole());
|
||||
|
||||
JSRegExp::Flags flags = re->GetFlags();
|
||||
|
||||
@ -340,17 +379,9 @@ bool RegExpImpl::CompileIrregexp(Handle<JSRegExp> re, bool is_ascii) {
|
||||
is_ascii);
|
||||
if (result.error_message != NULL) {
|
||||
// Unable to compile regexp.
|
||||
Factory* factory = isolate->factory();
|
||||
Handle<FixedArray> elements = factory->NewFixedArray(2);
|
||||
elements->set(0, *pattern);
|
||||
Handle<String> error_message =
|
||||
factory->NewStringFromUtf8(CStrVector(result.error_message));
|
||||
elements->set(1, *error_message);
|
||||
Handle<JSArray> array = factory->NewJSArrayWithElements(elements);
|
||||
Handle<Object> regexp_err =
|
||||
factory->NewSyntaxError("malformed_regexp", array);
|
||||
isolate->Throw(*regexp_err);
|
||||
re->SetDataAt(JSRegExp::code_index(is_ascii), *regexp_err);
|
||||
isolate->factory()->NewStringFromUtf8(CStrVector(result.error_message));
|
||||
CreateRegExpErrorObjectAndThrow(re, is_ascii, error_message, isolate);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -424,6 +424,9 @@ class StaticMarkingVisitor : public StaticVisitorBase {
|
||||
table_.Register(kVisitJSFunction,
|
||||
&VisitJSFunctionAndFlushCode);
|
||||
|
||||
table_.Register(kVisitJSRegExp,
|
||||
&VisitRegExpAndFlushCode);
|
||||
|
||||
table_.Register(kVisitPropertyCell,
|
||||
&FixedBodyVisitor<StaticMarkingVisitor,
|
||||
JSGlobalPropertyCell::BodyDescriptor,
|
||||
@ -564,6 +567,8 @@ class StaticMarkingVisitor : public StaticVisitorBase {
|
||||
// flushed.
|
||||
static const int kCodeAgeThreshold = 5;
|
||||
|
||||
static const int kRegExpCodeThreshold = 5;
|
||||
|
||||
inline static bool HasSourceCode(Heap* heap, SharedFunctionInfo* info) {
|
||||
Object* undefined = heap->raw_unchecked_undefined_value();
|
||||
return (info->script() != undefined) &&
|
||||
@ -700,6 +705,68 @@ class StaticMarkingVisitor : public StaticVisitorBase {
|
||||
}
|
||||
|
||||
|
||||
static void UpdateRegExpCodeAgeAndFlush(Heap* heap,
|
||||
JSRegExp* re,
|
||||
bool is_ascii) {
|
||||
// Make sure that the fixed array is in fact initialized on the RegExp.
|
||||
// We could potentially trigger a GC when initializing the RegExp.
|
||||
if (SafeMap(re->data())->instance_type() != FIXED_ARRAY_TYPE) return;
|
||||
|
||||
// Make sure this is a RegExp that actually contains code.
|
||||
if (re->TypeTagUnchecked() != JSRegExp::IRREGEXP) return;
|
||||
|
||||
Object* code = re->DataAtUnchecked(JSRegExp::code_index(is_ascii));
|
||||
if (!code->IsSmi() && SafeMap(code)->instance_type() == CODE_TYPE) {
|
||||
// Save a copy that can be reinstated if we need the code again.
|
||||
re->SetDataAtUnchecked(JSRegExp::saved_code_index(is_ascii),
|
||||
code,
|
||||
heap);
|
||||
// Set a number in the 0-255 range to guarantee no smi overflow.
|
||||
re->SetDataAtUnchecked(JSRegExp::code_index(is_ascii),
|
||||
Smi::FromInt(heap->sweep_generation() & 0xff),
|
||||
heap);
|
||||
} else if (code->IsSmi()) {
|
||||
int value = Smi::cast(code)->value();
|
||||
// The regexp has not been compiled yet or there was a compilation error.
|
||||
if (value == JSRegExp::kUninitializedValue ||
|
||||
value == JSRegExp::kCompilationErrorValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we should flush now.
|
||||
if (value == ((heap->sweep_generation() - kRegExpCodeThreshold) & 0xff)) {
|
||||
re->SetDataAtUnchecked(JSRegExp::code_index(is_ascii),
|
||||
Smi::FromInt(JSRegExp::kUninitializedValue),
|
||||
heap);
|
||||
re->SetDataAtUnchecked(JSRegExp::saved_code_index(is_ascii),
|
||||
Smi::FromInt(JSRegExp::kUninitializedValue),
|
||||
heap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Works by setting the current sweep_generation (as a smi) in the
|
||||
// code object place in the data array of the RegExp and keeps a copy
|
||||
// around that can be reinstated if we reuse the RegExp before flushing.
|
||||
// If we did not use the code for kRegExpCodeThreshold mark sweep GCs
|
||||
// we flush the code.
|
||||
static void VisitRegExpAndFlushCode(Map* map, HeapObject* object) {
|
||||
Heap* heap = map->heap();
|
||||
MarkCompactCollector* collector = heap->mark_compact_collector();
|
||||
if (!collector->is_code_flushing_enabled()) {
|
||||
VisitJSRegExpFields(map, object);
|
||||
return;
|
||||
}
|
||||
JSRegExp* re = reinterpret_cast<JSRegExp*>(object);
|
||||
// Flush code or set age on both ascii and two byte code.
|
||||
UpdateRegExpCodeAgeAndFlush(heap, re, true);
|
||||
UpdateRegExpCodeAgeAndFlush(heap, re, false);
|
||||
// Visit the fields of the RegExp, including the updated FixedArray.
|
||||
VisitJSRegExpFields(map, object);
|
||||
}
|
||||
|
||||
|
||||
static void VisitSharedFunctionInfoAndFlushCode(Map* map,
|
||||
HeapObject* object) {
|
||||
MarkCompactCollector* collector = map->heap()->mark_compact_collector();
|
||||
@ -830,6 +897,15 @@ class StaticMarkingVisitor : public StaticVisitorBase {
|
||||
// Don't visit the next function list field as it is a weak reference.
|
||||
}
|
||||
|
||||
static inline void VisitJSRegExpFields(Map* map,
|
||||
HeapObject* object) {
|
||||
int last_property_offset =
|
||||
JSRegExp::kSize + kPointerSize * map->inobject_properties();
|
||||
VisitPointers(map->heap(),
|
||||
SLOT_ADDR(object, JSRegExp::kPropertiesOffset),
|
||||
SLOT_ADDR(object, last_property_offset));
|
||||
}
|
||||
|
||||
|
||||
static void VisitSharedFunctionInfoFields(Heap* heap,
|
||||
HeapObject* object,
|
||||
|
@ -466,14 +466,22 @@ void JSRegExp::JSRegExpVerify() {
|
||||
|
||||
FixedArray* arr = FixedArray::cast(data());
|
||||
Object* ascii_data = arr->get(JSRegExp::kIrregexpASCIICodeIndex);
|
||||
// TheHole : Not compiled yet.
|
||||
// Smi : Not compiled yet (-1) or code prepared for flushing.
|
||||
// JSObject: Compilation error.
|
||||
// Code/ByteArray: Compiled code.
|
||||
ASSERT(ascii_data->IsTheHole() || ascii_data->IsJSObject() ||
|
||||
(is_native ? ascii_data->IsCode() : ascii_data->IsByteArray()));
|
||||
ASSERT(ascii_data->IsSmi() ||
|
||||
(is_native ? ascii_data->IsCode() : ascii_data->IsByteArray()));
|
||||
Object* uc16_data = arr->get(JSRegExp::kIrregexpUC16CodeIndex);
|
||||
ASSERT(uc16_data->IsTheHole() || uc16_data->IsJSObject() ||
|
||||
(is_native ? uc16_data->IsCode() : uc16_data->IsByteArray()));
|
||||
ASSERT(uc16_data->IsSmi() ||
|
||||
(is_native ? uc16_data->IsCode() : uc16_data->IsByteArray()));
|
||||
|
||||
Object* ascii_saved = arr->get(JSRegExp::kIrregexpASCIICodeSavedIndex);
|
||||
ASSERT(ascii_saved->IsSmi() || ascii_saved->IsString() ||
|
||||
ascii_saved->IsCode());
|
||||
Object* uc16_saved = arr->get(JSRegExp::kIrregexpUC16CodeSavedIndex);
|
||||
ASSERT(uc16_saved->IsSmi() || uc16_saved->IsString() ||
|
||||
uc16_saved->IsCode());
|
||||
|
||||
ASSERT(arr->get(JSRegExp::kIrregexpCaptureCountIndex)->IsSmi());
|
||||
ASSERT(arr->get(JSRegExp::kIrregexpMaxRegisterCountIndex)->IsSmi());
|
||||
break;
|
||||
|
@ -3850,6 +3850,12 @@ JSRegExp::Type JSRegExp::TypeTag() {
|
||||
}
|
||||
|
||||
|
||||
JSRegExp::Type JSRegExp::TypeTagUnchecked() {
|
||||
Smi* smi = Smi::cast(DataAtUnchecked(kTagIndex));
|
||||
return static_cast<JSRegExp::Type>(smi->value());
|
||||
}
|
||||
|
||||
|
||||
int JSRegExp::CaptureCount() {
|
||||
switch (TypeTag()) {
|
||||
case ATOM:
|
||||
@ -3885,6 +3891,13 @@ Object* JSRegExp::DataAt(int index) {
|
||||
}
|
||||
|
||||
|
||||
Object* JSRegExp::DataAtUnchecked(int index) {
|
||||
FixedArray* fa = reinterpret_cast<FixedArray*>(data());
|
||||
int offset = FixedArray::kHeaderSize + index * kPointerSize;
|
||||
return READ_FIELD(fa, offset);
|
||||
}
|
||||
|
||||
|
||||
void JSRegExp::SetDataAt(int index, Object* value) {
|
||||
ASSERT(TypeTag() != NOT_COMPILED);
|
||||
ASSERT(index >= kDataIndex); // Only implementation data can be set this way.
|
||||
@ -3892,6 +3905,17 @@ void JSRegExp::SetDataAt(int index, Object* value) {
|
||||
}
|
||||
|
||||
|
||||
void JSRegExp::SetDataAtUnchecked(int index, Object* value, Heap* heap) {
|
||||
ASSERT(index >= kDataIndex); // Only implementation data can be set this way.
|
||||
FixedArray* fa = reinterpret_cast<FixedArray*>(data());
|
||||
if (value->IsSmi()) {
|
||||
fa->set_unchecked(index, Smi::cast(value));
|
||||
} else {
|
||||
fa->set_unchecked(heap, index, value, SKIP_WRITE_BARRIER);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
JSObject::ElementsKind JSObject::GetElementsKind() {
|
||||
ElementsKind kind = map()->elements_kind();
|
||||
ASSERT((kind == FAST_ELEMENTS &&
|
||||
|
@ -88,6 +88,9 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
|
||||
case JS_GLOBAL_PROPERTY_CELL_TYPE:
|
||||
return kVisitPropertyCell;
|
||||
|
||||
case JS_REGEXP_TYPE:
|
||||
return kVisitJSRegExp;
|
||||
|
||||
case SHARED_FUNCTION_INFO_TYPE:
|
||||
return kVisitSharedFunctionInfo;
|
||||
|
||||
@ -108,7 +111,6 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
|
||||
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
|
||||
case JS_VALUE_TYPE:
|
||||
case JS_ARRAY_TYPE:
|
||||
case JS_REGEXP_TYPE:
|
||||
case JS_GLOBAL_PROXY_TYPE:
|
||||
case JS_GLOBAL_OBJECT_TYPE:
|
||||
case JS_BUILTINS_OBJECT_TYPE:
|
||||
|
@ -105,6 +105,7 @@ class StaticVisitorBase : public AllStatic {
|
||||
kVisitPropertyCell,
|
||||
kVisitSharedFunctionInfo,
|
||||
kVisitJSFunction,
|
||||
kVisitJSRegExp,
|
||||
|
||||
kVisitorIdCount,
|
||||
kMinObjectSizeInWords = 2
|
||||
@ -300,6 +301,8 @@ class StaticNewSpaceVisitor : public StaticVisitorBase {
|
||||
SharedFunctionInfo::BodyDescriptor,
|
||||
int>::Visit);
|
||||
|
||||
table_.Register(kVisitJSRegExp, &VisitJSRegExp);
|
||||
|
||||
table_.Register(kVisitSeqAsciiString, &VisitSeqAsciiString);
|
||||
|
||||
table_.Register(kVisitSeqTwoByteString, &VisitSeqTwoByteString);
|
||||
@ -342,6 +345,10 @@ class StaticNewSpaceVisitor : public StaticVisitorBase {
|
||||
SeqAsciiStringSize(map->instance_type());
|
||||
}
|
||||
|
||||
static inline int VisitJSRegExp(Map* map, HeapObject* object) {
|
||||
return JSObjectVisitor::Visit(map, object);
|
||||
}
|
||||
|
||||
static inline int VisitSeqTwoByteString(Map* map, HeapObject* object) {
|
||||
return SeqTwoByteString::cast(object)->
|
||||
SeqTwoByteStringSize(map->instance_type());
|
||||
|
@ -5292,8 +5292,10 @@ class JSMessageObject: public JSObject {
|
||||
// If it is an atom regexp
|
||||
// - a reference to a literal string to search for
|
||||
// If it is an irregexp regexp:
|
||||
// - a reference to code for ASCII inputs (bytecode or compiled).
|
||||
// - a reference to code for UC16 inputs (bytecode or compiled).
|
||||
// - a reference to code for ASCII inputs (bytecode or compiled), or a smi
|
||||
// used for tracking the last usage (used for code flushing).
|
||||
// - a reference to code for UC16 inputs (bytecode or compiled), or a smi
|
||||
// used for tracking the last usage (used for code flushing)..
|
||||
// - max number of registers used by irregexp implementations.
|
||||
// - number of capture registers (output values) of the regexp.
|
||||
class JSRegExp: public JSObject {
|
||||
@ -5326,6 +5328,12 @@ class JSRegExp: public JSObject {
|
||||
inline Object* DataAt(int index);
|
||||
// Set implementation data after the object has been prepared.
|
||||
inline void SetDataAt(int index, Object* value);
|
||||
|
||||
// Used during GC when flushing code or setting age.
|
||||
inline Object* DataAtUnchecked(int index);
|
||||
inline void SetDataAtUnchecked(int index, Object* value, Heap* heap);
|
||||
inline Type TypeTagUnchecked();
|
||||
|
||||
static int code_index(bool is_ascii) {
|
||||
if (is_ascii) {
|
||||
return kIrregexpASCIICodeIndex;
|
||||
@ -5334,6 +5342,14 @@ class JSRegExp: public JSObject {
|
||||
}
|
||||
}
|
||||
|
||||
static int saved_code_index(bool is_ascii) {
|
||||
if (is_ascii) {
|
||||
return kIrregexpASCIICodeSavedIndex;
|
||||
} else {
|
||||
return kIrregexpUC16CodeSavedIndex;
|
||||
}
|
||||
}
|
||||
|
||||
static inline JSRegExp* cast(Object* obj);
|
||||
|
||||
// Dispatched behavior.
|
||||
@ -5364,11 +5380,19 @@ class JSRegExp: public JSObject {
|
||||
// fails, this fields hold an exception object that should be
|
||||
// thrown if the regexp is used again.
|
||||
static const int kIrregexpUC16CodeIndex = kDataIndex + 1;
|
||||
|
||||
// Saved instance of Irregexp compiled code or bytecode for ASCII that
|
||||
// is a potential candidate for flushing.
|
||||
static const int kIrregexpASCIICodeSavedIndex = kDataIndex + 2;
|
||||
// Saved instance of Irregexp compiled code or bytecode for UC16 that is
|
||||
// a potential candidate for flushing.
|
||||
static const int kIrregexpUC16CodeSavedIndex = kDataIndex + 3;
|
||||
|
||||
// Maximal number of registers used by either ASCII or UC16.
|
||||
// Only used to check that there is enough stack space
|
||||
static const int kIrregexpMaxRegisterCountIndex = kDataIndex + 2;
|
||||
static const int kIrregexpMaxRegisterCountIndex = kDataIndex + 4;
|
||||
// Number of captures in the compiled regexp.
|
||||
static const int kIrregexpCaptureCountIndex = kDataIndex + 3;
|
||||
static const int kIrregexpCaptureCountIndex = kDataIndex + 5;
|
||||
|
||||
static const int kIrregexpDataSize = kIrregexpCaptureCountIndex + 1;
|
||||
|
||||
@ -5389,6 +5413,18 @@ class JSRegExp: public JSObject {
|
||||
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;
|
||||
|
||||
// The compilation error value for the regexp code object. The real error
|
||||
// object is in the saved code field.
|
||||
static const int kCompilationErrorValue = -2;
|
||||
|
||||
// When we store the sweep generation at which we moved the code from the
|
||||
// code index to the saved code index we mask it of to be in the [0:255]
|
||||
// range.
|
||||
static const int kCodeAgeMask = 0xff;
|
||||
};
|
||||
|
||||
|
||||
|
@ -235,7 +235,7 @@ function RegExpTest(string) {
|
||||
// Conversion is required by the ES5 specification (RegExp.prototype.exec
|
||||
// algorithm, step 5) even if the value is discarded for non-global RegExps.
|
||||
var i = TO_INTEGER(lastIndex);
|
||||
|
||||
|
||||
if (this.global) {
|
||||
if (i < 0 || i > string.length) {
|
||||
this.lastIndex = 0;
|
||||
@ -250,11 +250,11 @@ function RegExpTest(string) {
|
||||
}
|
||||
lastMatchInfoOverride = null;
|
||||
this.lastIndex = lastMatchInfo[CAPTURE1];
|
||||
return true;
|
||||
return true;
|
||||
} else {
|
||||
// Non-global regexp.
|
||||
// Remove irrelevant preceeding '.*' in a non-global test regexp.
|
||||
// The expression checks whether this.source starts with '.*' and
|
||||
// Remove irrelevant preceeding '.*' in a non-global test regexp.
|
||||
// The expression checks whether this.source starts with '.*' and
|
||||
// that the third char is not a '?'.
|
||||
if (%_StringCharCodeAt(this.source, 0) == 46 && // '.'
|
||||
%_StringCharCodeAt(this.source, 1) == 42 && // '*'
|
||||
@ -262,14 +262,14 @@ function RegExpTest(string) {
|
||||
if (!%_ObjectEquals(regexp_key, this)) {
|
||||
regexp_key = this;
|
||||
regexp_val = new $RegExp(SubString(this.source, 2, this.source.length),
|
||||
(!this.ignoreCase
|
||||
(!this.ignoreCase
|
||||
? !this.multiline ? "" : "m"
|
||||
: !this.multiline ? "i" : "im"));
|
||||
}
|
||||
if (%_RegExpExec(regexp_val, string, 0, lastMatchInfo) === null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
%_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, string, lastIndex]);
|
||||
// matchIndices is either null or the lastMatchInfo array.
|
||||
var matchIndices = %_RegExpExec(this, string, 0, lastMatchInfo);
|
||||
|
@ -276,7 +276,7 @@ function StringReplace(search, replace) {
|
||||
// the result.
|
||||
function ExpandReplacement(string, subject, matchInfo, builder) {
|
||||
var length = string.length;
|
||||
var builder_elements = builder.elements;
|
||||
var builder_elements = builder.elements;
|
||||
var next = %StringIndexOf(string, '$', 0);
|
||||
if (next < 0) {
|
||||
if (length > 0) builder_elements.push(string);
|
||||
|
@ -2449,9 +2449,8 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
|
||||
__ bind(&check_code);
|
||||
// Check that the irregexp code has been generated for the actual string
|
||||
// encoding. If it has, the field contains a code object otherwise it contains
|
||||
// the hole.
|
||||
__ CmpObjectType(r11, CODE_TYPE, kScratchRegister);
|
||||
__ j(not_equal, &runtime);
|
||||
// smi (code flushing support)
|
||||
__ JumpIfSmi(r11, &runtime);
|
||||
|
||||
// rdi: subject string
|
||||
// rcx: encoding of subject string (1 if ascii, 0 if two_byte);
|
||||
|
Loading…
Reference in New Issue
Block a user