Version 1.12.0-dev.5.9

Cherry-pick 1d537028f7cbe026fb682804fb13c72aa3261ae5 to dev
Cherry-pick 6e0b2418c7024406f603c2c775d320484355a449 to dev
Cherry-pick b569deac480aeced9fccbb802d47cfdcd8691894 to dev
Cherry-pick 664742f0162a13e0d60c65de0a713df8acd7d8e9 to dev
Cherry-pick f07cec063c4ac380119a628f25573db9a21d8634 to dev
Cherry-pick d0b03eebbd63261e7beb8413aa9ce9dfa2a862cc to dev
Cherry-pick 3aa62d03fcbffc2f2a77f05d2c8e975b75a56d76 to dev
Cherry-pick 74e02785b0e534e469668ce2cabced7d26ec55b1 to dev
Cherry-pick 028c9952959be3b1f275c3cfbe805ab80c49f07a to dev
diff --git a/pkg/compiler/lib/src/inferrer/simple_types_inferrer.dart b/pkg/compiler/lib/src/inferrer/simple_types_inferrer.dart
index e9ceabb..c54bebd 100644
--- a/pkg/compiler/lib/src/inferrer/simple_types_inferrer.dart
+++ b/pkg/compiler/lib/src/inferrer/simple_types_inferrer.dart
@@ -799,8 +799,6 @@
         elements.getGetterSelectorInComplexSendSet(node);
     TypeMask getterMask =
         elements.getGetterTypeMaskInComplexSendSet(node);
-    Selector operatorSelector =
-        elements.getOperatorSelectorInComplexSendSet(node);
     TypeMask operatorMask =
         elements.getOperatorTypeMaskInComplexSendSet(node);
     Selector setterSelector = elements.getSelector(node);
@@ -859,53 +857,53 @@
           node, element, setterSelector, setterMask, receiverType, rhsType,
           node.arguments.head);
     } else {
-      // [: foo++ :] or [: foo += 1 :].
-      ArgumentsTypes operatorArguments = new ArgumentsTypes<T>([rhsType], null);
+      // [foo ??= bar], [: foo++ :] or [: foo += 1 :].
       T getterType;
       T newType;
-      if (Elements.isErroneous(element)) {
-        getterType = types.dynamicType;
-        newType = types.dynamicType;
-      } else if (Elements.isStaticOrTopLevelField(element)) {
+
+      if (Elements.isErroneous(element)) return types.dynamicType;
+
+      if (Elements.isStaticOrTopLevelField(element)) {
         Element getterElement = elements[node.selector];
         getterType = handleStaticSend(
             node, getterSelector, getterMask, getterElement, null);
+      } else if (Elements.isUnresolved(element)
+                 || element.isSetter
+                 || element.isField) {
+        getterType = handleDynamicSend(
+            node, getterSelector, getterMask, receiverType, null);
+      } else if (element.isLocal) {
+        LocalElement local = element;
+        getterType = locals.use(local);
+      } else {
+        // Bogus SendSet, for example [: myMethod += 42 :].
+        getterType = types.dynamicType;
+      }
+
+      if (op == '??=') {
+        newType = types.allocateDiamondPhi(getterType, rhsType);
+      } else {
+        Selector operatorSelector =
+          elements.getOperatorSelectorInComplexSendSet(node);
         newType = handleDynamicSend(
             node, operatorSelector, operatorMask,
-            getterType, operatorArguments);
+            getterType, new ArgumentsTypes<T>([rhsType], null));
+      }
+
+      if (Elements.isStaticOrTopLevelField(element)) {
         handleStaticSend(
             node, setterSelector, setterMask, element,
             new ArgumentsTypes<T>([newType], null));
       } else if (Elements.isUnresolved(element)
                  || element.isSetter
                  || element.isField) {
-        getterType = handleDynamicSend(
-            node, getterSelector, getterMask, receiverType, null);
-        newType = handleDynamicSend(
-            node, operatorSelector, operatorMask,
-            getterType, operatorArguments);
         handleDynamicSend(node, setterSelector, setterMask, receiverType,
                           new ArgumentsTypes<T>([newType], null));
       } else if (element.isLocal) {
-        LocalElement local = element;
-        getterType = locals.use(local);
-        newType = handleDynamicSend(
-            node, operatorSelector, operatorMask,
-            getterType, operatorArguments);
         locals.update(element, newType, node);
-      } else {
-        // Bogus SendSet, for example [: myMethod += 42 :].
-        getterType = types.dynamicType;
-        newType = handleDynamicSend(
-            node, operatorSelector, operatorMask,
-            getterType, operatorArguments);
       }
 
-      if (node.isPostfix) {
-        return getterType;
-      } else {
-        return newType;
-      }
+      return node.isPostfix ? getterType : newType;
     }
   }
 
@@ -932,12 +930,18 @@
         getterMask,
         receiverType,
         new ArgumentsTypes<T>([indexType], null));
-    T returnType = handleDynamicSend(
-        node,
-        operatorSelector,
-        operatorMask,
-        getterType,
-        new ArgumentsTypes<T>([rhsType], null));
+
+    T returnType;
+    if (node.isIfNullAssignment) {
+      returnType = types.allocateDiamondPhi(getterType, rhsType);
+    } else {
+      returnType = handleDynamicSend(
+          node,
+          operatorSelector,
+          operatorMask,
+          getterType,
+          new ArgumentsTypes<T>([rhsType], null));
+    }
     handleDynamicSend(
         node,
         setterSelector,
diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc
index 60439a2..8718c00 100644
--- a/runtime/vm/dart.cc
+++ b/runtime/vm/dart.cc
@@ -23,6 +23,7 @@
 #include "vm/service_isolate.h"
 #include "vm/simulator.h"
 #include "vm/snapshot.h"
+#include "vm/store_buffer.h"
 #include "vm/stub_code.h"
 #include "vm/symbols.h"
 #include "vm/thread_interrupter.h"
@@ -211,6 +212,7 @@
   vm_isolate_ = NULL;
 
   TargetCPUFeatures::Cleanup();
+  StoreBuffer::ShutDown();
 #endif
 
   Profiler::Shutdown();
diff --git a/runtime/vm/growable_array.h b/runtime/vm/growable_array.h
index 045c3e5..1e7c204 100644
--- a/runtime/vm/growable_array.h
+++ b/runtime/vm/growable_array.h
@@ -99,6 +99,17 @@
     }
   }
 
