V8: Upgrade to 3.15.11.15
authorisaacs <i@izs.me>
Sat, 9 Feb 2013 01:17:45 +0000 (17:17 -0800)
committerisaacs <i@izs.me>
Sat, 9 Feb 2013 01:17:45 +0000 (17:17 -0800)
21 files changed:
deps/v8/build/common.gypi
deps/v8/src/compiler.cc
deps/v8/src/hydrogen.cc
deps/v8/src/log-utils.cc
deps/v8/src/mark-compact.cc
deps/v8/src/mark-compact.h
deps/v8/src/messages.js
deps/v8/src/mips/codegen-mips.cc
deps/v8/src/mips/lithium-codegen-mips.cc
deps/v8/src/objects-inl.h
deps/v8/src/objects.cc
deps/v8/src/objects.h
deps/v8/src/platform-posix.cc
deps/v8/src/runtime.cc
deps/v8/src/string.js
deps/v8/src/v8utils.h
deps/v8/src/version.cc
deps/v8/test/cctest/test-heap.cc
deps/v8/test/mjsunit/regress/regress-2437.js
deps/v8/test/mjsunit/regress/regress-2499.js [new file with mode: 0644]
deps/v8/test/mjsunit/regress/regress-crbug-170856.js [new file with mode: 0644]

index 44bebae..e68ee15 100644 (file)
           [ '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': [
index 5779aae..7e4eaa2 100644 (file)
@@ -697,7 +697,7 @@ static bool InstallFullCode(CompilationInfo* info) {
   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());
index bdca43e..aad9da3 100644 (file)
@@ -8306,6 +8306,7 @@ static bool ShiftAmountsAllowReplaceByRotate(HValue* sa,
                                              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) {
@@ -8334,6 +8335,7 @@ bool HGraphBuilder::MatchRotateRight(HValue* left,
   } else {
     return false;
   }
+  if (shl->left() != shr->left()) return false;
 
   if (!ShiftAmountsAllowReplaceByRotate(shl->right(), shr->right()) &&
       !ShiftAmountsAllowReplaceByRotate(shr->right(), shl->right())) {
index a66db3d..d8d92cb 100644 (file)
@@ -107,9 +107,6 @@ void Log::Initialize() {
                 // 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();
index 8ca14db..4e6599f 100644 (file)
@@ -885,8 +885,8 @@ void CodeFlusher::ProcessJSFunctionCandidates() {
     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
@@ -935,13 +935,40 @@ void CodeFlusher::ProcessSharedFunctionInfoCandidates() {
 }
 
 
+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;
@@ -957,6 +984,7 @@ void CodeFlusher::EvictCandidate(JSFunction* function) {
         next_candidate = GetNextCandidate(function);
         SetNextCandidate(candidate, next_candidate);
         ClearNextCandidate(function, undefined);
+        break;
       }
 
       candidate = next_candidate;
index 9a0b014..8821c3d 100644 (file)
@@ -434,6 +434,7 @@ class CodeFlusher {
     }
   }
 
+  void EvictCandidate(SharedFunctionInfo* shared_info);
   void EvictCandidate(JSFunction* function);
 
   void ProcessCandidates() {
index 0a50ae7..f0c51c6 100644 (file)
@@ -1090,6 +1090,7 @@ function captureStackTrace(obj, cons_opt) {
   // 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;
   };
index 1da8089..db313e1 100644 (file)
@@ -529,9 +529,9 @@ void SeqStringSetCharGenerator::Generate(MacroAssembler* masm,
     __ 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));
@@ -539,9 +539,9 @@ void SeqStringSetCharGenerator::Generate(MacroAssembler* masm,
     __ 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,
index fd7af9f..cc589e0 100644 (file)
@@ -4016,7 +4016,7 @@ void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) {
   __ Branch(&not_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,
index 67db5b4..1d9c87f 100644 (file)
@@ -4368,6 +4368,19 @@ void SharedFunctionInfo::set_code(Code* value, WriteBarrierMode mode) {
 }
 
 
+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));
 }
@@ -4955,13 +4968,6 @@ void JSRegExp::SetDataAtUnchecked(int index, Object* value, Heap* heap) {
 }
 
 
-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
index 885a5dd..126d1e0 100644 (file)
@@ -8562,7 +8562,7 @@ void SharedFunctionInfo::EnableDeoptimizationSupport(Code* recompiled) {
     // 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());
 }
index 6c9b31b..0be263a 100644 (file)
@@ -5429,6 +5429,7 @@ class SharedFunctionInfo: public HeapObject {
 
   // [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.
@@ -6648,7 +6649,6 @@ class JSRegExp: public JSObject {
   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) {
index e652a08..0016d59 100644 (file)
@@ -109,20 +109,11 @@ void* OS::GetRandomMmapAddr() {
     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);
   }
index 4464431..09ca047 100644 (file)
@@ -1098,14 +1098,15 @@ static MaybeObject* GetOwnProperty(Isolate* isolate,
 
   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);
@@ -1790,7 +1791,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpInitializeObject) {
         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;
   }
 
@@ -2145,7 +2147,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetCode) {
   // 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(
@@ -2903,8 +2905,7 @@ MUST_USE_RESULT static MaybeObject* StringReplaceAtomRegExpWithString(
 
   int matches = indices.length();
   if (matches == 0) {
-    pattern_regexp->ResetLastIndex();
-    return *subject;
+    return isolate->heap()->undefined_value();
   }
 
   // Detect integer overflow.
@@ -3005,8 +3006,7 @@ MUST_USE_RESULT static MaybeObject* StringReplaceRegExpWithString(
   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
@@ -3104,8 +3104,7 @@ MUST_USE_RESULT static MaybeObject* StringReplaceRegExpWithEmptyString(
   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];
index d9ca035..eb9aa35 100644 (file)
@@ -194,6 +194,7 @@ function StringMatch(regexp) {
     // 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.
@@ -244,10 +245,16 @@ function StringReplace(search, replace) {
       }
     } 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
@@ -258,11 +265,17 @@ function StringReplace(search, replace) {
                                                     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;
       }
     }
index 111abdf..9072b4e 100644 (file)
@@ -209,8 +209,6 @@ INLINE(void CopyChars(sinkchar* dest, const sourcechar* src, int chars));
 
 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)) {
index 7d31741..31d93d6 100644 (file)
@@ -35,7 +35,7 @@
 #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
index 93ac211..533a1c3 100644 (file)
@@ -2575,3 +2575,128 @@ TEST(Regress159140) {
   // 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);
+}
index 06b69b2..c82293a 100644 (file)
 // (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;
@@ -73,3 +81,76 @@ 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);
diff --git a/deps/v8/test/mjsunit/regress/regress-2499.js b/deps/v8/test/mjsunit/regress/regress-2499.js
new file mode 100644 (file)
index 0000000..52aad87
--- /dev/null
@@ -0,0 +1,40 @@
+// 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)
diff --git a/deps/v8/test/mjsunit/regress/regress-crbug-170856.js b/deps/v8/test/mjsunit/regress/regress-crbug-170856.js
new file mode 100644 (file)
index 0000000..2e73b12
--- /dev/null
@@ -0,0 +1,33 @@
+// 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);