// 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
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));
// 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),
if (collector == MARK_COMPACTOR) {
// Perform mark-sweep with optional compaction.
MarkCompact(tracer);
-
+ sweep_generation_++;
bool high_survival_rate_during_scavenges = IsHighSurvivalRate() &&
IsStableOrIncreasingSurvivalTrend();
&ObjectEvacuationStrategy<POINTER_OBJECT>::
template VisitSpecialized<SharedFunctionInfo::kSize>);
+ table_.Register(kVisitJSRegExp,
+ &ObjectEvacuationStrategy<POINTER_OBJECT>::
+ Visit);
+
table_.Register(kVisitJSFunction,
&ObjectEvacuationStrategy<POINTER_OBJECT>::
template VisitSpecialized<JSFunction::kSize>);
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_; }
// 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_;
__ 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
#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();
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;
}
table_.Register(kVisitJSFunction,
&VisitJSFunctionAndFlushCode);
+ table_.Register(kVisitJSRegExp,
+ &VisitRegExpAndFlushCode);
+
table_.Register(kVisitPropertyCell,
&FixedBodyVisitor<StaticMarkingVisitor,
JSGlobalPropertyCell::BodyDescriptor,
// 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) &&
}
+ 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();
// 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,
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;
}
+JSRegExp::Type JSRegExp::TypeTagUnchecked() {
+ Smi* smi = Smi::cast(DataAtUnchecked(kTagIndex));
+ return static_cast<JSRegExp::Type>(smi->value());
+}
+
+
int JSRegExp::CaptureCount() {
switch (TypeTag()) {
case ATOM:
}
+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.
}
+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 &&
case JS_GLOBAL_PROPERTY_CELL_TYPE:
return kVisitPropertyCell;
+ case JS_REGEXP_TYPE:
+ return kVisitJSRegExp;
+
case SHARED_FUNCTION_INFO_TYPE:
return kVisitSharedFunctionInfo;
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:
kVisitPropertyCell,
kVisitSharedFunctionInfo,
kVisitJSFunction,
+ kVisitJSRegExp,
kVisitorIdCount,
kMinObjectSizeInWords = 2
SharedFunctionInfo::BodyDescriptor,
int>::Visit);
+ table_.Register(kVisitJSRegExp, &VisitJSRegExp);
+
table_.Register(kVisitSeqAsciiString, &VisitSeqAsciiString);
table_.Register(kVisitSeqTwoByteString, &VisitSeqTwoByteString);
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());
// 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 {
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;
}
}
+ static int saved_code_index(bool is_ascii) {
+ if (is_ascii) {
+ return kIrregexpASCIICodeSavedIndex;
+ } else {
+ return kIrregexpUC16CodeSavedIndex;
+ }
+ }
+
static inline JSRegExp* cast(Object* obj);
// Dispatched behavior.
// 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;
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;
};
// 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;
}
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 && // '*'
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);
// 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);
__ 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);