+  // Swap entries |i| and |j|.
+  void Swap(intptr_t i, intptr_t j) {
+    ASSERT(i >= 0);
+    ASSERT(j >= 0);
+    ASSERT(i < length_);
+    ASSERT(j < length_);
+    T temp = data_[i];
+    data_[i] = data_[j];
+    data_[j] = temp;
+  }
+
   // The content is uninitialized after calling it.
   void SetLength(intptr_t new_length);
 
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 764d21a..933afd7 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -1666,8 +1666,8 @@
 }
 
 void Isolate::RemoveTimelineEventRecorder() {
-  SetTimelineEventRecorder(NULL);
   delete timeline_event_recorder_;
+  SetTimelineEventRecorder(NULL);
 }
 
 
@@ -1789,14 +1789,28 @@
     // Paused at start / exit . Don't tick.
     return 0;
   }
-  InterruptableThreadState* state = thread_state();
-  if (state == NULL) {
-    // Isolate is not scheduled on a thread.
+  // Make sure that the isolate's mutator thread does not change behind our
+  // backs. Otherwise we find the entry in the registry and end up reading
+  // the field again. Only by that time it has been reset to NULL because the
+  // thread was in process of exiting the isolate.
+  Thread* mutator = mutator_thread_;
+  if (mutator == NULL) {
+    // No active mutator.
     ProfileIdle();
     return 1;
   }
-  ASSERT(state->id != OSThread::kInvalidThreadId);
-  ThreadInterrupter::InterruptThread(state);
+
+  // TODO(johnmccutchan): Sample all threads, not just the mutator thread.
+  // TODO(johnmccutchan): Keep a global list of threads and use that
+  // instead of Isolate.
+  ThreadRegistry::EntryIterator it(thread_registry());
+  while (it.HasNext()) {
+    const ThreadRegistry::Entry& entry = it.Next();
+    if (entry.thread == mutator) {
+      ThreadInterrupter::InterruptThread(mutator);
+      break;
+    }
+  }
   return 1;
 }
 
@@ -1949,19 +1963,6 @@
 }
 
 
-#if defined(DEBUG)
-void Isolate::CheckForDuplicateThreadState(InterruptableThreadState* state) {
-  MonitorLocker ml(isolates_list_monitor_);
-  ASSERT(state != NULL);
-  Isolate* current = isolates_list_head_;
-  while (current) {
-    ASSERT(current->thread_state() != state);
-    current = current->next_;
-  }
-}
-#endif
-
-
 template<class T>
 T* Isolate::AllocateReusableHandle() {
   T* handle = reinterpret_cast<T*>(reusable_handles_.AllocateScopedHandle());
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index c43a3fa..9621311 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -632,15 +632,6 @@
 
   void PrintJSON(JSONStream* stream, bool ref = true);
 
-  void set_thread_state(InterruptableThreadState* state) {
-    ASSERT((thread_state_ == NULL) || (state == NULL));
-    thread_state_ = state;
-  }
-
-  InterruptableThreadState* thread_state() const {
-    return thread_state_;
-  }
-
   CompilerStats* compiler_stats() {
     return compiler_stats_;
   }
@@ -925,7 +916,6 @@
   // Manage list of existing isolates.
   static void AddIsolateTolist(Isolate* isolate);
   static void RemoveIsolateFromList(Isolate* isolate);
-  static void CheckForDuplicateThreadState(InterruptableThreadState* state);
 
   static Monitor* isolates_list_monitor_;  // Protects isolates_list_head_
   static Isolate* isolates_list_head_;
diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc
index ff0edc7..0ace0cc 100644
--- a/runtime/vm/profiler.cc
+++ b/runtime/vm/profiler.cc
@@ -169,7 +169,8 @@
   if (profiler_data == NULL) {
     return;
   }
-  ThreadInterrupter::Register(RecordSampleInterruptCallback, isolate);
+  Thread* thread = Thread::Current();
+  thread->SetThreadInterrupter(RecordSampleInterruptCallback, isolate);
   ThreadInterrupter::WakeUp();
 }
 
@@ -182,7 +183,8 @@
     return;
   }
   ASSERT(initialized_);
-  ThreadInterrupter::Unregister();
+  Thread* thread = Thread::Current();
+  thread->SetThreadInterrupter(NULL, NULL);
 }
 
 
diff --git a/runtime/vm/store_buffer.cc b/runtime/vm/store_buffer.cc
index f4fbad7..fee1281 100644
--- a/runtime/vm/store_buffer.cc
+++ b/runtime/vm/store_buffer.cc
@@ -26,6 +26,12 @@
 }
 
 
+void StoreBuffer::ShutDown() {
+  delete global_empty_;
+  delete global_mutex_;
+}
+
+
 StoreBuffer::StoreBuffer() : mutex_(new Mutex()) {
 }
 
@@ -105,7 +111,7 @@
   {
     MutexLocker ml(global_mutex_);
     if (!global_empty_->IsEmpty()) {
-      global_empty_->Pop();
+      return global_empty_->Pop();
     }
   }
   return new StoreBufferBlock();
diff --git a/runtime/vm/store_buffer.h b/runtime/vm/store_buffer.h
index b1bd387..12a0535 100644
--- a/runtime/vm/store_buffer.h
+++ b/runtime/vm/store_buffer.h
@@ -79,6 +79,7 @@
   StoreBuffer();
   ~StoreBuffer();
   static void InitOnce();
+  static void ShutDown();
 
   // Interrupt when crossing this threshold of non-empty blocks in the buffer.
   static const intptr_t kMaxNonEmpty = 100;
