[ 'v8_use_arm_eabi_hardfloat=="true"', {
'defines': [
'USE_EABI_HARDFLOAT=1',
- 'CAN_USE_VFP2_INSTRUCTIONS',
+ 'CAN_USE_VFP3_INSTRUCTIONS',
],
'target_conditions': [
['_toolset=="target"', {
}],
['OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="netbsd" \
or OS=="android"', {
+ 'cflags!': [
+ '-O2',
+ '-Os',
+ ],
+ 'cflags': [
+ '-fdata-sections',
+ '-ffunction-sections',
+ '-O3',
+ ],
'conditions': [
[ 'gcc_version==44 and clang==0', {
'cflags': [
Handle<ScopeInfo> scope_info =
ScopeInfo::Create(info->scope(), info->zone());
shared->set_scope_info(*scope_info);
- shared->set_code(*code);
+ shared->ReplaceCode(*code);
if (!function.is_null()) {
function->ReplaceCode(*code);
ASSERT(!function->IsOptimized());
HValue* const32_minus_sa) {
if (!const32_minus_sa->IsSub()) return false;
HSub* sub = HSub::cast(const32_minus_sa);
+ if (sa != sub->right()) return false;
HValue* const32 = sub->left();
if (!const32->IsConstant() ||
HConstant::cast(const32)->Integer32Value() != 32) {
} else {
return false;
}
+ if (shl->left() != shr->left()) return false;
if (!ShiftAmountsAllowReplaceByRotate(shl->right(), shr->right()) &&
!ShiftAmountsAllowReplaceByRotate(shr->right(), shl->right())) {
// one character so we can escape the loop properly.
p--;
break;
- case 'p':
- stream.Add("%d", OS::GetCurrentProcessId());
- break;
case 't': {
// %t expands to the current time in milliseconds.
double time = OS::TimeCurrentMillis();
if (!code_mark.Get()) {
shared->set_code(lazy_compile);
candidate->set_code(lazy_compile);
- } else if (code == lazy_compile) {
- candidate->set_code(lazy_compile);
+ } else {
+ candidate->set_code(code);
}
// We are in the middle of a GC cycle so the write barrier in the code
}
+void CodeFlusher::EvictCandidate(SharedFunctionInfo* shared_info) {
+ // Make sure previous flushing decisions are revisited.
+ isolate_->heap()->incremental_marking()->RecordWrites(shared_info);
+
+ SharedFunctionInfo* candidate = shared_function_info_candidates_head_;
+ SharedFunctionInfo* next_candidate;
+ if (candidate == shared_info) {
+ next_candidate = GetNextCandidate(shared_info);
+ shared_function_info_candidates_head_ = next_candidate;
+ ClearNextCandidate(shared_info);
+ } else {
+ while (candidate != NULL) {
+ next_candidate = GetNextCandidate(candidate);
+
+ if (next_candidate == shared_info) {
+ next_candidate = GetNextCandidate(shared_info);
+ SetNextCandidate(candidate, next_candidate);
+ ClearNextCandidate(shared_info);
+ break;
+ }
+
+ candidate = next_candidate;
+ }
+ }
+}
+
+
void CodeFlusher::EvictCandidate(JSFunction* function) {
ASSERT(!function->next_function_link()->IsUndefined());
Object* undefined = isolate_->heap()->undefined_value();
- // The function is no longer a candidate, make sure it gets visited
- // again so that previous flushing decisions are revisited.
+ // Make sure previous flushing decisions are revisited.
isolate_->heap()->incremental_marking()->RecordWrites(function);
+ isolate_->heap()->incremental_marking()->RecordWrites(function->shared());
JSFunction* candidate = jsfunction_candidates_head_;
JSFunction* next_candidate;
next_candidate = GetNextCandidate(function);
SetNextCandidate(candidate, next_candidate);
ClearNextCandidate(function, undefined);
+ break;
}
candidate = next_candidate;
}
}
+ void EvictCandidate(SharedFunctionInfo* shared_info);
void EvictCandidate(JSFunction* function);
void ProcessCandidates() {
// with a data property as soon as the stack trace has been formatted.
var getter = function() {
var value = FormatRawStackTrace(obj, raw_stack);
+ raw_stack = void 0;
%DefineOrRedefineDataProperty(obj, 'stack', value, NONE);
return value;
};
__ Check(eq, "Non-smi value", at, Operand(zero_reg));
__ lw(at, FieldMemOperand(string, String::kLengthOffset));
- __ Check(lt, "Index is too large", at, Operand(index));
+ __ Check(lt, "Index is too large", index, Operand(at));
- __ Check(ge, "Index is negative", index, Operand(Smi::FromInt(0)));
+ __ Check(ge, "Index is negative", index, Operand(zero_reg));
__ lw(at, FieldMemOperand(string, HeapObject::kMapOffset));
__ lbu(at, FieldMemOperand(at, Map::kInstanceTypeOffset));
__ And(at, at, Operand(kStringRepresentationMask | kStringEncodingMask));
static const uint32_t one_byte_seq_type = kSeqStringTag | kOneByteStringTag;
static const uint32_t two_byte_seq_type = kSeqStringTag | kTwoByteStringTag;
- __ Check(eq, "Unexpected string type", at,
- Operand(encoding == String::ONE_BYTE_ENCODING
- ? one_byte_seq_type : two_byte_seq_type));
+ __ Subu(at, at, Operand(encoding == String::ONE_BYTE_ENCODING
+ ? one_byte_seq_type : two_byte_seq_type));
+ __ Check(eq, "Unexpected string type", at, Operand(zero_reg));
}
__ Addu(at,
__ Branch(¬_applicable, ne, scratch, Operand(from_map));
__ li(new_map_reg, Operand(to_map));
- if (IsFastSmiElementsKind(from_kind) && IsFastObjectElementsKind(to_kind)) {
+ if (IsSimpleMapChangeTransition(from_kind, to_kind)) {
__ sw(new_map_reg, FieldMemOperand(object_reg, HeapObject::kMapOffset));
// Write barrier.
__ RecordWriteField(object_reg, HeapObject::kMapOffset, new_map_reg,
}
+void SharedFunctionInfo::ReplaceCode(Code* value) {
+ // If the GC metadata field is already used then the function was
+ // enqueued as a code flushing candidate and we remove it now.
+ if (code()->gc_metadata() != NULL) {
+ CodeFlusher* flusher = GetHeap()->mark_compact_collector()->code_flusher();
+ flusher->EvictCandidate(this);
+ }
+
+ ASSERT(code()->gc_metadata() == NULL && value->gc_metadata() == NULL);
+ set_code(value);
+}
+
+
ScopeInfo* SharedFunctionInfo::scope_info() {
return reinterpret_cast<ScopeInfo*>(READ_FIELD(this, kScopeInfoOffset));
}
}
-void JSRegExp::ResetLastIndex() {
- InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex,
- Smi::FromInt(0),
- SKIP_WRITE_BARRIER); // It's a Smi.
-}
-
-
ElementsKind JSObject::GetElementsKind() {
ElementsKind kind = map()->elements_kind();
#if DEBUG
// old code, we have to replace it. We should try to avoid this
// altogether because it flushes valuable type feedback by
// effectively resetting all IC state.
- set_code(recompiled);
+ ReplaceCode(recompiled);
}
ASSERT(has_deoptimization_support());
}
// [code]: Function code.
DECL_ACCESSORS(code, Code)
+ inline void ReplaceCode(Code* code);
// [optimized_code_map]: Map from native context to optimized code
// and a shared literals array or Smi 0 if none.
inline Object* DataAtUnchecked(int index);
inline void SetDataAtUnchecked(int index, Object* value, Heap* heap);
inline Type TypeTagUnchecked();
- inline void ResetLastIndex();
static int code_index(bool is_ascii) {
if (is_ascii) {
raw_addr &= V8_UINT64_C(0x3ffffffff000);
#else
uint32_t raw_addr = V8::RandomPrivate(isolate);
-
- // For our 32-bit mmap() hint, we pick a random address in the bottom
- // half of the top half of the address space (that is, the third quarter).
- // Because we do not MAP_FIXED, this will be treated only as a hint -- the
- // system will not fail to mmap() because something else happens to already
- // be mapped at our random address. We deliberately set the hint high enough
- // to get well above the system's break (that is, the heap); systems will
- // either try the hint and if that fails move higher (MacOS and other BSD
- // derivatives) or try the hint and if that fails allocate as if there were
- // no hint at all (Linux, Solaris, illumos and derivatives). The high hint
- // prevents the break from getting hemmed in at low values, ceding half of
- // the address space to the system heap.
+ // The range 0x20000000 - 0x60000000 is relatively unpopulated across a
+ // variety of ASLR modes (PAE kernel, NX compat mode, etc) and on macos
+ // 10.6 and 10.7.
raw_addr &= 0x3ffff000;
- raw_addr += 0x80000000;
+ raw_addr += 0x20000000;
#endif
return reinterpret_cast<void*>(raw_addr);
}
PropertyAttributes attrs = obj->GetLocalPropertyAttribute(*name);
if (attrs == ABSENT) return heap->undefined_value();
- AccessorPair* accessors = obj->GetLocalPropertyAccessorPair(*name);
+ AccessorPair* raw_accessors = obj->GetLocalPropertyAccessorPair(*name);
+ Handle<AccessorPair> accessors(raw_accessors, isolate);
Handle<FixedArray> elms = isolate->factory()->NewFixedArray(DESCRIPTOR_SIZE);
elms->set(ENUMERABLE_INDEX, heap->ToBoolean((attrs & DONT_ENUM) == 0));
elms->set(CONFIGURABLE_INDEX, heap->ToBoolean((attrs & DONT_DELETE) == 0));
- elms->set(IS_ACCESSOR_INDEX, heap->ToBoolean(accessors != NULL));
+ elms->set(IS_ACCESSOR_INDEX, heap->ToBoolean(raw_accessors != NULL));
- if (accessors == NULL) {
+ if (raw_accessors == NULL) {
elms->set(WRITABLE_INDEX, heap->ToBoolean((attrs & READ_ONLY) == 0));
// GetProperty does access check.
Handle<Object> value = GetProperty(obj, name);
JSRegExp::kIgnoreCaseFieldIndex, ignoreCase, SKIP_WRITE_BARRIER);
regexp->InObjectPropertyAtPut(
JSRegExp::kMultilineFieldIndex, multiline, SKIP_WRITE_BARRIER);
- regexp->ResetLastIndex();
+ regexp->InObjectPropertyAtPut(
+ JSRegExp::kLastIndexFieldIndex, Smi::FromInt(0), SKIP_WRITE_BARRIER);
return regexp;
}
// target function to undefined. SetCode is only used for built-in
// constructors like String, Array, and Object, and some web code
// doesn't like seeing source code for constructors.
- target_shared->set_code(source_shared->code());
+ target_shared->ReplaceCode(source_shared->code());
target_shared->set_scope_info(source_shared->scope_info());
target_shared->set_length(source_shared->length());
target_shared->set_formal_parameter_count(
int matches = indices.length();
if (matches == 0) {
- pattern_regexp->ResetLastIndex();
- return *subject;
+ return isolate->heap()->undefined_value();
}
// Detect integer overflow.
int32_t* current_match = global_cache.FetchNext();
if (current_match == NULL) {
if (global_cache.HasException()) return Failure::Exception();
- regexp->ResetLastIndex();
- return *subject;
+ return isolate->heap()->undefined_value();
}
// Guessing the number of parts that the final result string is built
int32_t* current_match = global_cache.FetchNext();
if (current_match == NULL) {
if (global_cache.HasException()) return Failure::Exception();
- regexp->ResetLastIndex();
- return *subject;
+ return isolate->heap()->undefined_value();
}
int start = current_match[0];
// lastMatchInfo is defined in regexp.js.
var result = %StringMatch(subject, regexp, lastMatchInfo);
if (result !== null) lastMatchInfoOverride = null;
+ regexp.lastIndex = 0;
return result;
}
// Non-regexp argument.
}
} else {
if (lastMatchInfoOverride == null) {
- return %StringReplaceRegExpWithString(subject,
- search,
- TO_STRING_INLINE(replace),
- lastMatchInfo);
+ var answer = %StringReplaceRegExpWithString(subject,
+ search,
+ TO_STRING_INLINE(replace),
+ lastMatchInfo);
+ if (IS_UNDEFINED(answer)) { // No match. Return subject string.
+ search.lastIndex = 0;
+ return subject;
+ }
+ if (search.global) search.lastIndex = 0;
+ return answer;
} else {
// We use this hack to detect whether StringReplaceRegExpWithString
// found at least one hit. In that case we need to remove any
search,
TO_STRING_INLINE(replace),
lastMatchInfo);
+ if (IS_UNDEFINED(answer)) { // No match. Return subject string.
+ search.lastIndex = 0;
+ lastMatchInfo[LAST_SUBJECT_INDEX] = saved_subject;
+ return subject;
+ }
if (%_IsSmi(lastMatchInfo[LAST_SUBJECT_INDEX])) {
lastMatchInfo[LAST_SUBJECT_INDEX] = saved_subject;
} else {
lastMatchInfoOverride = null;
}
+ if (search.global) search.lastIndex = 0;
return answer;
}
}
template <typename sourcechar, typename sinkchar>
void CopyChars(sinkchar* dest, const sourcechar* src, int chars) {
- ASSERT(chars >= 0);
- if (chars == 0) return;
sinkchar* limit = dest + chars;
#ifdef V8_HOST_CAN_READ_UNALIGNED
if (sizeof(*dest) == sizeof(*src)) {
#define MAJOR_VERSION 3
#define MINOR_VERSION 15
#define BUILD_NUMBER 11
-#define PATCH_LEVEL 10
+#define PATCH_LEVEL 15
// Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.)
#define IS_CANDIDATE_VERSION 0
// Unoptimized code is missing and the deoptimizer will go ballistic.
CompileRun("g('bozo');");
}
+
+
+TEST(Regress169209) {
+ i::FLAG_allow_natives_syntax = true;
+ i::FLAG_flush_code_incrementally = true;
+ InitializeVM();
+ v8::HandleScope scope;
+
+ // Perform one initial GC to enable code flushing.
+ HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
+
+ // Prepare a shared function info eligible for code flushing for which
+ // the unoptimized code will be replaced during optimization.
+ Handle<SharedFunctionInfo> shared1;
+ {
+ HandleScope inner_scope;
+ CompileRun("function f() { return 'foobar'; }"
+ "function g(x) { if (x) f(); }"
+ "f();"
+ "g(false);"
+ "g(false);");
+
+ Handle<JSFunction> f =
+ v8::Utils::OpenHandle(
+ *v8::Handle<v8::Function>::Cast(
+ v8::Context::GetCurrent()->Global()->Get(v8_str("f"))));
+ CHECK(f->is_compiled());
+ const int kAgingThreshold = 6;
+ for (int i = 0; i < kAgingThreshold; i++) {
+ f->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
+ }
+
+ shared1 = inner_scope.CloseAndEscape(handle(f->shared(), ISOLATE));
+ }
+
+ // Prepare a shared function info eligible for code flushing that will
+ // represent the dangling tail of the candidate list.
+ Handle<SharedFunctionInfo> shared2;
+ {
+ HandleScope inner_scope;
+ CompileRun("function flushMe() { return 0; }"
+ "flushMe(1);");
+
+ Handle<JSFunction> f =
+ v8::Utils::OpenHandle(
+ *v8::Handle<v8::Function>::Cast(
+ v8::Context::GetCurrent()->Global()->Get(v8_str("flushMe"))));
+ CHECK(f->is_compiled());
+ const int kAgingThreshold = 6;
+ for (int i = 0; i < kAgingThreshold; i++) {
+ f->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
+ }
+
+ shared2 = inner_scope.CloseAndEscape(handle(f->shared(), ISOLATE));
+ }
+
+ // Simulate incremental marking and collect code flushing candidates.
+ SimulateIncrementalMarking();
+ CHECK(shared1->code()->gc_metadata() != NULL);
+
+ // Optimize function and make sure the unoptimized code is replaced.
+#ifdef DEBUG
+ FLAG_stop_at = "f";
+#endif
+ CompileRun("%OptimizeFunctionOnNextCall(g);"
+ "g(false);");
+
+ // Finish garbage collection cycle.
+ HEAP->CollectAllGarbage(Heap::kNoGCFlags);
+ CHECK(shared1->code()->gc_metadata() == NULL);
+}
+
+
+TEST(Regress168801) {
+ i::FLAG_always_compact = true;
+ i::FLAG_cache_optimized_code = false;
+ i::FLAG_allow_natives_syntax = true;
+ i::FLAG_flush_code_incrementally = true;
+ InitializeVM();
+ v8::HandleScope scope;
+
+ // Perform one initial GC to enable code flushing.
+ HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
+
+ // Ensure the code ends up on an evacuation candidate.
+ SimulateFullSpace(HEAP->code_space());
+
+ // Prepare an unoptimized function that is eligible for code flushing.
+ Handle<JSFunction> function;
+ {
+ HandleScope inner_scope;
+ CompileRun("function mkClosure() {"
+ " return function(x) { return x + 1; };"
+ "}"
+ "var f = mkClosure();"
+ "f(1); f(2);");
+
+ Handle<JSFunction> f =
+ v8::Utils::OpenHandle(
+ *v8::Handle<v8::Function>::Cast(
+ v8::Context::GetCurrent()->Global()->Get(v8_str("f"))));
+ CHECK(f->is_compiled());
+ const int kAgingThreshold = 6;
+ for (int i = 0; i < kAgingThreshold; i++) {
+ f->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
+ }
+
+ function = inner_scope.CloseAndEscape(handle(*f, ISOLATE));
+ }
+
+ // Simulate incremental marking so that unoptimized function is enqueued as a
+ // candidate for code flushing. The shared function info however will not be
+ // explicitly enqueued.
+ SimulateIncrementalMarking();
+
+ // Now optimize the function so that it is taken off the candidate list.
+ {
+ HandleScope inner_scope;
+ CompileRun("%OptimizeFunctionOnNextCall(f); f(3);");
+ }
+
+ // This cycle will bust the heap and subsequent cycles will go ballistic.
+ HEAP->CollectAllGarbage(Heap::kNoGCFlags);
+ HEAP->CollectAllGarbage(Heap::kNoGCFlags);
+}
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Summary of the spec: lastIndex is reset to 0 if
+// - a regexp fails to match, regardless of global or non-global.
+// - a global regexp is used in a function that returns multiple results,
+// such as String.prototype.replace or String.prototype.match, since it
+// repeats the regexp until it fails to match.
+// Otherwise lastIndex is only set when a global regexp matches, to the index
+// after the match.
+
// Test Regexp.prototype.exec
r = /a/;
r.lastIndex = 1;
"zzzz".replace(r, function() { return ""; });
assertEquals(0, r.lastIndex);
+// Regexp functions that returns multiple results:
+// A global regexp always resets lastIndex regardless of whether it matches.
+r = /a/g;
+r.lastIndex = -1;
+"0123abcd".replace(r, "x");
+assertEquals(0, r.lastIndex);
+
+r.lastIndex = -1;
+"01234567".replace(r, "x");
+assertEquals(0, r.lastIndex);
+
+r.lastIndex = -1;
+"0123abcd".match(r);
+assertEquals(0, r.lastIndex);
+
+r.lastIndex = -1;
+"01234567".match(r);
+assertEquals(0, r.lastIndex);
+
+// A non-global regexp resets lastIndex iff it does not match.
+r = /a/;
+r.lastIndex = -1;
+"0123abcd".replace(r, "x");
+assertEquals(-1, r.lastIndex);
+
+r.lastIndex = -1;
+"01234567".replace(r, "x");
+assertEquals(0, r.lastIndex);
+
+r.lastIndex = -1;
+"0123abcd".match(r);
+assertEquals(-1, r.lastIndex);
+
+r.lastIndex = -1;
+"01234567".match(r);
+assertEquals(0, r.lastIndex);
+
+// Also test RegExp.prototype.exec and RegExp.prototype.test
+r = /a/g;
+r.lastIndex = 1;
+r.exec("01234567");
+assertEquals(0, r.lastIndex);
+
+r.lastIndex = 1;
+r.exec("0123abcd");
+assertEquals(5, r.lastIndex);
+
+r = /a/;
+r.lastIndex = 1;
+r.exec("01234567");
+assertEquals(0, r.lastIndex);
+
+r.lastIndex = 1;
+r.exec("0123abcd");
+assertEquals(1, r.lastIndex);
+
+r = /a/g;
+r.lastIndex = 1;
+r.test("01234567");
+assertEquals(0, r.lastIndex);
+
+r.lastIndex = 1;
+r.test("0123abcd");
+assertEquals(5, r.lastIndex);
+
+r = /a/;
+r.lastIndex = 1;
+r.test("01234567");
+assertEquals(0, r.lastIndex);
+
+r.lastIndex = 1;
+r.test("0123abcd");
+assertEquals(1, r.lastIndex);
--- /dev/null
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --allow-natives-syntax
+
+function foo(word, nBits) {
+ return (word[1] >>> nBits) | (word[0] << (32 - nBits));
+}
+
+word = [0x1001, 0];
+
+var expected = foo(word, 1);
+foo(word, 1);
+%OptimizeFunctionOnNextCall(foo);
+var optimized = foo(word, 1);
+assertEquals(expected, optimized)
--- /dev/null
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+r = new RegExp("a");
+for (var i = 0; i < 100; i++) {
+ r["abc" + i] = i;
+}
+"zzzz".replace(r, "");
+assertEquals(0, r.lastIndex);