diff --git a/runtime/vm/thread.cc b/runtime/vm/thread.cc
index aa4f67e..09a0774 100644
--- a/runtime/vm/thread.cc
+++ b/runtime/vm/thread.cc
@@ -22,6 +22,24 @@
 ThreadLocalKey Thread::thread_key_ = OSThread::kUnsetThreadLocalKey;
 
 
+// Remove |thread| from each isolate's thread registry.
+class ThreadPruner : public IsolateVisitor {
+ public:
+  explicit ThreadPruner(Thread* thread)
+      : thread_(thread) {
+    ASSERT(thread_ != NULL);
+  }
+
+  void VisitIsolate(Isolate* isolate) {
+    ThreadRegistry* registry = isolate->thread_registry();
+    ASSERT(registry != NULL);
+    registry->PruneThread(thread_);
+  }
+ private:
+  Thread* thread_;
+};
+
+
 static void DeleteThread(void* thread) {
   delete reinterpret_cast<Thread*>(thread);
 }
@@ -30,6 +48,9 @@
 Thread::~Thread() {
   // We should cleanly exit any isolate before destruction.
   ASSERT(isolate_ == NULL);
+  // Clear |this| from all isolate's thread registry.
+  ThreadPruner pruner(this);
+  Isolate::VisitIsolates(&pruner);
 }
 
 
@@ -38,8 +59,11 @@
   thread_key_ = OSThread::CreateThreadLocal(DeleteThread);
   ASSERT(thread_key_ != OSThread::kUnsetThreadLocalKey);
   ASSERT(Thread::Current() == NULL);
-  // Postpone initialization of VM constants for this first thread.
-  SetCurrent(new Thread(false));
+  // Allocate a new Thread and postpone initialization of VM constants for
+  // this first thread.
+  Thread* thread = new Thread(false);
+  // Verify that current thread was set.
+  ASSERT(Thread::Current() == thread);
 }
 
 
@@ -58,7 +82,10 @@
 
 void Thread::EnsureInit() {
   if (Thread::Current() == NULL) {
-    SetCurrent(new Thread());
+    // Allocate a new Thread.
+    Thread* thread = new Thread();
+    // Verify that current thread was set.
+    ASSERT(Thread::Current() == thread);
   }
 }
 
@@ -75,7 +102,11 @@
 
 
 Thread::Thread(bool init_vm_constants)
-    : isolate_(NULL),
+    : id_(OSThread::GetCurrentThreadId()),
+      thread_interrupt_callback_(NULL),
+      thread_interrupt_data_(NULL),
+      isolate_(NULL),
+      heap_(NULL),
       store_buffer_block_(NULL) {
   ClearState();
 #define DEFAULT_INIT(type_name, member_name, init_expr, default_init_value)    \
@@ -85,6 +116,7 @@
   if (init_vm_constants) {
     InitVMConstants();
   }
+  SetCurrent(this);
 }
 
 
@@ -126,23 +158,14 @@
   ASSERT(!isolate->HasMutatorThread());
   thread->isolate_ = isolate;
   isolate->MakeCurrentThreadMutator(thread);
-  // TODO(koda): Migrate thread_state_ and profile_data_ to Thread, to allow
-  // helper threads concurrent with mutator.
-  ASSERT(isolate->thread_state() == NULL);
-  InterruptableThreadState* thread_state =
-      ThreadInterrupter::GetCurrentThreadState();
-#if defined(DEBUG)
-  Isolate::CheckForDuplicateThreadState(thread_state);
-#endif
-  ASSERT(thread_state != NULL);
-  Profiler::BeginExecution(isolate);
-  isolate->set_thread_state(thread_state);
   isolate->set_vm_tag(VMTag::kVMTagId);
   ASSERT(thread->store_buffer_block_ == NULL);
   thread->store_buffer_block_ = isolate->store_buffer()->PopBlock();
   ASSERT(isolate->heap() != NULL);
   thread->heap_ = isolate->heap();
   thread->Schedule(isolate);
+  // TODO(koda): Migrate profiler interface to use Thread.
+  Profiler::BeginExecution(isolate);
 }
 
 
@@ -151,6 +174,7 @@
   // TODO(koda): Audit callers; they should know whether they're in an isolate.
   if (thread == NULL || thread->isolate() == NULL) return;
   Isolate* isolate = thread->isolate();
+  Profiler::EndExecution(isolate);
   thread->Unschedule();
   StoreBufferBlock* block = thread->store_buffer_block_;
   thread->store_buffer_block_ = NULL;
@@ -160,8 +184,6 @@
   } else {
     isolate->set_vm_tag(VMTag::kLoadWaitTagId);
   }
-  isolate->set_thread_state(NULL);
-  Profiler::EndExecution(isolate);
   isolate->ClearMutatorThread();
   thread->isolate_ = NULL;
   ASSERT(Isolate::Current() == NULL);
@@ -245,6 +267,32 @@
 }
 
 
+void Thread::SetThreadInterrupter(ThreadInterruptCallback callback,
+                                  void* data) {
+  ASSERT(Thread::Current() == this);
+  thread_interrupt_callback_ = callback;
+  thread_interrupt_data_ = data;
+}
+
+
+bool Thread::IsThreadInterrupterEnabled(ThreadInterruptCallback* callback,
+                                        void** data) const {
+#if defined(TARGET_OS_WINDOWS)
+  // On Windows we expect this to be called from the thread interrupter thread.
+  ASSERT(id() != OSThread::GetCurrentThreadId());
+#else
+  // On posix platforms, we expect this to be called from signal handler.
+  ASSERT(id() == OSThread::GetCurrentThreadId());
+#endif
+  ASSERT(callback != NULL);
+  ASSERT(data != NULL);
+  *callback = thread_interrupt_callback_;
+  *data = thread_interrupt_data_;
+  return (*callback != NULL) &&
+         (*data != NULL);
+}
+
+
 bool Thread::CanLoadFromThread(const Object& object) {
 #define CHECK_OBJECT(type_name, member_name, expr, default_init_value)         \
   if (object.raw() == expr) return true;
diff --git a/runtime/vm/thread.h b/runtime/vm/thread.h
index 2b2d343..557b862 100644
--- a/runtime/vm/thread.h
+++ b/runtime/vm/thread.h
@@ -36,6 +36,26 @@
   CACHED_VM_OBJECTS_LIST(V)                                                    \
   CACHED_ADDRESSES_LIST(V)
 
+struct InterruptedThreadState {
+  ThreadId tid;
+  uintptr_t pc;
+  uintptr_t csp;
+  uintptr_t dsp;
+  uintptr_t fp;
+  uintptr_t lr;
+};
+
+// When a thread is interrupted the thread specific interrupt callback will be
+// invoked. Each callback is given an InterruptedThreadState and the user data
+// pointer. When inside a thread interrupt callback doing any of the following
+// is forbidden:
+//   * Accessing TLS -- Because on Windows the callback will be running in a
+//                      different thread.
+//   * Allocating memory -- Because this takes locks which may already be held,
+//                          resulting in a dead lock.
+//   * Taking a lock -- See above.
+typedef void (*ThreadInterruptCallback)(const InterruptedThreadState& state,
+                                        void* data);
 
 // A VM thread; may be executing Dart code or performing helper tasks like
 // garbage collection or compilation. The Thread structure associated with
@@ -204,9 +224,22 @@
   static bool CanLoadFromThread(const Object& object);
   static intptr_t OffsetFromThread(const Object& object);
 
+  ThreadId id() const {
+    ASSERT(id_ != OSThread::kInvalidThreadId);
+    return id_;
+  }
+
+  void SetThreadInterrupter(ThreadInterruptCallback callback, void* data);
+
+  bool IsThreadInterrupterEnabled(ThreadInterruptCallback* callback,
+                                  void** data) const;
+
  private:
   static ThreadLocalKey thread_key_;
 
+  const ThreadId id_;
+  ThreadInterruptCallback thread_interrupt_callback_;
+  void* thread_interrupt_data_;
   Isolate* isolate_;
   Heap* heap_;
   State state_;
diff --git a/runtime/vm/thread_interrupter.cc b/runtime/vm/thread_interrupter.cc
index 10cff89..b28bf25 100644
--- a/runtime/vm/thread_interrupter.cc
+++ b/runtime/vm/thread_interrupter.cc
@@ -153,95 +153,6 @@
   }
 }
 
-// Register the currently running thread for interrupts. If the current thread
-// is already registered, callback and data will be updated.
-InterruptableThreadState* ThreadInterrupter::Register(
-    ThreadInterruptCallback callback, void* data) {
-  if (shutdown_) {
-    return NULL;
-  }
-  ASSERT(initialized_);
-  InterruptableThreadState* state = _EnsureThreadStateCreated();
-  // Set callback and data.
-  UpdateStateObject(callback, data);
-  return state;
-}
-
-
-// Unregister the currently running thread for interrupts.
-void ThreadInterrupter::Unregister() {
-  if (shutdown_) {
-    return;
-  }
-  ASSERT(initialized_);
-  _EnsureThreadStateCreated();
-  // Clear callback and data.
-  UpdateStateObject(NULL, NULL);
-}
-
-
-InterruptableThreadState* ThreadInterrupter::_EnsureThreadStateCreated() {
-  InterruptableThreadState* state = CurrentThreadState();
-  if (state == NULL) {
-    // Create thread state object lazily.
-    ThreadId current_thread = OSThread::GetCurrentThreadId();
-    if (FLAG_trace_thread_interrupter) {
-      intptr_t tid = OSThread::ThreadIdToIntPtr(current_thread);
-      OS::Print("ThreadInterrupter Tracking %p\n",
-                reinterpret_cast<void*>(tid));
-    }
-    // Note: We currently do not free a thread's InterruptableThreadState.
-    state = new InterruptableThreadState();
-    ASSERT(state != NULL);
-    state->callback = NULL;
-    state->data = NULL;
-    state->id = current_thread;
-    SetCurrentThreadState(state);
-  }
-  return state;
-}
-
-
-void ThreadInterrupter::UpdateStateObject(ThreadInterruptCallback callback,
-                                          void* data) {
-  InterruptableThreadState* state = CurrentThreadState();
-  ThreadId current_thread = OSThread::GetCurrentThreadId();
-  ASSERT(state != NULL);
-  ASSERT(OSThread::Compare(state->id, OSThread::GetCurrentThreadId()));
-  SetCurrentThreadState(NULL);
-  // It is now safe to modify the state object. If an interrupt occurs,
-  // the current thread state will be NULL.
-  state->callback = callback;
-  state->data = data;
-  SetCurrentThreadState(state);
-  if (FLAG_trace_thread_interrupter) {
-    intptr_t tid = OSThread::ThreadIdToIntPtr(current_thread);
-    if (callback == NULL) {
-      OS::Print("ThreadInterrupter Cleared %p\n", reinterpret_cast<void*>(tid));
-    } else {
-      OS::Print("ThreadInterrupter Updated %p\n", reinterpret_cast<void*>(tid));
-    }
-  }
-}
-
-
-InterruptableThreadState* ThreadInterrupter::GetCurrentThreadState() {
-  return _EnsureThreadStateCreated();
-}
-
-
-InterruptableThreadState* ThreadInterrupter::CurrentThreadState() {
-  InterruptableThreadState* state = reinterpret_cast<InterruptableThreadState*>(
-      OSThread::GetThreadLocal(thread_state_key_));
-  return state;
-}
-
-
-void ThreadInterrupter::SetCurrentThreadState(InterruptableThreadState* state) {
-  OSThread::SetThreadLocal(thread_state_key_,
-                              reinterpret_cast<uword>(state));
-}
-
 
 void ThreadInterruptNoOp(const InterruptedThreadState& state, void* data) {
   // NoOp.
diff --git a/runtime/vm/thread_interrupter.h b/runtime/vm/thread_interrupter.h
index 2d3d1b3..2e84b13 100644
--- a/runtime/vm/thread_interrupter.h
+++ b/runtime/vm/thread_interrupter.h
@@ -8,37 +8,10 @@
 #include "vm/allocation.h"
 #include "vm/signal_handler.h"
 #include "vm/os_thread.h"
-
+#include "vm/thread.h"
 
 namespace dart {
 
-struct InterruptedThreadState {
-  ThreadId tid;
-  uintptr_t pc;
-  uintptr_t csp;
-  uintptr_t dsp;
-  uintptr_t fp;
-  uintptr_t lr;
-};
-
-// When a thread is interrupted the thread specific interrupt callback will be
-// invoked. Each callback is given an InterruptedThreadState and the user data
-// pointer. When inside a thread interrupt callback doing any of the following
-// is forbidden:
-//   * Accessing TLS.
-//   * Allocating memory.
-//   * Taking a lock.
-typedef void (*ThreadInterruptCallback)(const InterruptedThreadState& state,
-                                        void* data);
-
-// State stored per registered thread.
-class InterruptableThreadState {
- public:
-  ThreadId id;
-  ThreadInterruptCallback callback;
-  void* data;
-};
-
 class ThreadInterrupter : public AllStatic {
  public:
   static void InitOnce();
@@ -54,19 +27,13 @@
 
   // Register the currently running thread for interrupts. If the current thread
   // is already registered, callback and data will be updated.
-  static InterruptableThreadState* Register(ThreadInterruptCallback callback,
-                                            void* data);
+  static void Register(ThreadInterruptCallback callback, void* data);
+
   // Unregister the currently running thread for interrupts.
   static void Unregister();
 
-  // Get the current thread state. Will create a thread state if one hasn't
-  // been allocated.
-  static InterruptableThreadState* GetCurrentThreadState();
-  // Get the current thread state. Will not create one if one doesn't exist.
-  static InterruptableThreadState* CurrentThreadState();
-
   // Interrupt a thread.
-  static void InterruptThread(InterruptableThreadState* thread_state);
+  static void InterruptThread(Thread* thread);
 
  private:
   static const intptr_t kMaxThreads = 4096;
@@ -83,11 +50,8 @@
     return current_wait_time_ == Monitor::kNoTimeout;
   }
 
-  static InterruptableThreadState* _EnsureThreadStateCreated();
   static void UpdateStateObject(ThreadInterruptCallback callback, void* data);
 
-  static void SetCurrentThreadState(InterruptableThreadState* state);
-
   static void ThreadMain(uword parameters);
 
   static void InstallSignalHandler();
diff --git a/runtime/vm/thread_interrupter_android.cc b/runtime/vm/thread_interrupter_android.cc
index fd27d53..5c4092d 100644
--- a/runtime/vm/thread_interrupter_android.cc
+++ b/runtime/vm/thread_interrupter_android.cc
@@ -24,33 +24,37 @@
     if (signal != SIGPROF) {
       return;
     }
-    InterruptableThreadState* state = ThreadInterrupter::CurrentThreadState();
-    if ((state == NULL) || (state->callback == NULL)) {
-      // No interrupter state or callback.
+    Thread* thread = Thread::Current();
+    if (thread == NULL) {
       return;
     }
-    ASSERT(OSThread::Compare(state->id, OSThread::GetCurrentThreadId()));
+    ThreadInterruptCallback callback = NULL;
+    void* callback_data = NULL;
+    if (!thread->IsThreadInterrupterEnabled(&callback, &callback_data)) {
+      return;
+    }
     // Extract thread state.
     ucontext_t* context = reinterpret_cast<ucontext_t*>(context_);
     mcontext_t mcontext = context->uc_mcontext;
     InterruptedThreadState its;
-    its.tid = state->id;
+    its.tid = thread->id();
     its.pc = SignalHandler::GetProgramCounter(mcontext);
     its.fp = SignalHandler::GetFramePointer(mcontext);
     its.csp = SignalHandler::GetCStackPointer(mcontext);
     its.dsp = SignalHandler::GetDartStackPointer(mcontext);
     its.lr = SignalHandler::GetLinkRegister(mcontext);
-    state->callback(its, state->data);
+    callback(its, callback_data);
   }
 };
 
 
-void ThreadInterrupter::InterruptThread(InterruptableThreadState* state) {
+void ThreadInterrupter::InterruptThread(Thread* thread) {
   if (FLAG_trace_thread_interrupter) {
     OS::Print("ThreadInterrupter interrupting %p\n",
-              reinterpret_cast<void*>(state->id));
+              reinterpret_cast<void*>(thread->id()));
   }
-  syscall(__NR_tgkill, getpid(), state->id, SIGPROF);
+  int result = syscall(__NR_tgkill, getpid(), thread->id(), SIGPROF);
+  ASSERT(result == 0);
 }
 
 
diff --git a/runtime/vm/thread_interrupter_linux.cc b/runtime/vm/thread_interrupter_linux.cc
index 5ff3c92..d7b9b85 100644
--- a/runtime/vm/thread_interrupter_linux.cc
+++ b/runtime/vm/thread_interrupter_linux.cc
@@ -22,33 +22,37 @@
     if (signal != SIGPROF) {
       return;
     }
-    InterruptableThreadState* state = ThreadInterrupter::CurrentThreadState();
-    if ((state == NULL) || (state->callback == NULL)) {
-      // No interrupter state or callback.
+    Thread* thread = Thread::Current();
+    if (thread == NULL) {
       return;
     }
-    ASSERT(OSThread::Compare(state->id, OSThread::GetCurrentThreadId()));
+    ThreadInterruptCallback callback = NULL;
+    void* callback_data = NULL;
+    if (!thread->IsThreadInterrupterEnabled(&callback, &callback_data)) {
+      return;
+    }
     // Extract thread state.
     ucontext_t* context = reinterpret_cast<ucontext_t*>(context_);
     mcontext_t mcontext = context->uc_mcontext;
     InterruptedThreadState its;
-    its.tid = state->id;
+    its.tid = thread->id();
     its.pc = SignalHandler::GetProgramCounter(mcontext);
     its.fp = SignalHandler::GetFramePointer(mcontext);
     its.csp = SignalHandler::GetCStackPointer(mcontext);
     its.dsp = SignalHandler::GetDartStackPointer(mcontext);
     its.lr = SignalHandler::GetLinkRegister(mcontext);
-    state->callback(its, state->data);
+    callback(its, callback_data);
   }
 };
 
 
-void ThreadInterrupter::InterruptThread(InterruptableThreadState* state) {
+void ThreadInterrupter::InterruptThread(Thread* thread) {
   if (FLAG_trace_thread_interrupter) {
     OS::Print("ThreadInterrupter interrupting %p\n",
-              reinterpret_cast<void*>(state->id));
+              reinterpret_cast<void*>(thread->id()));
   }
-  pthread_kill(state->id, SIGPROF);
+  int result = pthread_kill(thread->id(), SIGPROF);
+  ASSERT(result == 0);
 }
 
 
diff --git a/runtime/vm/thread_interrupter_macos.cc b/runtime/vm/thread_interrupter_macos.cc
index b5beeeb..f1ff45e 100644
--- a/runtime/vm/thread_interrupter_macos.cc
+++ b/runtime/vm/thread_interrupter_macos.cc
@@ -22,32 +22,36 @@
     if (signal != SIGPROF) {
       return;
     }
-    InterruptableThreadState* state = ThreadInterrupter::CurrentThreadState();
-    if ((state == NULL) || (state->callback == NULL)) {
-      // No interrupter state or callback.
+    Thread* thread = Thread::Current();
+    if (thread == NULL) {
       return;
     }
-    ASSERT(OSThread::Compare(state->id, OSThread::GetCurrentThreadId()));
+    ThreadInterruptCallback callback = NULL;
+    void* callback_data = NULL;
+    if (!thread->IsThreadInterrupterEnabled(&callback, &callback_data)) {
+      return;
+    }
     // Extract thread state.
     ucontext_t* context = reinterpret_cast<ucontext_t*>(context_);
     mcontext_t mcontext = context->uc_mcontext;
     InterruptedThreadState its;
-    its.tid = state->id;
+    its.tid = thread->id();
     its.pc = SignalHandler::GetProgramCounter(mcontext);
     its.fp = SignalHandler::GetFramePointer(mcontext);
     its.csp = SignalHandler::GetCStackPointer(mcontext);
     its.dsp = SignalHandler::GetDartStackPointer(mcontext);
     its.lr = SignalHandler::GetLinkRegister(mcontext);
-    state->callback(its, state->data);
+    callback(its, callback_data);
   }
 };
 
 
-void ThreadInterrupter::InterruptThread(InterruptableThreadState* state) {
+void ThreadInterrupter::InterruptThread(Thread* thread) {
   if (FLAG_trace_thread_interrupter) {
-    OS::Print("ThreadInterrupter interrupting %p\n", state->id);
+    OS::Print("ThreadInterrupter interrupting %p\n", thread->id());
   }
-  pthread_kill(state->id, SIGPROF);
+  int result = pthread_kill(thread->id(), SIGPROF);
+  ASSERT(result == 0);
 }
 
 
diff --git a/runtime/vm/thread_interrupter_test.cc b/runtime/vm/thread_interrupter_test.cc
index 4d69edd..aeb9ff1 100644
--- a/runtime/vm/thread_interrupter_test.cc
+++ b/runtime/vm/thread_interrupter_test.cc
@@ -17,11 +17,12 @@
   static void InterruptTest(const intptr_t run_time, const intptr_t period) {
     const double allowed_error = 0.25;  // +/- 25%
     intptr_t count = 0;
-    ThreadInterrupter::Unregister();
+    Thread::EnsureInit();
+    Thread* thread = Thread::Current();
+    thread->SetThreadInterrupter(IncrementCallback, &count);
     ThreadInterrupter::SetInterruptPeriod(period);
-    ThreadInterrupter::Register(IncrementCallback, &count);
     OS::Sleep(run_time * kMillisecondsPerSecond);
-    ThreadInterrupter::Unregister();
+    thread->SetThreadInterrupter(NULL, NULL);
     intptr_t run_time_micros = run_time * kMicrosecondsPerSecond;
     intptr_t expected_interrupts = run_time_micros / period;
     intptr_t error = allowed_error * expected_interrupts;
diff --git a/runtime/vm/thread_interrupter_win.cc b/runtime/vm/thread_interrupter_win.cc
index 07feed0..72220ed 100644
--- a/runtime/vm/thread_interrupter_win.cc
+++ b/runtime/vm/thread_interrupter_win.cc
@@ -51,57 +51,55 @@
   }
 
 
-  static void Interrupt(InterruptableThreadState* state) {
-    ASSERT(!OSThread::Compare(GetCurrentThreadId(), state->id));
+  static void Interrupt(Thread* thread) {
+    ASSERT(!OSThread::Compare(GetCurrentThreadId(), thread->id()));
     HANDLE handle = OpenThread(THREAD_GET_CONTEXT |
                                THREAD_QUERY_INFORMATION |
                                THREAD_SUSPEND_RESUME,
                                false,
-                               state->id);
+                               thread->id());
     ASSERT(handle != NULL);
     DWORD result = SuspendThread(handle);
     if (result == kThreadError) {
       if (FLAG_trace_thread_interrupter) {
-        OS::Print("ThreadInterrupted failed to suspend thread %p\n",
-                  reinterpret_cast<void*>(state->id));
+        OS::Print("ThreadInterrupter failed to suspend thread %p\n",
+                  reinterpret_cast<void*>(thread->id()));
       }
       CloseHandle(handle);
       return;
     }
     InterruptedThreadState its;
-    its.tid = state->id;
+    its.tid = thread->id();
     if (!GrabRegisters(handle, &its)) {
       // Failed to get thread registers.
       ResumeThread(handle);
       if (FLAG_trace_thread_interrupter) {
-        OS::Print("ThreadInterrupted failed to get registers for %p\n",
-                  reinterpret_cast<void*>(state->id));
+        OS::Print("ThreadInterrupter failed to get registers for %p\n",
+                  reinterpret_cast<void*>(thread->id()));
       }
       CloseHandle(handle);
       return;
     }
-    if (state->callback == NULL) {
-      // No callback registered.
-      ResumeThread(handle);
-      CloseHandle(handle);
-      return;
+    ThreadInterruptCallback callback = NULL;
+    void* callback_data = NULL;
+    if (thread->IsThreadInterrupterEnabled(&callback, &callback_data)) {
+      callback(its, callback_data);
     }
-    state->callback(its, state->data);
     ResumeThread(handle);
     CloseHandle(handle);
   }
 };
 
 
-void ThreadInterrupter::InterruptThread(InterruptableThreadState* state) {
+void ThreadInterrupter::InterruptThread(Thread* thread) {
   if (FLAG_trace_thread_interrupter) {
     OS::Print("ThreadInterrupter suspending %p\n",
-              reinterpret_cast<void*>(state->id));
+              reinterpret_cast<void*>(thread->id()));
   }
-  ThreadInterrupterWin::Interrupt(state);
+  ThreadInterrupterWin::Interrupt(thread);
   if (FLAG_trace_thread_interrupter) {
     OS::Print("ThreadInterrupter resuming %p\n",
-              reinterpret_cast<void*>(state->id));
+              reinterpret_cast<void*>(thread->id()));
   }
 }
 
diff --git a/runtime/vm/thread_registry.cc b/runtime/vm/thread_registry.cc
index 8ade009..23e22b7 100644
--- a/runtime/vm/thread_registry.cc
+++ b/runtime/vm/thread_registry.cc
@@ -9,6 +9,11 @@
 
 namespace dart {
 
+ThreadRegistry::~ThreadRegistry() {
+  delete monitor_;
+}
+
+
 void ThreadRegistry::SafepointThreads() {
   MonitorLocker ml(monitor_);
   // First wait for any older rounds that are still in progress.
@@ -44,6 +49,74 @@
 }
 
 
+void ThreadRegistry::PruneThread(Thread* thread) {
+  MonitorLocker ml(monitor_);
+  intptr_t length = entries_.length();
+  if (length == 0) {
+    return;
+  }
+  intptr_t found_index = -1;
+  for (intptr_t index = 0; index < length; index++) {
+    if (entries_.At(index).thread == thread) {
+      found_index = index;
+      break;
+    }
+  }
+  if (found_index < 0) {
+    return;
+  }
+  if (found_index != (length - 1)) {
+    // Swap with last entry.
+    entries_.Swap(found_index, length - 1);
+  }
+  entries_.RemoveLast();
+}
+
+
+ThreadRegistry::EntryIterator::EntryIterator(ThreadRegistry* registry)
+    : index_(0),
+      registry_(NULL) {
+  Reset(registry);
+}
+
+
+ThreadRegistry::EntryIterator::~EntryIterator() {
+  Reset(NULL);
+}
+
+
+void ThreadRegistry::EntryIterator::Reset(ThreadRegistry* registry) {
+  // Reset index.
+  index_ = 0;
+
+  // Unlock old registry.
+  if (registry_ != NULL) {
+    registry_->monitor_->Exit();
+  }
+
+  registry_ = registry;
+
+  // Lock new registry.
+  if (registry_ != NULL) {
+    registry_->monitor_->Enter();
+  }
+}
+
+
+bool ThreadRegistry::EntryIterator::HasNext() const {
+  if (registry_ == NULL) {
+    return false;
+  }
+  return index_ < registry_->entries_.length();
+}
+
+
+const ThreadRegistry::Entry& ThreadRegistry::EntryIterator::Next() {
+  ASSERT(HasNext());
+  return registry_->entries_.At(index_++);
+}
+
+
 void ThreadRegistry::CheckSafepointLocked() {
   int64_t last_round = -1;
   while (in_rendezvous_) {
diff --git a/runtime/vm/thread_registry.h b/runtime/vm/thread_registry.h
index f8f324d..abe669a 100644
--- a/runtime/vm/thread_registry.h
+++ b/runtime/vm/thread_registry.h
@@ -23,6 +23,8 @@
         remaining_(0),
         round_(0) {}
 
+  ~ThreadRegistry();
+
   // Bring all threads in this isolate to a safepoint. The caller is
   // expected to be implicitly at a safepoint. The threads will wait
   // until ResumeAllThreads is called. First participates in any
@@ -125,13 +127,33 @@
     }
   }
 
- private:
+  void PruneThread(Thread* thread);
+
   struct Entry {
     Thread* thread;
     bool scheduled;
     Thread::State state;
   };
 
+  class EntryIterator {
+   public:
+    explicit EntryIterator(ThreadRegistry* registry);
+    ~EntryIterator();
+
+    // Returns false when there are no more entries.
+    bool HasNext() const;
+
+    // Returns the next entry and moves forward.
+    const Entry& Next();
+
+   private:
+    void Reset(ThreadRegistry* registry);
+
+    intptr_t index_;
+    ThreadRegistry* registry_;
+  };
+
+ private:
   // Returns Entry corresponding to thread in registry or NULL.
   // Note: Lock should be taken before this function is called.
   // TODO(koda): Add method Monitor::IsOwnedByCurrentThread.
diff --git a/runtime/vm/thread_test.cc b/runtime/vm/thread_test.cc
index d1db1d3..39bec07 100644
--- a/runtime/vm/thread_test.cc
+++ b/runtime/vm/thread_test.cc
@@ -38,9 +38,10 @@
   // This unit test case needs a running isolate.
   Dart_CreateIsolate(
       NULL, NULL, bin::isolate_snapshot_buffer, NULL, NULL, NULL);
-  Isolate* isolate = Isolate::Current();
+  Thread* thread = Thread::Current();
+  Isolate* isolate = thread->isolate();
   // Thread interrupter interferes with this test, disable interrupts.
-  isolate->set_thread_state(NULL);
+  thread->SetThreadInterrupter(NULL, NULL);
   Profiler::EndExecution(isolate);
   Monitor* monitor = new Monitor();
   monitor->Enter();
diff --git a/sdk/api_readme.md b/sdk/api_readme.md
index 250c823..c723990 100644
--- a/sdk/api_readme.md
+++ b/sdk/api_readme.md
@@ -1,9 +1,9 @@
 Welcome to the Dart API reference documentation, covering the official Dart API
 libraries. Some of the most fundamental Dart libraries include:
    
-  * [dart:core](dart-core/index.html): Core functionality such as strings, numbers, collections, errors, dates, and URIs.
-  * [dart:html](dart-html/index.html): DOM manipulation for web apps.
-  * [dart:io](dart-io/index.html): I/O for command-line apps.
+  * [dart:core](dart-core/dart-core-library.html): Core functionality such as strings, numbers, collections, errors, dates, and URIs.
+  * [dart:html](dart-html/dart-html-library.html): DOM manipulation for web apps.
+  * [dart:io](dart-io/dart-io-library.html): I/O for command-line apps.
   
 Except for `dart:core`, you must import a library before you can use it. Here's
 an example of importing `dart:html` and `dart:math`:
diff --git a/sdk/lib/core/resource.dart b/sdk/lib/core/resource.dart
index 9bb88d1..17bdebc 100644
--- a/sdk/lib/core/resource.dart
+++ b/sdk/lib/core/resource.dart
@@ -7,6 +7,9 @@
 /**
  * A resource that can be read into the program.
  *
+ * WARNING: This API is _experimental_,
+ * and it may be changed or removed in future releases
+ *
  * A resource is data that can be located using a URI and read into
  * the program at runtime.
  * The URI may use the `package` scheme to read resources provided
diff --git a/tests/compiler/dart2js_extra/if_null2_test.dart b/tests/compiler/dart2js_extra/if_null2_test.dart
new file mode 100644
index 0000000..a9923a8
--- /dev/null
+++ b/tests/compiler/dart2js_extra/if_null2_test.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// Regression for #24134: inference was not tracking ??= correctly.
+library tests.compiler.dart2js_extra.if_null2_test;
+
+import "package:expect/expect.dart";
+
+main() {
+  var map;
+  map ??= {};
+  Expect.equals(0, map.length);
+  Expect.isTrue(map.length == 0);
+}
diff --git a/tests/compiler/dart2js_extra/if_null3_test.dart b/tests/compiler/dart2js_extra/if_null3_test.dart
new file mode 100644
index 0000000..dac99e0
--- /dev/null
+++ b/tests/compiler/dart2js_extra/if_null3_test.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// Regression for #24135: inference was not tracking `[]??=` correctly.
+library tests.compiler.dart2js_extra.if_null3_test;
+
+import "package:expect/expect.dart";
+
+void main() {
+  var map;
+  (((map ??= {})['key1'] ??= {})['key2'] ??= {})['key3'] = 'value';
+  Expect.equals('{key1: {key2: {key3: value}}}', '$map');
+}
diff --git a/tests/compiler/dart2js_extra/if_null_test.dart b/tests/compiler/dart2js_extra/if_null_test.dart
index deb9b64..5574643 100644
--- a/tests/compiler/dart2js_extra/if_null_test.dart
+++ b/tests/compiler/dart2js_extra/if_null_test.dart
@@ -2,7 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// SharedOptions=--enable-null-aware-operators
 import "package:expect/expect.dart";
 
 @NoInline() @AssumeDynamic()
diff --git a/tests/language/language_dart2js.status b/tests/language/language_dart2js.status
index 4740960..0d39999 100644
--- a/tests/language/language_dart2js.status
+++ b/tests/language/language_dart2js.status
@@ -120,7 +120,6 @@
 
 if_null_assignment_behavior_test/13: Crash # Issue 23491
 if_null_assignment_behavior_test/14: Crash # Issue 23491
-nullaware_opt_test: Fail # Fails at e?.f ??= 200;
 
 if_null_assignment_behavior_test/29: Crash # Issue 23611
 prefix_assignment_test/01: Crash # Issue 23611
@@ -488,20 +487,6 @@
 generic_field_mixin3_test: RuntimeError # Please triage this failure.
 generic_instanceof_test: RuntimeError # Please triage this failure.
 generic_native_test: RuntimeError # Please triage this failure.
-if_null_assignment_behavior_test/12: RuntimeError # Please triage this failure.
-if_null_assignment_behavior_test/28: RuntimeError # Please triage this failure.
-if_null_assignment_static_test/01: RuntimeError # v0.get$a is not a function
-if_null_assignment_static_test/03: RuntimeError # v0.get$a is not a function
-if_null_assignment_static_test/04: RuntimeError # v0.get$b is not a function
-if_null_assignment_static_test/05: RuntimeError # v0.get$a is not a function
-if_null_assignment_static_test/29: RuntimeError # v0.get$a is not a function
-if_null_assignment_static_test/31: RuntimeError # v0.get$a is not a function
-if_null_assignment_static_test/32: RuntimeError # v0.get$b is not a function
-if_null_assignment_static_test/33: RuntimeError # v0.get$a is not a function
-if_null_assignment_static_test/36: RuntimeError # v0.get$a is not a function
-if_null_assignment_static_test/38: RuntimeError # v0.get$a is not a function
-if_null_assignment_static_test/39: RuntimeError # v0.get$b is not a function
-if_null_assignment_static_test/40: RuntimeError # v0.get$a is not a function
 infinite_switch_label_test: Crash # (switch (target){l0:...  continue to a labeled switch case
 instance_creation_in_function_annotation_test: Crash # (static Iterable<Str...  cannot handle async/sync*/async* functions
 instanceof2_test: RuntimeError # Please triage this failure.
diff --git a/tools/VERSION b/tools/VERSION
index 9524960..79b12b13 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
 MINOR 12
 PATCH 0
 PRERELEASE 5
-PRERELEASE_PATCH 8
+PRERELEASE_PATCH 9