Version 2.12.0-158.0.dev

Merge commit '868cb26a2d9093983ed022b77ba26be6cb8fb7f7' into 'dev'
diff --git a/runtime/bin/builtin_impl_sources.gni b/runtime/bin/builtin_impl_sources.gni
index 58323d0..1045fdc 100644
--- a/runtime/bin/builtin_impl_sources.gni
+++ b/runtime/bin/builtin_impl_sources.gni
@@ -77,4 +77,5 @@
   "eventhandler_test.cc",
   "file_test.cc",
   "hashmap_test.cc",
+  "priority_heap_test.cc",
 ]
diff --git a/runtime/bin/eventhandler.cc b/runtime/bin/eventhandler.cc
index 6a02a0f..83e93a0 100644
--- a/runtime/bin/eventhandler.cc
+++ b/runtime/bin/eventhandler.cc
@@ -15,46 +15,6 @@
 namespace dart {
 namespace bin {
 
-void TimeoutQueue::UpdateTimeout(Dart_Port port, int64_t timeout) {
-  // Find port if present.
-  Timeout* last = NULL;
-  Timeout* current = timeouts_;
-  while (current != NULL) {
-    if (current->port() == port) {
-      // Found.
-      if (timeout < 0) {
-        // Remove from list and delete existing.
-        if (last != NULL) {
-          last->set_next(current->next());
-        } else {
-          timeouts_ = current->next();
-        }
-        delete current;
-      } else {
-        // Update timeout.
-        current->set_timeout(timeout);
-      }
-      break;
-    }
-    last = current;
-    current = current->next();
-  }
-  if (current == NULL && timeout >= 0) {
-    // Not found, create a new.
-    timeouts_ = new Timeout(port, timeout, timeouts_);
-  }
-  // Clear and find next timeout.
-  next_timeout_ = NULL;
-  current = timeouts_;
-  while (current != NULL) {
-    if ((next_timeout_ == NULL) ||
-        (current->timeout() < next_timeout_->timeout())) {
-      next_timeout_ = current;
-    }
-    current = current->next();
-  }
-}
-
 static EventHandler* event_handler = NULL;
 static Monitor* shutdown_monitor = NULL;
 
diff --git a/runtime/bin/eventhandler.h b/runtime/bin/eventhandler.h
index a305f65..69a8e3a 100644
--- a/runtime/bin/eventhandler.h
+++ b/runtime/bin/eventhandler.h
@@ -10,6 +10,7 @@
 #include "bin/isolate_data.h"
 
 #include "platform/hashmap.h"
+#include "platform/priority_queue.h"
 
 namespace dart {
 namespace bin {
@@ -57,56 +58,39 @@
 // clang-format on
 
 class TimeoutQueue {
- private:
-  class Timeout {
-   public:
-    Timeout(Dart_Port port, int64_t timeout, Timeout* next)
-        : port_(port), timeout_(timeout), next_(next) {}
-
-    Dart_Port port() const { return port_; }
-
-    int64_t timeout() const { return timeout_; }
-    void set_timeout(int64_t timeout) {
-      ASSERT(timeout >= 0);
-      timeout_ = timeout;
-    }
-
-    Timeout* next() const { return next_; }
-    void set_next(Timeout* next) { next_ = next; }
-
-   private:
-    Dart_Port port_;
-    int64_t timeout_;
-    Timeout* next_;
-  };
-
  public:
-  TimeoutQueue() : next_timeout_(NULL), timeouts_(NULL) {}
+  TimeoutQueue() {}
 
   ~TimeoutQueue() {
     while (HasTimeout())
       RemoveCurrent();
   }
 
-  bool HasTimeout() const { return next_timeout_ != NULL; }
+  bool HasTimeout() const { return !timeouts_.IsEmpty(); }
 
   int64_t CurrentTimeout() const {
-    ASSERT(next_timeout_ != NULL);
-    return next_timeout_->timeout();
+    ASSERT(!timeouts_.IsEmpty());
+    return timeouts_.Minimum().priority;
   }
 
   Dart_Port CurrentPort() const {
-    ASSERT(next_timeout_ != NULL);
-    return next_timeout_->port();
+    ASSERT(!timeouts_.IsEmpty());
+    return timeouts_.Minimum().value;
   }
 
-  void RemoveCurrent() { UpdateTimeout(CurrentPort(), -1); }
+  void RemoveCurrent() { timeouts_.RemoveMinimum(); }
 
-  void UpdateTimeout(Dart_Port port, int64_t timeout);
+  void UpdateTimeout(Dart_Port port, int64_t timeout) {
+    if (timeout < 0) {
+      timeouts_.RemoveByValue(port);
+    } else {
+      timeouts_.InsertOrChangePriority(timeout, port);
+    }
+  }
 
  private:
-  Timeout* next_timeout_;
-  Timeout* timeouts_;
+  PriorityQueue<int64_t, Dart_Port> timeouts_;
+  int64_t next_timeout_;
 
   DISALLOW_COPY_AND_ASSIGN(TimeoutQueue);
 };
@@ -399,7 +383,7 @@
     intptr_t is_reading;
     intptr_t token_count;
 
-    bool IsReady() { return token_count > 0 && is_reading; }
+    bool IsReady() { return token_count > 0 && is_reading != 0; }
   };
 
  public:
diff --git a/runtime/bin/priority_heap_test.cc b/runtime/bin/priority_heap_test.cc
new file mode 100644
index 0000000..165f1a4
--- /dev/null
+++ b/runtime/bin/priority_heap_test.cc
@@ -0,0 +1,167 @@
+// Copyright (c) 2020, 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.md file.
+
+#include "vm/unit_test.h"
+
+#include "platform/priority_queue.h"
+
+namespace dart {
+
+UNIT_TEST_CASE(PRIORITY_HEAP_WITH_INDEX__INCREASING) {
+  const word kSize = PriorityQueue<word, word>::kMinimumSize;
+
+  PriorityQueue<word, word> heap;
+  for (word i = 0; i < kSize; i++) {
+    heap.Insert(i, 10 + i);
+  }
+  ASSERT(heap.min_heap_size() == kSize);
+  for (word i = 0; i < kSize; i++) {
+    EXPECT(!heap.IsEmpty());
+    EXPECT_EQ(i, heap.Minimum().priority);
+    EXPECT_EQ(10 + i, heap.Minimum().value);
+    EXPECT(heap.ContainsValue(10 + i));
+    heap.RemoveMinimum();
+    EXPECT(!heap.ContainsValue(10 + i));
+  }
+  EXPECT(heap.IsEmpty());
+}
+
+UNIT_TEST_CASE(PRIORITY_HEAP_WITH_INDEX__DECREASING) {
+  const word kSize = PriorityQueue<word, word>::kMinimumSize;
+
+  PriorityQueue<word, word> heap;
+  for (word i = kSize - 1; i >= 0; i--) {
+    heap.Insert(i, 10 + i);
+  }
+  ASSERT(heap.min_heap_size() == kSize);
+  for (word i = 0; i < kSize; i++) {
+    EXPECT(!heap.IsEmpty());
+    EXPECT_EQ(i, heap.Minimum().priority);
+    EXPECT_EQ(10 + i, heap.Minimum().value);
+    EXPECT(heap.ContainsValue(10 + i));
+    heap.RemoveMinimum();
+    EXPECT(!heap.ContainsValue(10 + i));
+  }
+  EXPECT(heap.IsEmpty());
+}
+
+UNIT_TEST_CASE(PRIORITY_HEAP_WITH_INDEX__DELETE_BY_VALUES) {
+  const word kSize = PriorityQueue<word, word>::kMinimumSize;
+
+  PriorityQueue<word, word> heap;
+  for (word i = kSize - 1; i >= 0; i--) {
+    heap.Insert(i, 10 + i);
+  }
+
+  ASSERT(heap.min_heap_size() == kSize);
+
+  EXPECT(heap.RemoveByValue(10 + 0));
+  EXPECT(!heap.RemoveByValue(10 + 0));
+
+  EXPECT(heap.RemoveByValue(10 + 5));
+  EXPECT(!heap.RemoveByValue(10 + 5));
+
+  EXPECT(heap.RemoveByValue(10 + kSize - 1));
+  EXPECT(!heap.RemoveByValue(10 + kSize - 1));
+
+  for (word i = 0; i < kSize; i++) {
+    // Jump over the removed [i]s in the loop.
+    if (i != 0 && i != 5 && i != (kSize - 1)) {
+      EXPECT(!heap.IsEmpty());
+      EXPECT_EQ(i, heap.Minimum().priority);
+      EXPECT_EQ(10 + i, heap.Minimum().value);
+      EXPECT(heap.ContainsValue(10 + i));
+      heap.RemoveMinimum();
+      EXPECT(!heap.ContainsValue(10 + i));
+    }
+  }
+  EXPECT(heap.IsEmpty());
+}
+
+UNIT_TEST_CASE(PRIORITY_HEAP_WITH_INDEX__GROW_SHRINK) {
+  const word kSize = 1024;
+  const word kMinimumSize = PriorityQueue<word, word>::kMinimumSize;
+
+  PriorityQueue<word, word> heap;
+  for (word i = 0; i < kSize; i++) {
+    heap.Insert(i, 10 + i);
+  }
+
+  ASSERT(heap.min_heap_size() == kSize);
+
+  for (word i = 0; i < kSize; i++) {
+    EXPECT(!heap.IsEmpty());
+    EXPECT_EQ(i, heap.Minimum().priority);
+    EXPECT_EQ(10 + i, heap.Minimum().value);
+    EXPECT(heap.ContainsValue(10 + i));
+    heap.RemoveMinimum();
+    EXPECT(!heap.ContainsValue(10 + i));
+  }
+
+  EXPECT(heap.IsEmpty());
+  ASSERT(heap.min_heap_size() == kMinimumSize);
+
+  for (word i = 0; i < kSize; i++) {
+    heap.Insert(i, 10 + i);
+  }
+
+  for (word i = 0; i < kSize; i++) {
+    EXPECT(!heap.IsEmpty());
+    EXPECT_EQ(i, heap.Minimum().priority);
+    EXPECT_EQ(10 + i, heap.Minimum().value);
+    EXPECT(heap.ContainsValue(10 + i));
+    heap.RemoveMinimum();
+    EXPECT(!heap.ContainsValue(10 + i));
+  }
+
+  EXPECT(heap.IsEmpty());
+  ASSERT(heap.min_heap_size() == kMinimumSize);
+}
+
+UNIT_TEST_CASE(PRIORITY_HEAP_WITH_INDEX__CHANGE_PRIORITY) {
+  const word kSize = PriorityQueue<word, word>::kMinimumSize;
+
+  PriorityQueue<word, word> heap;
+  for (word i = 0; i < kSize; i++) {
+    if (i % 2 == 0) {
+      heap.Insert(i, 10 + i);
+    }
+  }
+  ASSERT(heap.min_heap_size() == kSize);
+  for (word i = 0; i < kSize; i++) {
+    bool was_inserted = i % 2 == 0;
+    bool increase = i % 3 == 0;
+    word new_priority = i + (increase ? 100 : -100);
+
+    EXPECT(was_inserted != heap.InsertOrChangePriority(new_priority, 10 + i));
+  }
+
+  for (word i = 0; i < kSize; i++) {
+    bool increase = i % 3 == 0;
+    if (!increase) {
+      word expected_priority = i + (increase ? 100 : -100);
+      EXPECT(!heap.IsEmpty());
+      EXPECT_EQ(expected_priority, heap.Minimum().priority);
+      EXPECT_EQ(10 + i, heap.Minimum().value);
+      EXPECT(heap.ContainsValue(10 + i));
+      heap.RemoveMinimum();
+      EXPECT(!heap.ContainsValue(10 + i));
+    }
+  }
+  for (word i = 0; i < kSize; i++) {
+    bool increase = i % 3 == 0;
+    if (increase) {
+      word expected_priority = i + (increase ? 100 : -100);
+      EXPECT(!heap.IsEmpty());
+      EXPECT_EQ(expected_priority, heap.Minimum().priority);
+      EXPECT_EQ(10 + i, heap.Minimum().value);
+      EXPECT(heap.ContainsValue(10 + i));
+      heap.RemoveMinimum();
+      EXPECT(!heap.ContainsValue(10 + i));
+    }
+  }
+  EXPECT(heap.IsEmpty());
+}
+
+}  // namespace dart.
diff --git a/runtime/lib/errors.cc b/runtime/lib/errors.cc
index cce88cf..049e919 100644
--- a/runtime/lib/errors.cc
+++ b/runtime/lib/errors.cc
@@ -67,9 +67,9 @@
 DEFINE_NATIVE_ENTRY(AssertionError_throwNew, 0, 3) {
   // No need to type check the arguments. This function can only be called
   // internally from the VM.
-  const TokenPosition assertion_start = TokenPosition(
+  const TokenPosition assertion_start = TokenPosition::Deserialize(
       Smi::CheckedHandle(zone, arguments->NativeArgAt(0)).Value());
-  const TokenPosition assertion_end = TokenPosition(
+  const TokenPosition assertion_end = TokenPosition::Deserialize(
       Smi::CheckedHandle(zone, arguments->NativeArgAt(1)).Value());
 
   const Instance& message =
@@ -151,7 +151,7 @@
 DEFINE_NATIVE_ENTRY(TypeError_throwNew, 0, 4) {
   // No need to type check the arguments. This function can only be called
   // internally from the VM.
-  const TokenPosition location = TokenPosition(
+  const TokenPosition location = TokenPosition::Deserialize(
       Smi::CheckedHandle(zone, arguments->NativeArgAt(0)).Value());
   const Instance& src_value =
       Instance::CheckedHandle(zone, arguments->NativeArgAt(1));
@@ -171,7 +171,7 @@
 // Return value: none, throws an exception.
 DEFINE_NATIVE_ENTRY(FallThroughError_throwNew, 0, 1) {
   GET_NON_NULL_NATIVE_ARGUMENT(Smi, smi_pos, arguments->NativeArgAt(0));
-  TokenPosition fallthrough_pos = TokenPosition(smi_pos.Value());
+  TokenPosition fallthrough_pos = TokenPosition::Deserialize(smi_pos.Value());
 
   const Array& args = Array::Handle(Array::New(2));
 
@@ -197,7 +197,7 @@
 DEFINE_NATIVE_ENTRY(AbstractClassInstantiationError_throwNew, 0, 2) {
   GET_NON_NULL_NATIVE_ARGUMENT(Smi, smi_pos, arguments->NativeArgAt(0));
   GET_NON_NULL_NATIVE_ARGUMENT(String, class_name, arguments->NativeArgAt(1));
-  TokenPosition error_pos = TokenPosition(smi_pos.Value());
+  TokenPosition error_pos = TokenPosition::Deserialize(smi_pos.Value());
 
   const Array& args = Array::Handle(Array::New(3));
 
diff --git a/runtime/lib/object.cc b/runtime/lib/object.cc
index cf1e6ff..4e92dc7 100644
--- a/runtime/lib/object.cc
+++ b/runtime/lib/object.cc
@@ -447,7 +447,7 @@
     if (!AbstractType::InstantiateAndTestSubtype(
             &subtype, &supertype, instantiator_type_args, function_type_args)) {
       // Throw a dynamic type error.
-      TokenPosition location;
+      TokenPosition location = TokenPosition::kNoSource;
       {
         DartFrameIterator iterator(Thread::Current(),
                                    StackFrameIterator::kNoCrossThreadIteration);
diff --git a/runtime/observatory/tests/service/async_generator_breakpoint_test.dart b/runtime/observatory/tests/service/async_generator_breakpoint_test.dart
index 44f5c2a..b607ea8 100644
--- a/runtime/observatory/tests/service/async_generator_breakpoint_test.dart
+++ b/runtime/observatory/tests/service/async_generator_breakpoint_test.dart
@@ -53,15 +53,19 @@
   var script = isolate.rootLibrary.scripts[0];
 
   var bp1 = await isolate.addBreakpoint(script, 13);
+  print("BP1 - $bp1");
   expect(bp1, isNotNull);
   expect(bp1 is Breakpoint, isTrue);
   var bp2 = await isolate.addBreakpoint(script, 18);
+  print("BP2 - $bp2");
   expect(bp2, isNotNull);
   expect(bp2 is Breakpoint, isTrue);
   var bp3 = await isolate.addBreakpoint(script, 23);
+  print("BP3 - $bp3");
   expect(bp3, isNotNull);
   expect(bp3 is Breakpoint, isTrue);
   var bp4 = await isolate.addBreakpoint(script, 27);
+  print("BP4 - $bp4");
   expect(bp4, isNotNull);
   expect(bp4 is Breakpoint, isTrue);
   var bp5 = await isolate.addBreakpoint(script, 44);
diff --git a/runtime/observatory/tests/service/service.status b/runtime/observatory/tests/service/service.status
index bc2bb42..5896d37 100644
--- a/runtime/observatory/tests/service/service.status
+++ b/runtime/observatory/tests/service/service.status
@@ -23,6 +23,10 @@
 developer_extension_test: SkipByDesign
 get_isolate_after_language_error_test: SkipByDesign
 
+# The _1 versions of this test can be slow due to stress testing flags.
+[ $mode == debug ]
+async_generator_breakpoint_test: Pass, Slow
+
 # Service protocol is not supported in product mode.
 [ $mode == product ]
 *: SkipByDesign
diff --git a/runtime/observatory_2/tests/service_2/async_generator_breakpoint_test.dart b/runtime/observatory_2/tests/service_2/async_generator_breakpoint_test.dart
index 1abcb9f..4f91673 100644
--- a/runtime/observatory_2/tests/service_2/async_generator_breakpoint_test.dart
+++ b/runtime/observatory_2/tests/service_2/async_generator_breakpoint_test.dart
@@ -53,15 +53,19 @@
   var script = isolate.rootLibrary.scripts[0];
 
   var bp1 = await isolate.addBreakpoint(script, 13);
+  print("BP1 - $bp1");
   expect(bp1, isNotNull);
   expect(bp1 is Breakpoint, isTrue);
   var bp2 = await isolate.addBreakpoint(script, 18);
+  print("BP2 - $bp2");
   expect(bp2, isNotNull);
   expect(bp2 is Breakpoint, isTrue);
   var bp3 = await isolate.addBreakpoint(script, 23);
+  print("BP3 - $bp3");
   expect(bp3, isNotNull);
   expect(bp3 is Breakpoint, isTrue);
   var bp4 = await isolate.addBreakpoint(script, 27);
+  print("BP4 - $bp4");
   expect(bp4, isNotNull);
   expect(bp4 is Breakpoint, isTrue);
   var bp5 = await isolate.addBreakpoint(script, 44);
diff --git a/runtime/observatory_2/tests/service_2/service_2.status b/runtime/observatory_2/tests/service_2/service_2.status
index c16feab..874b3f4 100644
--- a/runtime/observatory_2/tests/service_2/service_2.status
+++ b/runtime/observatory_2/tests/service_2/service_2.status
@@ -25,6 +25,10 @@
 developer_extension_test: SkipByDesign
 get_isolate_after_language_error_test: SkipByDesign
 
+# The _1 versions of this test can be slow due to stress testing flags.
+[ $mode == debug ]
+async_generator_breakpoint_test: Pass, Slow
+
 # Service protocol is not supported in product mode.
 [ $mode == product ]
 *: SkipByDesign
diff --git a/runtime/platform/priority_queue.h b/runtime/platform/priority_queue.h
new file mode 100644
index 0000000..2064fcc
--- /dev/null
+++ b/runtime/platform/priority_queue.h
@@ -0,0 +1,269 @@
+// Copyright (c) 2020, 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.
+
+#ifndef RUNTIME_PLATFORM_PRIORITY_QUEUE_H_
+#define RUNTIME_PLATFORM_PRIORITY_QUEUE_H_
+
+#include "platform/assert.h"
+#include "platform/globals.h"
+#include "platform/hashmap.h"
+#include "platform/utils.h"
+
+namespace dart {
+
+// A min-priority queue with deletion support.
+//
+// The [PriorityQueue] allows insertion of entries with a priority [P] and a
+// value [V]. The minimum element can be queried in O(1) time.
+// Insertion/Deletion operations have O(N) time.
+//
+// In addition to the normal insert/minimum/remove-minimum operations this
+// priority queue allows deletion-by-value. We have therefore an invariant
+// is that the value must be unique amongst all entries.
+template <typename P, typename V>
+class PriorityQueue {
+ public:
+  static const intptr_t kMinimumSize = 16;
+
+  struct Entry {
+    P priority;
+    V value;
+  };
+
+  PriorityQueue() : hashmap_(&MatchFun, kMinimumSize) {
+    min_heap_size_ = kMinimumSize;
+    min_heap_ =
+        reinterpret_cast<Entry*>(malloc(sizeof(Entry) * min_heap_size_));
+    if (min_heap_ == nullptr) FATAL("Cannot allocate memory.");
+    size_ = 0;
+  }
+
+  ~PriorityQueue() { free(min_heap_); }
+
+  // Whether the queue is empty.
+  bool IsEmpty() const { return size_ == 0; }
+
+  // Inserts a new entry with [priority] and [value], requires there to be no
+  // existing entry with given [value].
+  void Insert(const P& priority, const V& value) {
+    ASSERT(!ContainsValue(value));
+
+    if (size_ == min_heap_size_) {
+      Resize(min_heap_size_ << 1);
+    }
+
+    Set(size_, {priority, value});
+    BubbleUp(size_);
+
+    size_++;
+  }
+
+  // Returns a reference to the minimum entry.
+  //
+  // The caller can access it's priority and value in read-only mode only.
+  const Entry& Minimum() const {
+    ASSERT(!IsEmpty());
+    return min_heap_[0];
+  }
+
+  // Removes the minimum entry.
+  void RemoveMinimum() {
+    ASSERT(!IsEmpty());
+    RemoveAt(0);
+  }
+
+  // Removes an existing entry with the given [value].
+  //
+  // Returns true if such an entry was removed.
+  bool RemoveByValue(const V& value) {
+    auto entry = FindMapEntry(value);
+    if (entry != nullptr) {
+      const intptr_t offset = ValueOfMapEntry(entry);
+      RemoveAt(offset);
+
+      ASSERT(hashmap_.size() == size_);
+      return true;
+    }
+    return false;
+  }
+
+  // Whether the priority queue contains an entry with the given [value].
+  bool ContainsValue(const V& value) { return FindMapEntry(value) != nullptr; }
+
+  // Changes the priority of an existing entry with given [value] or adds a
+  // new entry.
+  bool InsertOrChangePriority(const P& priority, const V& value) {
+    auto map_entry = FindMapEntry(value);
+    if (map_entry == nullptr) {
+      Insert(priority, value);
+      return true;
+    }
+
+    const intptr_t offset = ValueOfMapEntry(map_entry);
+    ASSERT(offset < size_);
+
+    Entry& entry = min_heap_[offset];
+    entry.priority = priority;
+    if (offset == 0) {
+      BubbleDown(offset);
+    } else {
+      intptr_t parent = (offset - 1) / 2;
+      intptr_t diff = entry.priority - min_heap_[parent].priority;
+      if (diff < 0) {
+        BubbleUp(offset);
+      } else if (diff > 0) {
+        BubbleDown(offset);
+      }
+    }
+    return false;
+  }
+
+#ifdef TESTING
+  intptr_t min_heap_size() { return min_heap_size_; }
+#endif  // TESTING
+
+ private:
+  // Utility functions dealing with the SimpleHashMap interface.
+  static bool MatchFun(void* key1, void* key2) { return key1 == key2; }
+
+  SimpleHashMap::Entry* FindMapEntry(const V& key, bool insert = false) {
+    return hashmap_.Lookup(CastKey(key), HashKey(key), insert);
+  }
+  void RemoveMapEntry(const V& key) {
+    ASSERT(FindMapEntry(key) != nullptr);
+    hashmap_.Remove(CastKey(key), HashKey(key));
+  }
+  void SetMapEntry(const V& key, intptr_t value) {
+    FindMapEntry(key, /*insert=*/true)->value = reinterpret_cast<void*>(value);
+  }
+  static uint32_t HashKey(const V& key) {
+    return static_cast<uint32_t>(reinterpret_cast<intptr_t>(CastKey(key)));
+  }
+  static intptr_t ValueOfMapEntry(SimpleHashMap::Entry* entry) {
+    return reinterpret_cast<intptr_t>(entry->value);
+  }
+  static void* CastKey(const V& key) {
+    return reinterpret_cast<void*>((const_cast<V&>(key)));
+  }
+
+  void RemoveAt(intptr_t offset) {
+    ASSERT(offset < size_);
+
+    size_--;
+
+    if (offset == size_) {
+      RemoveMapEntry(min_heap_[offset].value);
+    } else {
+      Replace(offset, size_);
+      BubbleDown(offset);
+    }
+
+    if (size_ <= (min_heap_size_ >> 2) &&
+        kMinimumSize <= (min_heap_size_ >> 1)) {
+      Resize(min_heap_size_ >> 1);
+    }
+  }
+
+  void BubbleUp(intptr_t offset) {
+    while (true) {
+      if (offset == 0) return;
+
+      intptr_t parent = (offset - 1) / 2;
+      if (min_heap_[parent].priority > min_heap_[offset].priority) {
+        Swap(parent, offset);
+      }
+      offset = parent;
+    }
+  }
+
+  void BubbleDown(intptr_t offset) {
+    while (true) {
+      intptr_t left_child_index = 2 * offset + 1;
+      bool has_left_child = left_child_index < size_;
+
+      if (!has_left_child) return;
+
+      intptr_t smallest_index = offset;
+
+      if (min_heap_[left_child_index].priority < min_heap_[offset].priority) {
+        smallest_index = left_child_index;
+      }
+
+      intptr_t right_child_index = left_child_index + 1;
+      bool has_right_child = right_child_index < size_;
+      if (has_right_child) {
+        if (min_heap_[right_child_index].priority <
+            min_heap_[smallest_index].priority) {
+          smallest_index = right_child_index;
+        }
+      }
+
+      if (offset == smallest_index) {
+        return;
+      }
+
+      Swap(offset, smallest_index);
+      offset = smallest_index;
+    }
+  }
+
+  void Set(intptr_t offset1, const Entry& entry) {
+    min_heap_[offset1] = entry;
+    SetMapEntry(entry.value, offset1);
+  }
+
+  void Swap(intptr_t offset1, intptr_t offset2) {
+    Entry temp = min_heap_[offset1];
+    min_heap_[offset1] = min_heap_[offset2];
+    min_heap_[offset2] = temp;
+
+    SetMapEntry(min_heap_[offset1].value, offset1);
+    SetMapEntry(min_heap_[offset2].value, offset2);
+  }
+
+  void Replace(intptr_t index, intptr_t with_other) {
+    RemoveMapEntry(min_heap_[index].value);
+
+    const Entry& entry = min_heap_[with_other];
+    SetMapEntry(entry.value, index);
+    min_heap_[index] = entry;
+  }
+
+  void Resize(intptr_t new_min_heap_size) {
+    ASSERT(size_ < new_min_heap_size);
+    ASSERT(new_min_heap_size != min_heap_size_);
+
+    Entry* new_backing = reinterpret_cast<Entry*>(
+        realloc(min_heap_, sizeof(Entry) * new_min_heap_size));
+
+    if (new_backing == NULL) FATAL("Cannot allocate memory.");
+
+    min_heap_ = new_backing;
+    min_heap_size_ = new_min_heap_size;
+  }
+
+  // The array is representing a tree structure with guaranteed log(n) height.
+  // It has the property that the value of node N is always equal or smaller
+  // than the value of N's children. Furthermore it is a "dense" tree in the
+  // sense that all rows/layers of the tree are fully occupied except the last
+  // one. The way to represent such "dense" trees is via an array that allows
+  // finding left/right children by <2*index+1><2*index+2> and the parent by
+  // <(index-1)/2>.
+  //
+  // Insertion operations can be performed by adding one more entry at the end
+  // (bottom right) and bubbling it up until the tree invariant is satisfied
+  // again.
+  //
+  // Deletion operations can be performed by replacing the minimum element
+  // (first entry) by the last entry (bottom right) and bubbling it down until
+  // the tree invariant is satisified again.
+  Entry* min_heap_;
+  intptr_t min_heap_size_;
+  intptr_t size_;
+  SimpleHashMap hashmap_;
+};
+
+}  // namespace dart
+
+#endif  // RUNTIME_PLATFORM_PRIORITY_QUEUE_H_
diff --git a/runtime/vm/clustered_snapshot.h b/runtime/vm/clustered_snapshot.h
index 031ab05..2b2ab85 100644
--- a/runtime/vm/clustered_snapshot.h
+++ b/runtime/vm/clustered_snapshot.h
@@ -340,9 +340,7 @@
     }
   }
 
-  void WriteTokenPosition(TokenPosition pos) {
-    Write<int32_t>(pos.SnapshotEncode());
-  }
+  void WriteTokenPosition(TokenPosition pos) { Write(pos.Serialize()); }
 
   void WriteCid(intptr_t cid) {
     COMPILE_ASSERT(ObjectLayout::kClassIdTagSize <= 32);
@@ -616,7 +614,7 @@
   }
 
   TokenPosition ReadTokenPosition() {
-    return TokenPosition::SnapshotDecode(Read<int32_t>());
+    return TokenPosition::Deserialize(Read<int32_t>());
   }
 
   intptr_t ReadCid() {
diff --git a/runtime/vm/code_descriptors.cc b/runtime/vm/code_descriptors.cc
index edce815..e75d702 100644
--- a/runtime/vm/code_descriptors.cc
+++ b/runtime/vm/code_descriptors.cc
@@ -40,10 +40,11 @@
     prev_pc_offset = pc_offset;
 
     if (!FLAG_precompiled_mode) {
+      const int32_t encoded_pos = token_pos.Serialize();
       encoded_data_.WriteSLEB128(deopt_id - prev_deopt_id);
-      encoded_data_.WriteSLEB128(token_pos.value() - prev_token_pos);
+      encoded_data_.WriteSLEB128(encoded_pos - prev_token_pos);
       prev_deopt_id = deopt_id;
-      prev_token_pos = token_pos.value();
+      prev_token_pos = encoded_pos;
     }
   }
 }
@@ -197,8 +198,8 @@
 }
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
-const TokenPosition CodeSourceMapBuilder::kInitialPosition =
-    TokenPosition(TokenPosition::kDartCodeProloguePos);
+const TokenPosition& CodeSourceMapBuilder::kInitialPosition =
+    TokenPosition::kDartCodePrologue;
 
 CodeSourceMapBuilder::CodeSourceMapBuilder(
     Zone* zone,
@@ -311,7 +312,7 @@
   }
   for (intptr_t i = to_push.length() - 1; i >= 0; i--) {
     intptr_t callee_id = to_push[i];
-    TokenPosition call_token;
+    TokenPosition call_token = TokenPosition::kNoSource;
     if (callee_id != 0) {
       // TODO(rmacnak): Should make this array line up with the others.
       call_token = inline_id_to_token_pos_[callee_id - 1];
@@ -396,20 +397,19 @@
 
 void CodeSourceMapBuilder::WriteChangePosition(TokenPosition pos) {
   stream_.Write<uint8_t>(kChangePosition);
-  intptr_t position_or_line = pos.value();
+  intptr_t position_or_line = pos.Serialize();
 #if defined(DART_PRECOMPILER)
-  intptr_t column = TokenPosition::kNoSourcePos;
+  intptr_t column = TokenPosition::kNoSource.Serialize();
   if (FLAG_precompiled_mode) {
     // Don't use the raw position value directly in precompiled mode. Instead,
     // use the value of kNoSource as a fallback when no line or column
     // information is found.
-    position_or_line = TokenPosition::kNoSourcePos;
+    position_or_line = TokenPosition::kNoSource.Serialize();
     intptr_t inline_id = buffered_inline_id_stack_.Last();
     if (inline_id < inline_id_to_function_.length()) {
       const Function* function = inline_id_to_function_[inline_id];
       Script& script = Script::Handle(function->script());
-      script.GetTokenLocationUsingLineStarts(pos.SourcePosition(),
-                                             &position_or_line, &column);
+      script.GetTokenLocation(pos, &position_or_line, &column);
     }
   }
 #endif
@@ -619,8 +619,8 @@
         THR_Print("%" Px "-%" Px ": ", start + current_pc_offset,
                   start + current_pc_offset + delta - 1);
         for (intptr_t i = 0; i < function_stack.length(); i++) {
-          THR_Print("%s@%" Pd " ", function_stack[i]->ToCString(),
-                    token_positions[i].value());
+          THR_Print("%s@%s", function_stack[i]->ToCString(),
+                    token_positions[i].ToCString());
         }
         THR_Print("\n");
         current_pc_offset += delta;
@@ -698,7 +698,8 @@
 }
 
 TokenPosition CodeSourceMapReader::ReadPosition(ReadStream* stream) {
-  const intptr_t line = stream->Read<int32_t>();
+  const TokenPosition line =
+      TokenPosition::Deserialize(stream->Read<int32_t>());
 #if defined(DART_PRECOMPILER)
   // The special handling for non-symbolic stack trace mode only needs to
   // happen in the precompiler, because those CSMs are not serialized in
@@ -707,7 +708,7 @@
     stream->Read<int32_t>();  // Discard the column information.
   }
 #endif
-  return TokenPosition(line);
+  return line;
 }
 
 }  // namespace dart
diff --git a/runtime/vm/code_descriptors.h b/runtime/vm/code_descriptors.h
index f42190b..bb2f773 100644
--- a/runtime/vm/code_descriptors.h
+++ b/runtime/vm/code_descriptors.h
@@ -42,7 +42,7 @@
 
   intptr_t prev_pc_offset;
   intptr_t prev_deopt_id;
-  intptr_t prev_token_pos;
+  int32_t prev_token_pos;
 
   DISALLOW_COPY_AND_ASSIGN(DescriptorList);
 };
@@ -186,7 +186,7 @@
   // The position at which a function implicitly starts, for both the root and
   // after a push bytecode. We use the classifying position kDartCodePrologue
   // since it is the most common.
-  static const TokenPosition kInitialPosition;
+  static const TokenPosition& kInitialPosition;
 
   static const uint8_t kChangePosition = 0;
   static const uint8_t kAdvancePC = 1;
diff --git a/runtime/vm/code_descriptors_test.cc b/runtime/vm/code_descriptors_test.cc
index d8e2c41..1f5cb52 100644
--- a/runtime/vm/code_descriptors_test.cc
+++ b/runtime/vm/code_descriptors_test.cc
@@ -123,7 +123,7 @@
 ISOLATE_UNIT_TEST_CASE(DescriptorList_TokenPositions) {
   DescriptorList* descriptors = new DescriptorList(thread->zone());
   ASSERT(descriptors != NULL);
-  const intptr_t token_positions[] = {
+  const int32_t token_positions[] = {
       kMinInt32,
       5,
       13,
@@ -141,12 +141,12 @@
       TokenPosition::kMinSourcePos,
       TokenPosition::kMaxSourcePos,
   };
-  const intptr_t num_token_positions =
-      sizeof(token_positions) / sizeof(token_positions[0]);
+  const intptr_t num_token_positions = ARRAY_SIZE(token_positions);
 
   for (intptr_t i = 0; i < num_token_positions; i++) {
-    descriptors->AddDescriptor(PcDescriptorsLayout::kRuntimeCall, 0, 0,
-                               TokenPosition(token_positions[i]), 0, 1);
+    const TokenPosition& tp = TokenPosition::Deserialize(token_positions[i]);
+    descriptors->AddDescriptor(PcDescriptorsLayout::kRuntimeCall, 0, 0, tp, 0,
+                               1);
   }
 
   const PcDescriptors& finalized_descriptors =
@@ -158,11 +158,12 @@
 
   intptr_t i = 0;
   while (it.MoveNext()) {
-    if (token_positions[i] != it.TokenPos().value()) {
-      OS::PrintErr("[%" Pd "]: Expected: %" Pd " != %" Pd "\n", i,
-                   token_positions[i], it.TokenPos().value());
+    const TokenPosition& tp = TokenPosition::Deserialize(token_positions[i]);
+    if (tp != it.TokenPos()) {
+      OS::PrintErr("[%" Pd "]: Expected: %s != %s\n", i, tp.ToCString(),
+                   it.TokenPos().ToCString());
     }
-    EXPECT(token_positions[i] == it.TokenPos().value());
+    EXPECT(tp == it.TokenPos());
     i++;
   }
 }
diff --git a/runtime/vm/compilation_trace.cc b/runtime/vm/compilation_trace.cc
index d2f88ed..8b1a53b 100644
--- a/runtime/vm/compilation_trace.cc
+++ b/runtime/vm/compilation_trace.cc
@@ -516,7 +516,7 @@
   WriteString(str_);
 
   WriteInt(function.kind());
-  WriteInt(function.token_pos().value());
+  WriteInt(function.token_pos().Serialize());
 
   code_ = function.CurrentCode();
   intptr_t usage = function.usage_counter();
@@ -814,7 +814,7 @@
 
   func_name_ = ReadString();  // Without private mangling.
   FunctionLayout::Kind kind = static_cast<FunctionLayout::Kind>(ReadInt());
-  intptr_t token_pos = ReadInt();
+  const TokenPosition& token_pos = TokenPosition::Deserialize(ReadInt());
   intptr_t usage = ReadInt();
   intptr_t inlining_depth = ReadInt();
   intptr_t num_call_sites = ReadInt();
@@ -926,7 +926,7 @@
 }
 
 FunctionPtr TypeFeedbackLoader::FindFunction(FunctionLayout::Kind kind,
-                                             intptr_t token_pos) {
+                                             const TokenPosition& token_pos) {
   if (cls_name_.Equals(Symbols::TopLevel())) {
     func_ = lib_.LookupFunctionAllowPrivate(func_name_);
   } else {
@@ -969,8 +969,7 @@
     bool found = false;
     for (intptr_t i = 0; i < closure_functions.Length(); i++) {
       func_ ^= closure_functions.At(i);
-      if ((func_.Owner() == cls_.raw()) &&
-          (func_.token_pos().value() == token_pos)) {
+      if (func_.Owner() == cls_.raw() && func_.token_pos() == token_pos) {
         found = true;
         break;
       }
diff --git a/runtime/vm/compilation_trace.h b/runtime/vm/compilation_trace.h
index 80d671e..459ca27 100644
--- a/runtime/vm/compilation_trace.h
+++ b/runtime/vm/compilation_trace.h
@@ -102,7 +102,8 @@
   ObjectPtr LoadClasses();
   ObjectPtr LoadFields();
   ObjectPtr LoadFunction();
-  FunctionPtr FindFunction(FunctionLayout::Kind kind, intptr_t token_pos);
+  FunctionPtr FindFunction(FunctionLayout::Kind kind,
+                           const TokenPosition& token_pos);
 
   ClassPtr ReadClassByName();
   StringPtr ReadString();
diff --git a/runtime/vm/compiler/aot/precompiler.h b/runtime/vm/compiler/aot/precompiler.h
index 41a7902..a1da918 100644
--- a/runtime/vm/compiler/aot/precompiler.h
+++ b/runtime/vm/compiler/aot/precompiler.h
@@ -94,7 +94,7 @@
   static inline intptr_t Hashcode(Key key) {
     const TokenPosition token_pos = key->token_pos();
     if (token_pos.IsReal()) {
-      return token_pos.value();
+      return token_pos.Hash();
     }
     return key->kernel_offset();
   }
@@ -117,7 +117,7 @@
 
   static Value ValueOf(Pair kv) { return kv; }
 
-  static inline intptr_t Hashcode(Key key) { return key->token_pos().value(); }
+  static inline intptr_t Hashcode(Key key) { return key->token_pos().Hash(); }
 
   static inline bool IsKeyEqual(Pair pair, Key key) {
     return pair->raw() == key->raw();
diff --git a/runtime/vm/compiler/assembler/disassembler.cc b/runtime/vm/compiler/assembler/disassembler.cc
index e7e73a5..8df2bfc 100644
--- a/runtime/vm/compiler/assembler/disassembler.cc
+++ b/runtime/vm/compiler/assembler/disassembler.cc
@@ -339,7 +339,8 @@
   THR_Print("%s}\n", handlers.ToCString());
 
 #if defined(DART_PRECOMPILED_RUNTIME) || defined(DART_PRECOMPILER)
-  if (code.catch_entry_moves_maps() != Object::null()) {
+  if (FLAG_precompiled_mode &&
+      code.catch_entry_moves_maps() != Object::null()) {
     THR_Print("Catch entry moves for function '%s' {\n", function_fullname);
     CatchEntryMovesMapReader reader(
         TypedData::Handle(code.catch_entry_moves_maps()));
diff --git a/runtime/vm/compiler/backend/il_deserializer.cc b/runtime/vm/compiler/backend/il_deserializer.cc
index 6c30d95..fec331a 100644
--- a/runtime/vm/compiler/backend/il_deserializer.cc
+++ b/runtime/vm/compiler/backend/il_deserializer.cc
@@ -768,7 +768,7 @@
   TokenPosition token_pos = TokenPosition::kNoSource;
   if (auto const token_int =
           CheckInteger(list->ExtraLookupValue("token_pos"))) {
-    token_pos = TokenPosition(token_int->value());
+    token_pos = TokenPosition::Deserialize(token_int->value());
   }
   InstrInfo common_info = {deopt_id, token_pos};
 
@@ -1833,7 +1833,7 @@
   }
   TokenPosition token_pos = TokenPosition::kNoSource;
   if (const auto pos_sexp = CheckInteger(list->ExtraLookupValue("token_pos"))) {
-    token_pos = TokenPosition(pos_sexp->value());
+    token_pos = TokenPosition::Deserialize(pos_sexp->value());
   }
   auto type_args_ptr = &Object::null_type_arguments();
   if (const auto ta_sexp = list->ExtraLookupValue("type_args")) {
diff --git a/runtime/vm/compiler/backend/il_serializer.cc b/runtime/vm/compiler/backend/il_serializer.cc
index 5e6fc92..979c410 100644
--- a/runtime/vm/compiler/backend/il_serializer.cc
+++ b/runtime/vm/compiler/backend/il_serializer.cc
@@ -437,7 +437,7 @@
   AddSymbol(sexp, "Type");
   const auto& type = Type::Cast(t);
   if (!type.token_pos().IsNoSource()) {
-    AddExtraInteger(sexp, "token_pos", type.token_pos().value());
+    AddExtraInteger(sexp, "token_pos", type.token_pos().Serialize());
   }
   // We want to check for the type being recursive before we may serialize
   // any sub-parts that include possible TypeRefs to this type.
@@ -823,7 +823,7 @@
     sexp->AddExtra("env", env()->ToSExpression(s));
   }
   if (!token_pos().IsNoSource()) {
-    s->AddExtraInteger(sexp, "token_pos", token_pos().value());
+    s->AddExtraInteger(sexp, "token_pos", token_pos().Serialize());
   }
 }
 
diff --git a/runtime/vm/compiler/backend/redundancy_elimination.cc b/runtime/vm/compiler/backend/redundancy_elimination.cc
index 269a793..c682aef 100644
--- a/runtime/vm/compiler/backend/redundancy_elimination.cc
+++ b/runtime/vm/compiler/backend/redundancy_elimination.cc
@@ -1692,9 +1692,7 @@
 
     // For now, bail out for large functions to avoid OOM situations.
     // TODO(fschneider): Fix the memory consumption issue.
-    intptr_t function_length = graph->function().end_token_pos().Pos() -
-                               graph->function().token_pos().Pos();
-    if (function_length >= FLAG_huge_method_cutoff_in_tokens) {
+    if (graph->function().SourceSize() >= FLAG_huge_method_cutoff_in_tokens) {
       return false;
     }
 
@@ -2850,9 +2848,7 @@
 
     // For now, bail out for large functions to avoid OOM situations.
     // TODO(fschneider): Fix the memory consumption issue.
-    intptr_t function_length = graph->function().end_token_pos().Pos() -
-                               graph->function().token_pos().Pos();
-    if (function_length >= FLAG_huge_method_cutoff_in_tokens) {
+    if (graph->function().SourceSize() >= FLAG_huge_method_cutoff_in_tokens) {
       return;
     }
 
diff --git a/runtime/vm/compiler/backend/yield_position_test.cc b/runtime/vm/compiler/backend/yield_position_test.cc
index a32bb74..e90be85 100644
--- a/runtime/vm/compiler/backend/yield_position_test.cc
+++ b/runtime/vm/compiler/backend/yield_position_test.cc
@@ -103,11 +103,11 @@
     EXPECT_EQ(3, yield_points.length());
 
     EXPECT_EQ(1, yield_points[0].first);
-    EXPECT_EQ(88, yield_points[0].second.value());
+    EXPECT_EQ(88, yield_points[0].second.Pos());
     EXPECT_EQ(2, yield_points[1].first);
-    EXPECT_EQ(129, yield_points[1].second.value());
+    EXPECT_EQ(129, yield_points[1].second.Pos());
     EXPECT_EQ(3, yield_points[2].first);
-    EXPECT_EQ(170, yield_points[2].second.value());
+    EXPECT_EQ(170, yield_points[2].second.Pos());
   };
 
   validate_indices(*GetYieldPointsFromGraph(flow_graph));
diff --git a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
index 7b3e6a0..7378aa7 100644
--- a/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
+++ b/runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
@@ -4839,8 +4839,9 @@
 
   // Use position of equal sign if it exists. If the equal sign does not exist
   // use the position of the identifier.
-  TokenPosition debug_position =
-      Utils::Maximum(helper.position_, helper.equals_position_);
+  const TokenPosition debug_position = helper.equals_position_.IsReal()
+                                           ? helper.equals_position_
+                                           : helper.position_;
   if (NeedsDebugStepCheck(stack(), debug_position)) {
     instructions = DebugStepCheck(debug_position) + instructions;
   }
@@ -4882,7 +4883,7 @@
     // Positions has to be unique in regards to the parent.
     // A non-real at this point is probably -1, we cannot blindly use that
     // as others might use it too. Create a new dummy non-real TokenPosition.
-    position = TokenPosition(offset).ToSynthetic();
+    position = TokenPosition::Synthetic(offset);
   }
 
   // The VM has a per-isolate table of functions indexed by the enclosing
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.h b/runtime/vm/compiler/frontend/kernel_translation_helper.h
index 36c8a5e..72b3106 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.h
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.h
@@ -296,8 +296,8 @@
   void SetNext(Field field) { next_read_ = field; }
   void SetJustRead(Field field) { next_read_ = field + 1; }
 
-  TokenPosition position_;
-  TokenPosition end_position_;
+  TokenPosition position_ = TokenPosition::kNoSource;
+  TokenPosition end_position_ = TokenPosition::kNoSource;
   AsyncMarker async_marker_;
   AsyncMarker dart_async_marker_;
   intptr_t total_parameter_count_ = 0;
@@ -352,7 +352,7 @@
     return (flags_ & kIsGenericCovariantImpl) != 0;
   }
 
-  TokenPosition position_;
+  TokenPosition position_ = TokenPosition::kNoSource;
   uint8_t flags_ = 0;
   StringIndex name_index_;
 
@@ -415,8 +415,8 @@
     return (flags_ & kIsGenericCovariantImpl) != 0;
   }
 
-  TokenPosition position_;
-  TokenPosition equals_position_;
+  TokenPosition position_ = TokenPosition::kNoSource;
+  TokenPosition equals_position_ = TokenPosition::kNoSource;
   uint8_t flags_ = 0;
   StringIndex name_index_;
   intptr_t annotation_count_ = 0;
@@ -488,8 +488,8 @@
 
   NameIndex canonical_name_getter_;
   NameIndex canonical_name_setter_;
-  TokenPosition position_;
-  TokenPosition end_position_;
+  TokenPosition position_ = TokenPosition::kNoSource;
+  TokenPosition end_position_ = TokenPosition::kNoSource;
   uint32_t flags_ = 0;
   intptr_t source_uri_index_ = 0;
   intptr_t annotation_count_ = 0;
@@ -589,9 +589,9 @@
   }
 
   NameIndex canonical_name_;
-  TokenPosition start_position_;
-  TokenPosition position_;
-  TokenPosition end_position_;
+  TokenPosition start_position_ = TokenPosition::kNoSource;
+  TokenPosition position_ = TokenPosition::kNoSource;
+  TokenPosition end_position_ = TokenPosition::kNoSource;
   Kind kind_;
   uint32_t flags_ = 0;
   intptr_t source_uri_index_ = 0;
@@ -655,9 +655,9 @@
   bool IsSynthetic() { return (flags_ & kSynthetic) != 0; }
 
   NameIndex canonical_name_;
-  TokenPosition start_position_;
-  TokenPosition position_;
-  TokenPosition end_position_;
+  TokenPosition start_position_ = TokenPosition::kNoSource;
+  TokenPosition position_ = TokenPosition::kNoSource;
+  TokenPosition end_position_ = TokenPosition::kNoSource;
   uint8_t flags_ = 0;
   intptr_t source_uri_index_ = 0;
   intptr_t annotation_count_ = 0;
@@ -733,9 +733,9 @@
   }
 
   NameIndex canonical_name_;
-  TokenPosition start_position_;
-  TokenPosition position_;
-  TokenPosition end_position_;
+  TokenPosition start_position_ = TokenPosition::kNoSource;
+  TokenPosition position_ = TokenPosition::kNoSource;
+  TokenPosition end_position_ = TokenPosition::kNoSource;
   StringIndex name_index_;
   intptr_t source_uri_index_ = 0;
   intptr_t annotation_count_ = 0;
diff --git a/runtime/vm/compiler/frontend/scope_builder.h b/runtime/vm/compiler/frontend/scope_builder.h
index c8680c3..1531508 100644
--- a/runtime/vm/compiler/frontend/scope_builder.h
+++ b/runtime/vm/compiler/frontend/scope_builder.h
@@ -162,7 +162,7 @@
   intptr_t name_index_;
 
   bool needs_expr_temp_;
-  TokenPosition first_body_token_position_;
+  TokenPosition first_body_token_position_ = TokenPosition::kNoSource;
 
   KernelReaderHelper helper_;
   ConstantReader constant_reader_;
diff --git a/runtime/vm/compiler/jit/compiler.cc b/runtime/vm/compiler/jit/compiler.cc
index d88ab4d..37606511 100644
--- a/runtime/vm/compiler/jit/compiler.cc
+++ b/runtime/vm/compiler/jit/compiler.cc
@@ -724,8 +724,7 @@
     ParsedFunction* parsed_function = new (zone)
         ParsedFunction(thread, Function::ZoneHandle(zone, function.raw()));
     if (trace_compiler) {
-      const intptr_t token_size =
-          function.end_token_pos().Pos() - function.token_pos().Pos();
+      const intptr_t token_size = function.SourceSize();
       THR_Print("Compiling %s%sfunction %s: '%s' @ token %s, size %" Pd "\n",
                 (osr_id == Compiler::kNoOSRDeoptId ? "" : "osr "),
                 (optimized ? "optimized " : ""),
@@ -1300,9 +1299,9 @@
 
 DEFINE_RUNTIME_ENTRY(CompileFunction, 1) {
   const Function& function = Function::CheckedHandle(zone, arguments.ArgAt(0));
-  FATAL3("Precompilation missed function %s (%" Pd ", %s)\n",
+  FATAL3("Precompilation missed function %s (%s, %s)\n",
          function.ToLibNamePrefixedQualifiedCString(),
-         function.token_pos().value(),
+         function.token_pos().ToCString(),
          Function::KindToCString(function.kind()));
 }
 
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index d1096d7..6120b67 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -109,8 +109,7 @@
                                      TokenPosition token_pos) {
   ASSERT(!IsLatent());
   ASSERT(func.script() == script_);
-  ASSERT((func.token_pos() <= token_pos) &&
-         (token_pos <= func.end_token_pos()));
+  ASSERT(token_pos.IsWithin(func.token_pos(), func.end_token_pos()));
   ASSERT(func.is_debuggable());
   function_ = func.raw();
   token_pos_ = token_pos;
@@ -394,11 +393,10 @@
                              const Script& script,
                              TokenPosition token_pos,
                              TokenPosition end_token_pos) {
-  TokenPosition func_start = func.token_pos();
-  if (((func_start <= token_pos) && (token_pos <= func.end_token_pos())) ||
-      ((token_pos <= func_start) && (func_start <= end_token_pos))) {
-    // Check script equality second because it allocates
-    // handles as a side effect.
+  const TokenPosition& func_start = func.token_pos();
+  if (token_pos.IsWithin(func_start, func.end_token_pos()) ||
+      func_start.IsWithin(token_pos, end_token_pos)) {
+    // Check script equality last because it allocates handles as a side effect.
     return func.script() == script.raw();
   }
   return false;
@@ -566,24 +564,20 @@
 
 intptr_t ActivationFrame::LineNumber() {
   // Compute line number lazily since it causes scanning of the script.
-  if ((line_number_ < 0) && TokenPos().IsSourcePosition()) {
-    const TokenPosition token_pos = TokenPos().SourcePosition();
+  const TokenPosition& token_pos = TokenPos();
+  if ((line_number_ < 0) && token_pos.IsReal()) {
     const Script& script = Script::Handle(SourceScript());
-    script.GetTokenLocation(token_pos, &line_number_, NULL);
+    script.GetTokenLocation(token_pos, &line_number_, nullptr);
   }
   return line_number_;
 }
 
 intptr_t ActivationFrame::ColumnNumber() {
   // Compute column number lazily since it causes scanning of the script.
-  if ((column_number_ < 0) && TokenPos().IsSourcePosition()) {
-    const TokenPosition token_pos = TokenPos().SourcePosition();
+  const TokenPosition& token_pos = TokenPos();
+  if ((column_number_ < 0) && token_pos.IsReal()) {
     const Script& script = Script::Handle(SourceScript());
-    if (script.HasSource()) {
-      script.GetTokenLocation(token_pos, &line_number_, &column_number_);
-    } else {
-      column_number_ = -1;
-    }
+    script.GetTokenLocation(token_pos, &line_number_, &column_number_);
   }
   return column_number_;
 }
@@ -648,13 +642,14 @@
     }
     intptr_t var_desc_len = var_descriptors_.Length();
     bool found = false;
+    // We store the deopt ids as real token positions.
+    const auto to_compare = TokenPosition::Deserialize(deopt_id);
     for (intptr_t cur_idx = 0; cur_idx < var_desc_len; cur_idx++) {
       LocalVarDescriptorsLayout::VarInfo var_info;
       var_descriptors_.GetInfo(cur_idx, &var_info);
       const int8_t kind = var_info.kind();
       if ((kind == LocalVarDescriptorsLayout::kContextLevel) &&
-          (deopt_id >= var_info.begin_pos.value()) &&
-          (deopt_id <= var_info.end_pos.value())) {
+          to_compare.IsWithin(var_info.begin_pos, var_info.end_pos)) {
         context_level_ = var_info.index();
         found = true;
         break;
@@ -925,51 +920,51 @@
         (kind != LocalVarDescriptorsLayout::kContextVar)) {
       continue;
     }
-    if ((var_info.begin_pos <= activation_token_pos) &&
-        (activation_token_pos <= var_info.end_pos)) {
-      if ((kind == LocalVarDescriptorsLayout::kContextVar) &&
-          (ContextLevel() < var_info.scope_id)) {
-        // The variable is textually in scope but the context level
-        // at the activation frame's PC is lower than the context
-        // level of the variable. The context containing the variable
-        // has already been removed from the chain. This can happen when we
-        // break at a return statement, since the contexts get discarded
-        // before the debugger gets called.
-        continue;
-      }
-      // The current variable is textually in scope. Now check whether
-      // there is another local variable with the same name that shadows
-      // or is shadowed by this variable.
-      String& var_name = String::Handle(var_descriptors_.GetName(cur_idx));
-      intptr_t indices_len = desc_indices_.length();
-      bool name_match_found = false;
-      for (intptr_t i = 0; i < indices_len; i++) {
-        if (var_name.Equals(*var_names[i])) {
-          // Found two local variables with the same name. Now determine
-          // which one is shadowed.
-          name_match_found = true;
-          LocalVarDescriptorsLayout::VarInfo i_var_info;
-          var_descriptors_.GetInfo(desc_indices_[i], &i_var_info);
-          if (i_var_info.begin_pos < var_info.begin_pos) {
-            // The variable we found earlier is in an outer scope
-            // and is shadowed by the current variable. Replace the
-            // descriptor index of the previously found variable
-            // with the descriptor index of the current variable.
-            desc_indices_[i] = cur_idx;
-          } else {
-            // The variable we found earlier is in an inner scope
-            // and shadows the current variable. Skip the current
-            // variable. (Nothing to do.)
-          }
-          break;  // Stop looking for name matches.
+    if (!activation_token_pos.IsWithin(var_info.begin_pos, var_info.end_pos)) {
+      continue;
+    }
+    if ((kind == LocalVarDescriptorsLayout::kContextVar) &&
+        (ContextLevel() < var_info.scope_id)) {
+      // The variable is textually in scope but the context level
+      // at the activation frame's PC is lower than the context
+      // level of the variable. The context containing the variable
+      // has already been removed from the chain. This can happen when we
+      // break at a return statement, since the contexts get discarded
+      // before the debugger gets called.
+      continue;
+    }
+    // The current variable is textually in scope. Now check whether
+    // there is another local variable with the same name that shadows
+    // or is shadowed by this variable.
+    String& var_name = String::Handle(var_descriptors_.GetName(cur_idx));
+    intptr_t indices_len = desc_indices_.length();
+    bool name_match_found = false;
+    for (intptr_t i = 0; i < indices_len; i++) {
+      if (var_name.Equals(*var_names[i])) {
+        // Found two local variables with the same name. Now determine
+        // which one is shadowed.
+        name_match_found = true;
+        LocalVarDescriptorsLayout::VarInfo i_var_info;
+        var_descriptors_.GetInfo(desc_indices_[i], &i_var_info);
+        if (i_var_info.begin_pos < var_info.begin_pos) {
+          // The variable we found earlier is in an outer scope
+          // and is shadowed by the current variable. Replace the
+          // descriptor index of the previously found variable
+          // with the descriptor index of the current variable.
+          desc_indices_[i] = cur_idx;
+        } else {
+          // The variable we found earlier is in an inner scope
+          // and shadows the current variable. Skip the current
+          // variable. (Nothing to do.)
         }
+        break;  // Stop looking for name matches.
       }
-      if (!name_match_found) {
-        // No duplicate name found. Add the current descriptor index to the
-        // list of visible variables.
-        desc_indices_.Add(cur_idx);
-        var_names.Add(&var_name);
-      }
+    }
+    if (!name_match_found) {
+      // No duplicate name found. Add the current descriptor index to the
+      // list of visible variables.
+      desc_indices_.Add(cur_idx);
+      var_names.Add(&var_name);
     }
   }
   vars_initialized_ = true;
@@ -1167,7 +1162,7 @@
   Object& value = Instance::Handle();
   const Array& list = Array::Handle(Array::New(2 * num_variables));
   for (intptr_t i = 0; i < num_variables; i++) {
-    TokenPosition ignore;
+    TokenPosition ignore = TokenPosition::kNoSource;
     VariableAt(i, &var_name, &ignore, &ignore, &ignore, &value);
     list.SetAt(2 * i, var_name);
     list.SetAt((2 * i) + 1, value);
@@ -1181,7 +1176,7 @@
   String& var_name = String::Handle();
   Instance& value = Instance::Handle();
   for (intptr_t i = 0; i < num_variables; i++) {
-    TokenPosition ignore;
+    TokenPosition ignore = TokenPosition::kNoSource;
     VariableAt(i, &var_name, &ignore, &ignore, &ignore, &value);
     if (var_name.Equals(Symbols::This())) {
       return value.raw();
@@ -1232,7 +1227,7 @@
   TypeArguments& type_arguments = TypeArguments::Handle();
   intptr_t num_variables = desc_indices_.length();
   for (intptr_t i = 0; i < num_variables; i++) {
-    TokenPosition ignore;
+    TokenPosition ignore = TokenPosition::kNoSource;
     VariableAt(i, &name, &ignore, &ignore, &ignore, &value);
     if (name.Equals(Symbols::FunctionTypeArgumentsVar())) {
       type_arguments_available = true;
@@ -1347,7 +1342,7 @@
   const Script& script = Script::Handle(SourceScript());
   jsobj->AddProperty("type", "Frame");
   jsobj->AddProperty("kind", KindToCString(kind_));
-  const TokenPosition pos = TokenPos().SourcePosition();
+  const TokenPosition& pos = TokenPos();
   jsobj->AddLocation(script, pos);
   jsobj->AddProperty("function", function());
   jsobj->AddProperty("code", code());
@@ -1357,9 +1352,9 @@
     for (intptr_t v = 0; v < num_vars; v++) {
       String& var_name = String::Handle();
       Instance& var_value = Instance::Handle();
-      TokenPosition declaration_token_pos;
-      TokenPosition visible_start_token_pos;
-      TokenPosition visible_end_token_pos;
+      TokenPosition declaration_token_pos = TokenPosition::kNoSource;
+      TokenPosition visible_start_token_pos = TokenPosition::kNoSource;
+      TokenPosition visible_end_token_pos = TokenPosition::kNoSource;
       VariableAt(v, &var_name, &declaration_token_pos, &visible_start_token_pos,
                  &visible_end_token_pos, &var_value);
       if (!IsSyntheticVariableName(var_name)) {
@@ -1383,7 +1378,7 @@
   jsobj->AddProperty("type", "Frame");
   jsobj->AddProperty("kind", KindToCString(kind_));
   const Script& script = Script::Handle(SourceScript());
-  const TokenPosition pos = TokenPos().SourcePosition();
+  const TokenPosition& pos = TokenPos();
   jsobj->AddLocation(script, pos);
   jsobj->AddProperty("function", function());
   jsobj->AddProperty("code", code());
@@ -2239,16 +2234,14 @@
   intptr_t token_start_column = -1;
   intptr_t token_line = -1;
   if (requested_column >= 0) {
-    TokenPosition ignored;
-    TokenPosition end_of_line_pos;
+    TokenPosition ignored = TokenPosition::kNoSource;
+    TokenPosition end_of_line_pos = TokenPosition::kNoSource;
     script.GetTokenLocation(pos, &token_line, &token_start_column);
     script.TokenRangeAtLine(token_line, &ignored, &end_of_line_pos);
     TokenPosition token_end_pos =
-        (end_of_line_pos < next_closest_token_position)
-            ? end_of_line_pos
-            : next_closest_token_position;
+        TokenPosition::Min(next_closest_token_position, end_of_line_pos);
 
-    if ((token_end_pos < exact_token_pos) ||
+    if ((exact_token_pos.IsReal() && (token_end_pos < exact_token_pos)) ||
         (token_start_column > *best_column)) {
       // Prefer the token with the lowest column number compatible
       // with the requested column.
@@ -2261,9 +2254,11 @@
     *best_fit_pos = pos;
     *best_line = token_line;
     *best_column = token_start_column;
-    // best_token_pos is only used when column number is specified.
-    *best_token_pos = TokenPosition(exact_token_pos.value() -
-                                    (requested_column - *best_column));
+    // best_token_pos should only be real when the column number is specified.
+    if (requested_column >= 0 && exact_token_pos.IsReal()) {
+      *best_token_pos = TokenPosition::Deserialize(
+          exact_token_pos.Pos() - (requested_column - *best_column));
+    }
   }
 }
 
@@ -2330,12 +2325,9 @@
                                              TokenPosition exact_token_pos) {
   ASSERT(!func.HasOptimizedCode());
 
-  if (requested_token_pos < func.token_pos()) {
-    requested_token_pos = func.token_pos();
-  }
-  if (last_token_pos > func.end_token_pos()) {
-    last_token_pos = func.end_token_pos();
-  }
+  requested_token_pos =
+      TokenPosition::Max(requested_token_pos, func.token_pos());
+  last_token_pos = TokenPosition::Min(last_token_pos, func.end_token_pos());
 
   Zone* zone = Thread::Current()->zone();
   Script& script = Script::Handle(zone, func.script());
@@ -2351,15 +2343,19 @@
   TokenPosition best_fit_pos = TokenPosition::kMaxSource;
   intptr_t best_column = INT_MAX;
   intptr_t best_line = INT_MAX;
-  // best_token_pos and exact_token_pos are only used
-  // if column number is provided.
+  // best_token_pos is only set to a real position if a real exact_token_pos
+  // and a column number are provided.
   TokenPosition best_token_pos = TokenPosition::kNoSource;
 
   PcDescriptors::Iterator iter(desc, kSafepointKind);
   while (iter.MoveNext()) {
-    const TokenPosition pos = iter.TokenPos();
-    if ((!pos.IsReal()) || (pos < requested_token_pos) ||
-        (pos > last_token_pos)) {
+    const TokenPosition& pos = iter.TokenPos();
+    if (pos.IsSynthetic() && pos == requested_token_pos) {
+      // if there's a safepoint for a synthetic function start and the start
+      // was requested, we're done.
+      return pos;
+    }
+    if (!pos.IsWithin(requested_token_pos, last_token_pos)) {
       // Token is not in the target range.
       continue;
     }
@@ -2368,8 +2364,9 @@
       // Find next closest safepoint
       PcDescriptors::Iterator iter2(desc, kSafepointKind);
       while (iter2.MoveNext()) {
-        const TokenPosition next = iter2.TokenPos();
-        if (next < next_closest_token_position && next > pos) {
+        const TokenPosition& next = iter2.TokenPos();
+        if (!next.IsReal()) continue;
+        if ((pos < next) && (next < next_closest_token_position)) {
           next_closest_token_position = next;
         }
       }
@@ -2384,33 +2381,31 @@
   // the token on the line which is at the best fit column (if column
   // was specified) and has the lowest code address.
   if (best_fit_pos != TokenPosition::kMaxSource) {
+    ASSERT(best_fit_pos.IsReal());
     const Script& script = Script::Handle(zone, func.script());
     const TokenPosition begin_pos = best_fit_pos;
 
-    TokenPosition end_of_line_pos;
+    TokenPosition end_of_line_pos = TokenPosition::kNoSource;
     if (best_line == -1) {
-      script.GetTokenLocation(begin_pos, &best_line, NULL);
+      script.GetTokenLocation(begin_pos, &best_line, nullptr);
     }
     ASSERT(best_line > 0);
-    TokenPosition ignored;
+    TokenPosition ignored = TokenPosition::kNoSource;
     script.TokenRangeAtLine(best_line, &ignored, &end_of_line_pos);
-    if (end_of_line_pos < begin_pos) {
-      end_of_line_pos = begin_pos;
-    }
+    end_of_line_pos = TokenPosition::Max(end_of_line_pos, begin_pos);
 
     uword lowest_pc_offset = kUwordMax;
     PcDescriptors::Iterator iter(desc, kSafepointKind);
     while (iter.MoveNext()) {
-      const TokenPosition pos = iter.TokenPos();
-      if (!pos.IsReal() || (pos < begin_pos) || (pos > end_of_line_pos)) {
-        // Token is not on same line as best fit.
-        continue;
-      }
-
-      if (requested_column >= 0) {
+      const TokenPosition& pos = iter.TokenPos();
+      if (best_token_pos.IsReal()) {
         if (pos != best_token_pos) {
+          // Not an match for the requested column.
           continue;
         }
+      } else if (!pos.IsWithin(begin_pos, end_of_line_pos)) {
+        // Token is not on same line as best fit.
+        continue;
       }
 
       // Prefer the lowest pc offset.
@@ -2557,14 +2552,14 @@
   }
 }
 
-static void SelectBestFit(Function* best_fit, Function* func) {
+static void UpdateBestFit(Function* best_fit, const Function& func) {
   if (best_fit->IsNull()) {
-    *best_fit = func->raw();
-  } else {
-    if ((func->token_pos() > best_fit->token_pos()) &&
-        ((func->end_token_pos() <= best_fit->end_token_pos()))) {
-      *best_fit = func->raw();
-    }
+    *best_fit = func.raw();
+  } else if ((best_fit->token_pos().IsSynthetic() ||
+              func.token_pos().IsSynthetic() ||
+              (best_fit->token_pos() < func.token_pos())) &&
+             (func.end_token_pos() <= best_fit->end_token_pos())) {
+    *best_fit = func.raw();
   }
 }
 
@@ -2623,7 +2618,7 @@
       function ^= closures.At(i);
       if (FunctionOverlaps(function, script, token_pos, last_token_pos)) {
         // Select the inner most closure.
-        SelectBestFit(best_fit, &function);
+        UpdateBestFit(best_fit, function);
       }
     }
     if (!best_fit->IsNull()) {
@@ -2688,8 +2683,8 @@
       if (!fields.IsNull()) {
         const intptr_t num_fields = fields.Length();
         for (intptr_t pos = 0; pos < num_fields; pos++) {
-          TokenPosition start;
-          TokenPosition end;
+          TokenPosition start = TokenPosition::kNoSource;
+          TokenPosition end = TokenPosition::kNoSource;
           field ^= fields.At(pos);
           ASSERT(!field.IsNull());
           if (field.Script() != script.raw()) {
@@ -2702,8 +2697,8 @@
           }
           start = field.token_pos();
           end = field.end_token_pos();
-          if ((start <= token_pos && token_pos <= end) ||
-              (token_pos <= start && start <= last_token_pos)) {
+          if (token_pos.IsWithin(start, end) ||
+              start.IsWithin(token_pos, last_token_pos)) {
             return true;
           }
         }
@@ -2803,7 +2798,7 @@
       // have already been compiled. We can resolve the breakpoint now.
       // If requested_column is larger than zero, [token_pos, last_token_pos]
       // governs one single line of code.
-      TokenPosition exact_token_pos = TokenPosition(-1);
+      TokenPosition exact_token_pos = TokenPosition::kNoSource;
       if (token_pos != last_token_pos && requested_column >= 0) {
 #if !defined(DART_PRECOMPILED_RUNTIME)
         exact_token_pos =
@@ -3010,7 +3005,8 @@
     }
     return latent_bpt;
   }
-  TokenPosition first_token_idx, last_token_idx;
+  TokenPosition first_token_idx = TokenPosition::kNoSource;
+  TokenPosition last_token_idx = TokenPosition::kNoSource;
   script.TokenRangeAtLine(line_number, &first_token_idx, &last_token_idx);
   if (!first_token_idx.IsReal()) {
     // Script does not contain the given line number.
@@ -3825,6 +3821,8 @@
 // the given token position.
 FunctionPtr Debugger::FindInnermostClosure(const Function& function,
                                            TokenPosition token_pos) {
+  ASSERT(function.end_token_pos().IsReal());
+  const TokenPosition& func_start = function.token_pos();
   Zone* zone = Thread::Current()->zone();
   const Script& outer_origin = Script::Handle(zone, function.script());
   const GrowableObjectArray& closures = GrowableObjectArray::Handle(
@@ -3834,12 +3832,16 @@
   Function& best_fit = Function::Handle(zone);
   for (intptr_t i = 0; i < num_closures; i++) {
     closure ^= closures.At(i);
-    if ((function.token_pos() < closure.token_pos()) &&
-        (closure.end_token_pos() < function.end_token_pos()) &&
-        (closure.token_pos() <= token_pos) &&
-        (token_pos <= closure.end_token_pos()) &&
+    const TokenPosition& closure_start = closure.token_pos();
+    const TokenPosition& closure_end = closure.end_token_pos();
+    // We're only interested in closures that have real ending token positions.
+    // The starting token position can be synthetic.
+    if (closure_end.IsReal() && (function.end_token_pos() > closure_end) &&
+        (!closure_start.IsReal() || !func_start.IsReal() ||
+         (closure_start > func_start)) &&
+        token_pos.IsWithin(closure_start, closure_end) &&
         (closure.script() == outer_origin.raw())) {
-      SelectBestFit(&best_fit, &closure);
+      UpdateBestFit(&best_fit, closure);
     }
   }
   return best_fit.raw();
@@ -3851,13 +3853,14 @@
 TokenPosition Debugger::FindExactTokenPosition(const Script& script,
                                                TokenPosition start_of_line,
                                                intptr_t column_number) {
-  intptr_t line = -1;
-  intptr_t col = -1;
-  Zone* zone = Thread::Current()->zone();
-  kernel::KernelLineStartsReader line_starts_reader(
-      TypedData::Handle(zone, script.line_starts()), zone);
-  line_starts_reader.LocationForPosition(start_of_line.value(), &line, &col);
-  return TokenPosition(start_of_line.value() + (column_number - col));
+  intptr_t line;
+  intptr_t col;
+  script.GetTokenLocation(start_of_line, &line, &col);
+  if (line < 0) {
+    return TokenPosition::kNoSource;
+  }
+  return TokenPosition::Deserialize(start_of_line.Pos() +
+                                    (column_number - col));
 }
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
@@ -3988,7 +3991,8 @@
         intptr_t line_number = matched_loc->requested_line_number();
         intptr_t column_number = matched_loc->requested_column_number();
         ASSERT(line_number >= 0);
-        TokenPosition first_token_pos, last_token_pos;
+        TokenPosition first_token_pos = TokenPosition::kNoSource;
+        TokenPosition last_token_pos = TokenPosition::kNoSource;
         script.TokenRangeAtLine(line_number, &first_token_pos, &last_token_pos);
         if (!first_token_pos.IsDebugPause() || !last_token_pos.IsDebugPause()) {
           // Script does not contain the given line number or there are no
diff --git a/runtime/vm/dwarf.h b/runtime/vm/dwarf.h
index 22df262..ef72088 100644
--- a/runtime/vm/dwarf.h
+++ b/runtime/vm/dwarf.h
@@ -60,7 +60,7 @@
 
   static Value ValueOf(Pair kv) { return kv.index_; }
 
-  static inline intptr_t Hashcode(Key key) { return key->token_pos().value(); }
+  static inline intptr_t Hashcode(Key key) { return key->token_pos().Hash(); }
 
   static inline bool IsKeyEqual(Pair pair, Key key) {
     return pair.function_->raw() == key->raw();
diff --git a/runtime/vm/json_stream.cc b/runtime/vm/json_stream.cc
index adfc624..5b3c8fe 100644
--- a/runtime/vm/json_stream.cc
+++ b/runtime/vm/json_stream.cc
@@ -320,7 +320,7 @@
 
 void JSONStream::PrintValue(TokenPosition tp) {
   PrintCommaIfNeeded();
-  PrintValue(tp.value());
+  PrintValue(static_cast<intptr_t>(tp.Serialize()));
 }
 
 void JSONStream::PrintValue(const ServiceEvent* event) {
diff --git a/runtime/vm/kernel.cc b/runtime/vm/kernel.cc
index ed49742..4a65c47 100644
--- a/runtime/vm/kernel.cc
+++ b/runtime/vm/kernel.cc
@@ -78,11 +78,11 @@
   for (intptr_t i = 0; i < line_number; ++i) {
     cumulative += helper_->At(line_starts_data_, i);
   }
-  *first_token_index = dart::TokenPosition(cumulative);
+  *first_token_index = dart::TokenPosition::Deserialize(cumulative);
   if (line_number == line_starts_data_.Length()) {
-    *last_token_index = dart::TokenPosition(source_length);
+    *last_token_index = dart::TokenPosition::Deserialize(source_length);
   } else {
-    *last_token_index = dart::TokenPosition(
+    *last_token_index = dart::TokenPosition::Deserialize(
         cumulative + helper_->At(line_starts_data_, line_number) - 1);
   }
 }
@@ -168,7 +168,7 @@
 void KernelTokenPositionCollector::RecordTokenPosition(TokenPosition position) {
   if (record_for_script_id_ == current_script_id_ &&
       record_token_positions_into_ != NULL && position.IsReal()) {
-    record_token_positions_into_->Add(position.value());
+    record_token_positions_into_->Add(position.Serialize());
   }
 }
 
@@ -259,8 +259,8 @@
       if (entry.IsClass()) {
         const Class& klass = Class::Cast(entry);
         if (klass.script() == interesting_script.raw()) {
-          token_positions.Add(klass.token_pos().value());
-          token_positions.Add(klass.end_token_pos().value());
+          token_positions.Add(klass.token_pos().Serialize());
+          token_positions.Add(klass.end_token_pos().Serialize());
         }
         if (klass.is_finalized()) {
           temp_array = klass.fields();
diff --git a/runtime/vm/kernel_binary.h b/runtime/vm/kernel_binary.h
index 38f1474..48e3231 100644
--- a/runtime/vm/kernel_binary.h
+++ b/runtime/vm/kernel_binary.h
@@ -229,14 +229,18 @@
         raw_buffer_(buffer),
         typed_data_(NULL),
         size_(size),
-        offset_(0) {}
+        offset_(0),
+        max_position_(TokenPosition::kNoSource),
+        min_position_(TokenPosition::kNoSource) {}
 
   explicit Reader(const ExternalTypedData& typed_data)
       : thread_(Thread::Current()),
         raw_buffer_(NULL),
         typed_data_(&typed_data),
         size_(typed_data.IsNull() ? 0 : typed_data.Length()),
-        offset_(0) {}
+        offset_(0),
+        max_position_(TokenPosition::kNoSource),
+        min_position_(TokenPosition::kNoSource) {}
 
   uint32_t ReadFromIndex(intptr_t end_offset,
                          intptr_t fields_before,
@@ -329,14 +333,9 @@
     // Position is saved as unsigned,
     // but actually ranges from -1 and up (thus the -1)
     intptr_t value = ReadUInt() - 1;
-    TokenPosition result = TokenPosition(value);
-    max_position_ = Utils::Maximum(max_position_, result);
-    if (min_position_.IsNoSource()) {
-      min_position_ = result;
-    } else if (result.IsReal()) {
-      min_position_ = Utils::Minimum(min_position_, result);
-    }
-
+    TokenPosition result = TokenPosition::Deserialize(value);
+    max_position_ = TokenPosition::Max(max_position_, result);
+    min_position_ = TokenPosition::Min(min_position_, result);
     return result;
   }
 
@@ -553,12 +552,8 @@
   }
 
   ~PositionScope() {
-    if (reader_->min_position_.IsNoSource()) {
-      reader_->min_position_ = min_;
-    } else if (min_.IsReal()) {
-      reader_->min_position_ = Utils::Minimum(reader_->min_position_, min_);
-    }
-    reader_->max_position_ = Utils::Maximum(reader_->max_position_, max_);
+    reader_->min_position_ = TokenPosition::Min(reader_->min_position_, min_);
+    reader_->max_position_ = TokenPosition::Max(reader_->max_position_, max_);
   }
 
  private:
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index f2f9877..6cb5b65 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -3959,7 +3959,7 @@
                                 const AbstractType& dst_type,
                                 const String& dst_name) {
   const Array& args = Array::Handle(Array::New(4));
-  const Smi& pos = Smi::Handle(Smi::New(token_pos.value()));
+  const Smi& pos = Smi::Handle(Smi::New(token_pos.Serialize()));
   args.SetAt(0, pos);
   args.SetAt(1, src_value);
   args.SetAt(2, dst_type);
@@ -7640,9 +7640,8 @@
     // Native methods don't need to be optimized.
     return false;
   }
-  const intptr_t function_length = end_token_pos().Pos() - token_pos().Pos();
   if (is_optimizable() && (script() != Script::null()) &&
-      (function_length < FLAG_huge_method_cutoff_in_tokens)) {
+      SourceSize() < FLAG_huge_method_cutoff_in_tokens) {
     // Additional check needed for implicit getters.
     return (unoptimized_code() == Object::null()) ||
            (Code::Handle(unoptimized_code()).Size() <
@@ -9398,7 +9397,7 @@
     if (src.IsNull() || src.Length() == 0) {
       return Symbols::OptimizedOut().raw();
     }
-    uint16_t end_char = src.CharAt(end_token_pos().value());
+    uint16_t end_char = src.CharAt(end_token_pos().Pos());
     if ((end_char == ',') ||  // Case 1.
         (end_char == ')') ||  // Case 2.
         (end_char == ';' && String::Handle(zone, name())
@@ -9662,6 +9661,27 @@
          (NeedsTypeArgumentTypeChecks() || NeedsArgumentTypeChecks());
 }
 
+intptr_t Function::SourceSize() const {
+  const TokenPosition& start = token_pos();
+  const TokenPosition& end = end_token_pos();
+  if (!end.IsReal() || start.IsNoSource() || start.IsClassifying()) {
+    // No source information, so just return 0.
+    return 0;
+  }
+  if (start.IsSynthetic()) {
+    // Try and approximate the source size using the parent's source size.
+    const auto& parent = Function::Handle(parent_function());
+    ASSERT(!parent.IsNull());
+    const intptr_t parent_size = parent.SourceSize();
+    if (parent_size == 0) {
+      return parent_size;
+    }
+    // Parent must have a real ending position.
+    return parent_size - (parent.end_token_pos().Pos() - end.Pos());
+  }
+  return end.Pos() - start.Pos();
+}
+
 const char* Function::ToCString() const {
   if (IsNull()) {
     return "Function: null";
@@ -11231,31 +11251,6 @@
   StoreNonPointer(&raw_ptr()->col_offset_, col_offset);
 }
 
-// Specialized for AOT compilation, which does this lookup for every token
-// position that could be part of a stack trace.
-bool Script::GetTokenLocationUsingLineStarts(TokenPosition target_token_pos,
-                                             intptr_t* line,
-                                             intptr_t* column) const {
-#if defined(DART_PRECOMPILED_RUNTIME)
-  return false;
-#else
-  // Negative positions denote positions that do not correspond to Dart code.
-  if (target_token_pos.value() < 0) return false;
-
-  Zone* zone = Thread::Current()->zone();
-  TypedData& line_starts_data = TypedData::Handle(zone, line_starts());
-  ASSERT(!line_starts_data.IsNull());
-
-  kernel::KernelLineStartsReader line_starts_reader(line_starts_data, zone);
-  line_starts_reader.LocationForPosition(target_token_pos.value(), line,
-                                         column);
-  // The line and column numbers returned are ordinals, so we shouldn't get 0.
-  ASSERT(*line > 0);
-  ASSERT(*column > 0);
-  return true;
-#endif
-}
-
 #if !defined(DART_PRECOMPILED_RUNTIME)
 static bool IsLetter(int32_t c) {
   return (('A' <= c) && (c <= 'Z')) || (('a' <= c) && (c <= 'z'));
@@ -11278,37 +11273,36 @@
                               intptr_t* line,
                               intptr_t* column,
                               intptr_t* token_len) const {
-  ASSERT(line != NULL);
-  Zone* zone = Thread::Current()->zone();
-
-  LookupSourceAndLineStarts(zone);
-  if (line_starts() == TypedData::null()) {
-    // Scripts in the AOT snapshot do not have a line starts array.
-    *line = -1;
-    if (column != NULL) {
-      *column = -1;
-    }
-    if (token_len != NULL) {
-      *token_len = 1;
-    }
-    return;
+  ASSERT(line != nullptr);
+  // Set up appropriate values for any early returns.
+  *line = -1;
+  if (column != nullptr) {
+    *column = -1;
   }
+  if (token_len != nullptr) {
+    *token_len = 1;
+  }
+  // Scripts in the AOT snapshot do not have a line starts array.
 #if !defined(DART_PRECOMPILED_RUNTIME)
+  if (!token_pos.IsReal()) return;
+
+  auto const zone = Thread::Current()->zone();
+  LookupSourceAndLineStarts(zone);
   const TypedData& line_starts_data = TypedData::Handle(zone, line_starts());
   kernel::KernelLineStartsReader line_starts_reader(line_starts_data, zone);
-  line_starts_reader.LocationForPosition(token_pos.value(), line, column);
-  if (token_len != NULL) {
-    *token_len = 1;
-    // We don't explicitly save this data: Load the source
-    // and find it from there.
-    const String& source = String::Handle(zone, Source());
-    if (!source.IsNull()) {
-      intptr_t offset = token_pos.value();
-      if (offset < source.Length() && IsIdentStartChar(source.CharAt(offset))) {
-        for (intptr_t i = offset + 1;
-             i < source.Length() && IsIdentChar(source.CharAt(i)); ++i) {
-          ++*token_len;
-        }
+  const intptr_t pos = token_pos.Pos();
+  line_starts_reader.LocationForPosition(pos, line, column);
+  if (token_len == nullptr) return;
+  *token_len = 1;
+  // We don't explicitly save this data: Load the source
+  // and find it from there.
+  const String& source = String::Handle(zone, Source());
+  if (!source.IsNull()) {
+    intptr_t offset = pos;
+    if (offset < source.Length() && IsIdentStartChar(source.CharAt(offset))) {
+      for (intptr_t i = offset + 1;
+           i < source.Length() && IsIdentChar(source.CharAt(i)); ++i) {
+        ++*token_len;
       }
     }
   }
@@ -11321,16 +11315,10 @@
   ASSERT(first_token_index != NULL && last_token_index != NULL);
   ASSERT(line_number > 0);
 
-  Thread* thread = Thread::Current();
-  Zone* zone = thread->zone();
-  LookupSourceAndLineStarts(zone);
-  if (line_starts() == TypedData::null()) {
-    // Scripts in the AOT snapshot do not have a line starts array.
-    *first_token_index = TokenPosition::kNoSource;
-    *last_token_index = TokenPosition::kNoSource;
-    return;
-  }
+  // Scripts in the AOT snapshot do not have a line starts array.
 #if !defined(DART_PRECOMPILED_RUNTIME)
+  Zone* zone = Thread::Current()->zone();
+  LookupSourceAndLineStarts(zone);
   const String& source = String::Handle(zone, Source());
   intptr_t source_length;
   if (source.IsNull()) {
@@ -11342,8 +11330,7 @@
     source_length = source.Length();
   }
   const TypedData& line_starts_data = TypedData::Handle(zone, line_starts());
-  kernel::KernelLineStartsReader line_starts_reader(line_starts_data,
-                                                    Thread::Current()->zone());
+  kernel::KernelLineStartsReader line_starts_reader(line_starts_data, zone);
   line_starts_reader.TokenRangeAtLine(source_length, line_number,
                                       first_token_index, last_token_index);
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
@@ -14359,8 +14346,8 @@
                           " %-13s level=%-3d"
                           " begin=%-3d end=%d\n",
                           i, LocalVarDescriptors::KindToCString(kind), index,
-                          static_cast<int>(info.begin_pos.value()),
-                          static_cast<int>(info.end_pos.value()));
+                          static_cast<int>(info.begin_pos.Pos()),
+                          static_cast<int>(info.end_pos.Pos()));
   } else if (kind == LocalVarDescriptorsLayout::kContextVar) {
     return Utils::SNPrint(
         buffer, len,
@@ -16810,18 +16797,19 @@
 }
 
 TokenPosition ContextScope::TokenIndexAt(intptr_t scope_index) const {
-  return TokenPosition(Smi::Value(VariableDescAddr(scope_index)->token_pos));
+  return TokenPosition::Deserialize(
+      Smi::Value(VariableDescAddr(scope_index)->token_pos));
 }
 
 void ContextScope::SetTokenIndexAt(intptr_t scope_index,
                                    TokenPosition token_pos) const {
   StoreSmi(&VariableDescAddr(scope_index)->token_pos,
-           Smi::New(token_pos.value()));
+           Smi::New(token_pos.Serialize()));
 }
 
 TokenPosition ContextScope::DeclarationTokenIndexAt(
     intptr_t scope_index) const {
-  return TokenPosition(
+  return TokenPosition::Deserialize(
       Smi::Value(VariableDescAddr(scope_index)->declaration_token_pos));
 }
 
@@ -16829,7 +16817,7 @@
     intptr_t scope_index,
     TokenPosition declaration_token_pos) const {
   StoreSmi(&VariableDescAddr(scope_index)->declaration_token_pos,
-           Smi::New(declaration_token_pos.value()));
+           Smi::New(declaration_token_pos.Serialize()));
 }
 
 StringPtr ContextScope::NameAt(intptr_t scope_index) const {
@@ -24238,13 +24226,14 @@
 
   intptr_t line = -1;
   intptr_t column = -1;
-  if (FLAG_precompiled_mode) {
-    line = token_pos.value();
-  } else if (token_pos.IsSourcePosition()) {
-    ASSERT(!script.IsNull());
-    script.GetTokenLocation(token_pos.SourcePosition(), &line, &column);
+  if (token_pos.IsReal()) {
+    if (FLAG_precompiled_mode) {
+      line = token_pos.Pos();
+    } else {
+      ASSERT(!script.IsNull());
+      script.GetTokenLocation(token_pos, &line, &column);
+    }
   }
-
   PrintSymbolicStackFrameIndex(buffer, frame_index);
   PrintSymbolicStackFrameBody(buffer, function_name, url, line, column);
 }
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index aab8456..b94c5a3 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -2954,7 +2954,7 @@
 
   TokenPosition token_pos() const {
 #if defined(DART_PRECOMPILED_RUNTIME)
-    return TokenPosition();
+    return TokenPosition::kNoSource;
 #else
     return raw_ptr()->token_pos_;
 #endif
@@ -2963,7 +2963,7 @@
 
   TokenPosition end_token_pos() const {
 #if defined(DART_PRECOMPILED_RUNTIME)
-    return TokenPosition();
+    return TokenPosition::kNoSource;
 #else
     return raw_ptr()->end_token_pos_;
 #endif
@@ -2976,6 +2976,9 @@
 #endif
   }
 
+  // Returns the size of the source for this function.
+  intptr_t SourceSize() const;
+
   intptr_t num_fixed_parameters() const {
     return FunctionLayout::PackedNumFixedParameters::decode(
         raw_ptr()->packed_fields_);
@@ -4540,9 +4543,6 @@
 
   void SetLocationOffset(intptr_t line_offset, intptr_t col_offset) const;
 
-  bool GetTokenLocationUsingLineStarts(TokenPosition token_pos,
-                                       intptr_t* line,
-                                       intptr_t* column) const;
   void GetTokenLocation(TokenPosition token_pos,
                         intptr_t* line,
                         intptr_t* column,
@@ -5587,7 +5587,7 @@
 
         if (!FLAG_precompiled_mode) {
           cur_deopt_id_ += stream.ReadSLEB128();
-          cur_token_pos_ += stream.ReadSLEB128();
+          cur_token_pos_ += stream.ReadSLEB128<int32_t>();
         }
         byte_index_ = stream.Position();
 
@@ -5600,7 +5600,9 @@
 
     uword PcOffset() const { return cur_pc_offset_; }
     intptr_t DeoptId() const { return cur_deopt_id_; }
-    TokenPosition TokenPos() const { return TokenPosition(cur_token_pos_); }
+    TokenPosition TokenPos() const {
+      return TokenPosition::Deserialize(cur_token_pos_);
+    }
     intptr_t TryIndex() const { return cur_try_index_; }
     intptr_t YieldIndex() const { return cur_yield_index_; }
     PcDescriptorsLayout::Kind Kind() const {
@@ -5630,7 +5632,7 @@
     intptr_t cur_pc_offset_;
     intptr_t cur_kind_;
     intptr_t cur_deopt_id_;
-    intptr_t cur_token_pos_;
+    int32_t cur_token_pos_;
     intptr_t cur_try_index_;
     intptr_t cur_yield_index_;
   };
diff --git a/runtime/vm/object_service.cc b/runtime/vm/object_service.cc
index 1dfe634..459b279 100644
--- a/runtime/vm/object_service.cc
+++ b/runtime/vm/object_service.cc
@@ -808,7 +808,7 @@
 
   JSONObject jsobj(&jsarray);
   jsobj.AddProperty("name", String::Handle(target_name()).ToCString());
-  jsobj.AddProperty("tokenPos", token_pos.value());
+  jsobj.AddProperty("tokenPos", static_cast<intptr_t>(token_pos.Serialize()));
   // TODO(rmacnak): Figure out how to stringify DeoptReasons().
   // jsobj.AddProperty("deoptReasons", ...);
 
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index cc5e5c1..fdd6784 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -2914,18 +2914,18 @@
   DescriptorList* builder = new DescriptorList(thread->zone());
 
   // kind, pc_offset, deopt_id, token_pos, try_index, yield_index
-  builder->AddDescriptor(PcDescriptorsLayout::kOther, 10, 1, TokenPosition(20),
-                         1, 1);
-  builder->AddDescriptor(PcDescriptorsLayout::kDeopt, 20, 2, TokenPosition(30),
-                         0, -1);
-  builder->AddDescriptor(PcDescriptorsLayout::kOther, 30, 3, TokenPosition(40),
-                         1, 10);
-  builder->AddDescriptor(PcDescriptorsLayout::kOther, 10, 4, TokenPosition(40),
-                         2, 20);
-  builder->AddDescriptor(PcDescriptorsLayout::kOther, 10, 5, TokenPosition(80),
-                         3, 30);
-  builder->AddDescriptor(PcDescriptorsLayout::kOther, 80, 6, TokenPosition(150),
-                         3, 30);
+  builder->AddDescriptor(PcDescriptorsLayout::kOther, 10, 1,
+                         TokenPosition::Deserialize(20), 1, 1);
+  builder->AddDescriptor(PcDescriptorsLayout::kDeopt, 20, 2,
+                         TokenPosition::Deserialize(30), 0, -1);
+  builder->AddDescriptor(PcDescriptorsLayout::kOther, 30, 3,
+                         TokenPosition::Deserialize(40), 1, 10);
+  builder->AddDescriptor(PcDescriptorsLayout::kOther, 10, 4,
+                         TokenPosition::Deserialize(40), 2, 20);
+  builder->AddDescriptor(PcDescriptorsLayout::kOther, 10, 5,
+                         TokenPosition::Deserialize(80), 3, 30);
+  builder->AddDescriptor(PcDescriptorsLayout::kOther, 80, 6,
+                         TokenPosition::Deserialize(150), 3, 30);
 
   PcDescriptors& descriptors = PcDescriptors::Handle();
   descriptors ^= builder->FinalizePcDescriptors(0);
@@ -2945,7 +2945,7 @@
 
   EXPECT_EQ(true, iter.MoveNext());
   EXPECT_EQ(1, iter.YieldIndex());
-  EXPECT_EQ(20, iter.TokenPos().value());
+  EXPECT_EQ(20, iter.TokenPos().Pos());
   EXPECT_EQ(1, iter.TryIndex());
   EXPECT_EQ(static_cast<uword>(10), iter.PcOffset());
   EXPECT_EQ(1, iter.DeoptId());
@@ -2953,28 +2953,28 @@
 
   EXPECT_EQ(true, iter.MoveNext());
   EXPECT_EQ(-1, iter.YieldIndex());
-  EXPECT_EQ(30, iter.TokenPos().value());
+  EXPECT_EQ(30, iter.TokenPos().Pos());
   EXPECT_EQ(PcDescriptorsLayout::kDeopt, iter.Kind());
 
   EXPECT_EQ(true, iter.MoveNext());
   EXPECT_EQ(10, iter.YieldIndex());
-  EXPECT_EQ(40, iter.TokenPos().value());
+  EXPECT_EQ(40, iter.TokenPos().Pos());
 
   EXPECT_EQ(true, iter.MoveNext());
   EXPECT_EQ(20, iter.YieldIndex());
-  EXPECT_EQ(40, iter.TokenPos().value());
+  EXPECT_EQ(40, iter.TokenPos().Pos());
 
   EXPECT_EQ(true, iter.MoveNext());
   EXPECT_EQ(30, iter.YieldIndex());
-  EXPECT_EQ(80, iter.TokenPos().value());
+  EXPECT_EQ(80, iter.TokenPos().Pos());
 
   EXPECT_EQ(true, iter.MoveNext());
   EXPECT_EQ(30, iter.YieldIndex());
-  EXPECT_EQ(150, iter.TokenPos().value());
+  EXPECT_EQ(150, iter.TokenPos().Pos());
 
   EXPECT_EQ(3, iter.TryIndex());
   EXPECT_EQ(static_cast<uword>(80), iter.PcOffset());
-  EXPECT_EQ(150, iter.TokenPos().value());
+  EXPECT_EQ(150, iter.TokenPos().Pos());
   EXPECT_EQ(PcDescriptorsLayout::kOther, iter.Kind());
 
   EXPECT_EQ(false, iter.MoveNext());
@@ -2985,17 +2985,17 @@
 
   // kind, pc_offset, deopt_id, token_pos, try_index
   builder->AddDescriptor(PcDescriptorsLayout::kOther, 100, 1,
-                         TokenPosition(200), 1, 10);
+                         TokenPosition::Deserialize(200), 1, 10);
   builder->AddDescriptor(PcDescriptorsLayout::kDeopt, 200, 2,
-                         TokenPosition(300), 0, -1);
+                         TokenPosition::Deserialize(300), 0, -1);
   builder->AddDescriptor(PcDescriptorsLayout::kOther, 300, 3,
-                         TokenPosition(400), 1, 10);
-  builder->AddDescriptor(PcDescriptorsLayout::kOther, 100, 4, TokenPosition(0),
-                         2, 20);
+                         TokenPosition::Deserialize(400), 1, 10);
+  builder->AddDescriptor(PcDescriptorsLayout::kOther, 100, 4,
+                         TokenPosition::Deserialize(0), 2, 20);
   builder->AddDescriptor(PcDescriptorsLayout::kOther, 100, 5,
-                         TokenPosition(800), 3, 30);
+                         TokenPosition::Deserialize(800), 3, 30);
   builder->AddDescriptor(PcDescriptorsLayout::kOther, 800, 6,
-                         TokenPosition(150), 3, 30);
+                         TokenPosition::Deserialize(150), 3, 30);
 
   PcDescriptors& descriptors = PcDescriptors::Handle();
   descriptors ^= builder->FinalizePcDescriptors(0);
@@ -3015,7 +3015,7 @@
 
   EXPECT_EQ(true, iter.MoveNext());
   EXPECT_EQ(10, iter.YieldIndex());
-  EXPECT_EQ(200, iter.TokenPos().value());
+  EXPECT_EQ(200, iter.TokenPos().Pos());
   EXPECT_EQ(1, iter.TryIndex());
   EXPECT_EQ(static_cast<uword>(100), iter.PcOffset());
   EXPECT_EQ(1, iter.DeoptId());
@@ -3023,28 +3023,28 @@
 
   EXPECT_EQ(true, iter.MoveNext());
   EXPECT_EQ(-1, iter.YieldIndex());
-  EXPECT_EQ(300, iter.TokenPos().value());
+  EXPECT_EQ(300, iter.TokenPos().Pos());
   EXPECT_EQ(PcDescriptorsLayout::kDeopt, iter.Kind());
 
   EXPECT_EQ(true, iter.MoveNext());
   EXPECT_EQ(10, iter.YieldIndex());
-  EXPECT_EQ(400, iter.TokenPos().value());
+  EXPECT_EQ(400, iter.TokenPos().Pos());
 
   EXPECT_EQ(true, iter.MoveNext());
   EXPECT_EQ(20, iter.YieldIndex());
-  EXPECT_EQ(0, iter.TokenPos().value());
+  EXPECT_EQ(0, iter.TokenPos().Pos());
 
   EXPECT_EQ(true, iter.MoveNext());
   EXPECT_EQ(30, iter.YieldIndex());
-  EXPECT_EQ(800, iter.TokenPos().value());
+  EXPECT_EQ(800, iter.TokenPos().Pos());
 
   EXPECT_EQ(true, iter.MoveNext());
   EXPECT_EQ(30, iter.YieldIndex());
-  EXPECT_EQ(150, iter.TokenPos().value());
+  EXPECT_EQ(150, iter.TokenPos().Pos());
 
   EXPECT_EQ(3, iter.TryIndex());
   EXPECT_EQ(static_cast<uword>(800), iter.PcOffset());
-  EXPECT_EQ(150, iter.TokenPos().value());
+  EXPECT_EQ(150, iter.TokenPos().Pos());
   EXPECT_EQ(PcDescriptorsLayout::kOther, iter.Kind());
 
   EXPECT_EQ(false, iter.MoveNext());
diff --git a/runtime/vm/profiler_service.cc b/runtime/vm/profiler_service.cc
index 4f88ae4..d539a35 100644
--- a/runtime/vm/profiler_service.cc
+++ b/runtime/vm/profiler_service.cc
@@ -174,7 +174,12 @@
   intptr_t i = 0;
   for (; i < source_position_ticks_.length(); i++) {
     ProfileFunctionSourcePosition& position = source_position_ticks_[i];
-    if (position.token_pos().value() == token_position.value()) {
+    const intptr_t cmp =
+        TokenPosition::CompareForSorting(position.token_pos(), token_position);
+    if (cmp > 0) {
+      // Found insertion point.
+      break;
+    } else if (cmp == 0) {
       if (FLAG_trace_profiler_verbose) {
         OS::PrintErr("Ticking source position %s %s\n",
                      exclusive ? "exclusive" : "inclusive",
@@ -184,9 +189,6 @@
       position.Tick(exclusive);
       return;
     }
-    if (position.token_pos().value() > token_position.value()) {
-      break;
-    }
   }
 
   // Add new one, sorted by token position value.
@@ -934,7 +936,7 @@
   if (cache_entry->inlined_functions.length() == 0) {
     *inlined_functions = NULL;
     *inlined_token_positions = NULL;
-    *token_position = cache_entry->token_position = TokenPosition();
+    *token_position = cache_entry->token_position = TokenPosition::kNoSource;
     return;
   }
 
diff --git a/runtime/vm/profiler_service.h b/runtime/vm/profiler_service.h
index 0ad1e7f..a3d42a8 100644
--- a/runtime/vm/profiler_service.h
+++ b/runtime/vm/profiler_service.h
@@ -115,7 +115,7 @@
     intptr_t offset;
     GrowableArray<const Function*> inlined_functions;
     GrowableArray<TokenPosition> inlined_token_positions;
-    TokenPosition token_position;
+    TokenPosition token_position = TokenPosition::kNoSource;
   };
 
   static const intptr_t kCacheSize = 128;
diff --git a/runtime/vm/profiler_test.cc b/runtime/vm/profiler_test.cc
index 6c3589e..048a8bf 100644
--- a/runtime/vm/profiler_test.cc
+++ b/runtime/vm/profiler_test.cc
@@ -282,13 +282,10 @@
       return NULL;
     }
     TokenPosition token_pos = pfsp.token_pos();
-    if (!token_pos.IsSourcePosition()) {
+    if (!token_pos.IsReal()) {
       // Not a location in a script.
       return NULL;
     }
-    if (token_pos.IsSynthetic()) {
-      token_pos = token_pos.FromSynthetic();
-    }
 
     intptr_t line = 0, column = 0, token_len = 0;
     script.GetTokenLocation(token_pos, &line, &column, &token_len);
@@ -2315,10 +2312,10 @@
       "}\n";
 
   // Token position of * in `i * i`.
-  const TokenPosition squarePosition = TokenPosition(19);
+  const TokenPosition squarePosition = TokenPosition::Deserialize(19);
 
   // Token position of the call to `doWork`.
-  const TokenPosition callPosition = TokenPosition(95);
+  const TokenPosition callPosition = TokenPosition::Deserialize(95);
 
   DisableNativeProfileScope dnps;
   // Disable profiling for this thread.
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 19a1934..fd85941 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -1919,9 +1919,12 @@
   struct VarInfo {
     int32_t index_kind = 0;  // Bitfield for slot index on stack or in context,
                              // and Entry kind of type VarInfoKind.
-    TokenPosition declaration_pos;  // Token position of declaration.
-    TokenPosition begin_pos;        // Token position of scope start.
-    TokenPosition end_pos;          // Token position of scope end.
+    TokenPosition declaration_pos =
+        TokenPosition::kNoSource;  // Token position of declaration.
+    TokenPosition begin_pos =
+        TokenPosition::kNoSource;  // Token position of scope start.
+    TokenPosition end_pos =
+        TokenPosition::kNoSource;   // Token position of scope end.
     int16_t scope_id;               // Scope to which the variable belongs.
 
     VarInfoKind kind() const {
diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc
index 853c1cac..639d149 100644
--- a/runtime/vm/raw_object_snapshot.cc
+++ b/runtime/vm/raw_object_snapshot.cc
@@ -87,7 +87,7 @@
   reader->AddBackRef(object_id, &type, kIsDeserialized);
 
   // Set all non object fields.
-  type.set_token_pos(TokenPosition::SnapshotDecode(reader->Read<int32_t>()));
+  type.set_token_pos(TokenPosition::Deserialize(reader->Read<int32_t>()));
   const uint8_t combined = reader->Read<uint8_t>();
   type.set_type_state(combined >> 4);
   type.set_nullability(static_cast<Nullability>(combined & 0xf));
@@ -160,7 +160,7 @@
   writer->Write<bool>(typeclass_is_in_fullsnapshot);
 
   // Write out all the non object pointer fields.
-  writer->Write<int32_t>(token_pos_.SnapshotEncode());
+  writer->Write<int32_t>(token_pos_.Serialize());
   const uint8_t combined = (type_state_ << 4) | nullability_;
   ASSERT(type_state_ == (combined >> 4));
   ASSERT(nullability_ == (combined & 0xf));
@@ -234,7 +234,7 @@
 
   // Set all non object fields.
   type_parameter.set_token_pos(
-      TokenPosition::SnapshotDecode(reader->Read<int32_t>()));
+      TokenPosition::Deserialize(reader->Read<int32_t>()));
   type_parameter.set_index(reader->Read<int16_t>());
   const uint8_t combined = reader->Read<uint8_t>();
   type_parameter.set_flags(combined >> 4);
@@ -285,7 +285,7 @@
   writer->WriteTags(writer->GetObjectTags(this));
 
   // Write out all the non object pointer fields.
-  writer->Write<int32_t>(token_pos_.SnapshotEncode());
+  writer->Write<int32_t>(token_pos_.Serialize());
   writer->Write<int16_t>(index_);
   const uint8_t combined = (flags_ << 4) | nullability_;
   ASSERT(flags_ == (combined >> 4));
@@ -637,7 +637,7 @@
 
   // Set all non object fields.
   language_error.set_token_pos(
-      TokenPosition::SnapshotDecode(reader->Read<int32_t>()));
+      TokenPosition::Deserialize(reader->Read<int32_t>()));
   language_error.set_report_after_token(reader->Read<bool>());
   language_error.set_kind(reader->Read<uint8_t>());
 
@@ -662,7 +662,7 @@
   writer->WriteTags(writer->GetObjectTags(this));
 
   // Write out all the non object fields.
-  writer->Write<int32_t>(token_pos_.SnapshotEncode());
+  writer->Write<int32_t>(token_pos_.Serialize());
   writer->Write<bool>(report_after_token_);
   writer->Write<uint8_t>(kind_);
 
diff --git a/runtime/vm/scopes.cc b/runtime/vm/scopes.cc
index c84dc26..155ac8e 100644
--- a/runtime/vm/scopes.cc
+++ b/runtime/vm/scopes.cc
@@ -32,8 +32,8 @@
       function_level_(function_level),
       loop_level_(loop_level),
       context_level_(LocalScope::kUninitializedContextLevel),
-      begin_token_pos_(TokenPosition::kNoSourcePos),
-      end_token_pos_(TokenPosition::kNoSourcePos),
+      begin_token_pos_(TokenPosition::kNoSource),
+      end_token_pos_(TokenPosition::kNoSource),
       variables_(),
       labels_(),
       context_variables_(),
@@ -779,8 +779,9 @@
     desc.name = &Symbols::Empty();  // No name.
     desc.info.set_kind(LocalVarDescriptorsLayout::kContextLevel);
     desc.info.scope_id = 0;
-    desc.info.begin_pos = TokenPosition(start_deopt_id);
-    desc.info.end_pos = TokenPosition(end_deopt_id);
+    // We repurpose the token position fields to store deopt IDs in this case.
+    desc.info.begin_pos = TokenPosition::Deserialize(start_deopt_id);
+    desc.info.end_pos = TokenPosition::Deserialize(end_deopt_id);
     desc.info.set_index(start_context_level);
     Add(desc);
 
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index 6a674bb..6118c2b 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -3283,8 +3283,8 @@
     }
   }
   SourceReport report(report_set, compile_mode);
-  report.PrintJSON(js, script, TokenPosition(start_pos),
-                   TokenPosition(end_pos));
+  report.PrintJSON(js, script, TokenPosition::Deserialize(start_pos),
+                   TokenPosition::Deserialize(end_pos));
   return true;
 #endif  // !DART_PRECOMPILED_RUNTIME
 }
diff --git a/runtime/vm/source_report.cc b/runtime/vm/source_report.cc
index b52f017..d065499 100644
--- a/runtime/vm/source_report.cc
+++ b/runtime/vm/source_report.cc
@@ -26,8 +26,8 @@
       compile_mode_(compile_mode),
       thread_(NULL),
       script_(NULL),
-      start_pos_(TokenPosition::kNoSource),
-      end_pos_(TokenPosition::kNoSource),
+      start_pos_(TokenPosition::kMinSource),
+      end_pos_(TokenPosition::kMaxSource),
       profile_(Isolate::Current()),
       next_script_index_(0) {}
 
@@ -51,8 +51,8 @@
                         TokenPosition end_pos) {
   thread_ = thread;
   script_ = script;
-  start_pos_ = start_pos;
-  end_pos_ = end_pos;
+  start_pos_ = TokenPosition::Max(start_pos, TokenPosition::kMinSource);
+  end_pos_ = TokenPosition::Min(end_pos, TokenPosition::kMaxSource);
   ClearScriptTable();
   if (IsReportRequested(kProfile)) {
     // Build the profile.
@@ -79,10 +79,7 @@
       // The function is from the wrong script.
       return true;
     }
-    if (((start_pos_ > TokenPosition::kMinSource) &&
-         (func.end_token_pos() < start_pos_)) ||
-        ((end_pos_ > TokenPosition::kMinSource) &&
-         (func.token_pos() > end_pos_))) {
+    if ((func.end_token_pos() < start_pos_) || (func.token_pos() > end_pos_)) {
       // The function does not intersect with the requested token range.
       return true;
     }
@@ -129,10 +126,8 @@
       // The field is from the wrong script.
       return true;
     }
-    if (((start_pos_ > TokenPosition::kMinSource) &&
-         (field.end_token_pos() < start_pos_)) ||
-        ((end_pos_ > TokenPosition::kMinSource) &&
-         (field.token_pos() > end_pos_))) {
+    if ((field.end_token_pos() < start_pos_) ||
+        (field.token_pos() > end_pos_)) {
       // The field does not intersect with the requested token range.
       return true;
     }
@@ -195,9 +190,8 @@
                                       const Function& function,
                                       const Code& code) {
   ASSERT(!code.IsNull());
-  const TokenPosition begin_pos = function.token_pos();
-  const TokenPosition end_pos = function.end_token_pos();
-
+  const TokenPosition& begin_pos = function.token_pos();
+  const TokenPosition& end_pos = function.end_token_pos();
   ZoneGrowableArray<const ICData*>* ic_data_array =
       new (zone()) ZoneGrowableArray<const ICData*>();
   function.RestoreICDataMap(ic_data_array, false /* clone ic-data */);
@@ -214,8 +208,8 @@
     ASSERT(iter.DeoptId() < ic_data_array->length());
     const ICData* ic_data = (*ic_data_array)[iter.DeoptId()];
     if (ic_data != NULL) {
-      const TokenPosition token_pos = iter.TokenPos();
-      if ((token_pos < begin_pos) || (token_pos > end_pos)) {
+      const TokenPosition& token_pos = iter.TokenPos();
+      if (!token_pos.IsWithin(begin_pos, end_pos)) {
         // Does not correspond to a valid source position.
         continue;
       }
@@ -228,8 +222,8 @@
                                      const Function& function,
                                      const Code& code) {
   ASSERT(!code.IsNull());
-  const TokenPosition begin_pos = function.token_pos();
-  const TokenPosition end_pos = function.end_token_pos();
+  const TokenPosition& begin_pos = function.token_pos();
+  const TokenPosition& end_pos = function.end_token_pos();
 
   ZoneGrowableArray<const ICData*>* ic_data_array =
       new (zone()) ZoneGrowableArray<const ICData*>();
@@ -241,7 +235,7 @@
   const int kCoverageMiss = 1;
   const int kCoverageHit = 2;
 
-  intptr_t func_length = (end_pos.Pos() - begin_pos.Pos()) + 1;
+  intptr_t func_length = function.SourceSize() + 1;
   GrowableArray<char> coverage(func_length);
   coverage.SetLength(func_length);
   for (int i = 0; i < func_length; i++) {
@@ -262,8 +256,8 @@
     ASSERT(iter.DeoptId() < ic_data_array->length());
     const ICData* ic_data = (*ic_data_array)[iter.DeoptId()];
     if (ic_data != NULL) {
-      const TokenPosition token_pos = iter.TokenPos();
-      if ((token_pos < begin_pos) || (token_pos > end_pos)) {
+      const TokenPosition& token_pos = iter.TokenPos();
+      if (!token_pos.IsWithin(begin_pos, end_pos)) {
         // Does not correspond to a valid source position.
         continue;
       }
@@ -303,9 +297,9 @@
 void SourceReport::PrintPossibleBreakpointsData(JSONObject* jsobj,
                                                 const Function& func,
                                                 const Code& code) {
-  const TokenPosition begin_pos = func.token_pos();
-  const TokenPosition end_pos = func.end_token_pos();
-  intptr_t func_length = (end_pos.Pos() - begin_pos.Pos()) + 1;
+  const TokenPosition& begin_pos = func.token_pos();
+  const TokenPosition& end_pos = func.end_token_pos();
+  intptr_t func_length = func.SourceSize() + 1;
 
   BitVector possible(zone(), func_length);
 
@@ -320,8 +314,8 @@
 
   PcDescriptors::Iterator iter(descriptors, kSafepointKind);
   while (iter.MoveNext()) {
-    const TokenPosition token_pos = iter.TokenPos();
-    if ((token_pos < begin_pos) || (token_pos > end_pos)) {
+    const TokenPosition& token_pos = iter.TokenPos();
+    if (!token_pos.IsWithin(begin_pos, end_pos)) {
       // Does not correspond to a valid source position.
       continue;
     }
@@ -357,7 +351,7 @@
       for (intptr_t i = 0; i < profile_function->NumSourcePositions(); i++) {
         const ProfileFunctionSourcePosition& position =
             profile_function->GetSourcePosition(i);
-        if (position.token_pos().IsSourcePosition()) {
+        if (position.token_pos().IsReal()) {
           // Add as an integer.
           positions.AddValue(position.token_pos().Pos());
         } else {
diff --git a/runtime/vm/source_report.h b/runtime/vm/source_report.h
index 4ec20c9..246f64a 100644
--- a/runtime/vm/source_report.h
+++ b/runtime/vm/source_report.h
@@ -47,8 +47,8 @@
   // in the isolate.
   void PrintJSON(JSONStream* js,
                  const Script& script,
-                 TokenPosition start_pos = TokenPosition::kNoSource,
-                 TokenPosition end_pos = TokenPosition::kNoSource);
+                 TokenPosition start_pos = TokenPosition::kMinSource,
+                 TokenPosition end_pos = TokenPosition::kMaxSource);
 
  private:
   void ClearScriptTable();
diff --git a/runtime/vm/token_position.cc b/runtime/vm/token_position.cc
index 0f8ae68..507a528 100644
--- a/runtime/vm/token_position.cc
+++ b/runtime/vm/token_position.cc
@@ -4,34 +4,29 @@
 
 #include "vm/token_position.h"
 
+#include "vm/hash.h"
 #include "vm/object.h"
+#include "vm/zone_text_buffer.h"
 
 namespace dart {
 
-TokenPosition TokenPosition::SnapshotDecode(int32_t value) {
-  return TokenPosition(static_cast<intptr_t>(value));
+intptr_t TokenPosition::Hash() const {
+  return FinalizeHash(value_, 31);
 }
 
-int32_t TokenPosition::SnapshotEncode() {
+TokenPosition TokenPosition::Deserialize(int32_t value) {
+  return TokenPosition(value);
+}
+
+int32_t TokenPosition::Serialize() const {
   return static_cast<int32_t>(value_);
 }
 
-bool TokenPosition::IsSynthetic() const {
-  if (value_ >= kMinSourcePos) {
-    return false;
-  }
-  if (value_ < kLast.value()) {
-    return true;
-  }
-  return false;
-}
-
 #define DEFINE_VALUES(name, value)                                             \
   const TokenPosition TokenPosition::k##name(value);
 SENTINEL_TOKEN_DESCRIPTORS(DEFINE_VALUES);
 #undef DEFINE_VALUES
 const TokenPosition TokenPosition::kMinSource(kMinSourcePos);
-
 const TokenPosition TokenPosition::kMaxSource(kMaxSourcePos);
 
 const char* TokenPosition::ToCString() const {
@@ -41,17 +36,16 @@
     return #name;
     SENTINEL_TOKEN_DESCRIPTORS(DEFINE_CASE);
 #undef DEFINE_CASE
-    default: {
-      Zone* zone = Thread::Current()->zone();
-      ASSERT(zone != NULL);
-      if (IsSynthetic()) {
-        // TODO(johnmccutchan): Print synthetic positions differently.
-        return FromSynthetic().ToCString();
-      } else {
-        return OS::SCreate(zone, "%d", value_);
-      }
-    }
+    default:
+      break;
   }
+  ASSERT(IsReal() || IsSynthetic());
+  ZoneTextBuffer buffer(Thread::Current()->zone());
+  if (IsSynthetic()) {
+    buffer.AddString("syn:");
+  }
+  buffer.Printf("%" Pd32 "", value_);
+  return buffer.buffer();
 }
 
 }  // namespace dart
diff --git a/runtime/vm/token_position.h b/runtime/vm/token_position.h
index 270a14c..d15553e 100644
--- a/runtime/vm/token_position.h
+++ b/runtime/vm/token_position.h
@@ -17,14 +17,26 @@
 // ClassifyingTokenPositions 1 -> -1 - 1
 // ClassifyingTokenPositions N -> -1 - N
 //
-// Synthetically created AstNodes are given real source positions but encoded
-// as negative numbers from [kSmiMin32, -1 - N]. For example:
+// Real token positions represent source offsets in some script, and are encoded
+// as non-negative values which are equal to that offset.
 //
-// A source position of 0 in a synthetic AstNode would be encoded as -2 - N.
-// A source position of 1 in a synthetic AstNode would be encoded as -3 - N.
+// Synthetically created functions that correspond to user code are given
+// starting token positions unique from other synthetic functions. The value for
+// these token positions encode a unique non-negative value as a negative number
+// within [kSmiMin32, -1 - N).
 //
-// All other AstNodes are given real source positions encoded as positive
-// integers.
+// For example:
+// A synthetic token with value 0 is encoded as ((-1 - N) - (0 + 1)) = -2 - N.
+// A synthetic token with value 1 is encoded as ((-1 - N) - (1 + 1)) = -3 - N.
+//
+// Note that the encoded value is _not_ related to any possible real token
+// position, as two real token positions for different scripts can have the same
+// value and thus cannot serve as a unique nonce for a synthetic node.
+//
+// All other nodes read from user code, such as non-synthetic functions, fields,
+// etc., are given real starting token positions. All nodes coming from user
+// code, both real or synthetic, with ending token positions have real ending
+// token positions.
 //
 // This organization allows for ~1 billion token positions.
 
@@ -44,56 +56,112 @@
   V(DartCodeEpilogue, -13)                                                     \
   V(Last, -14)  // Always keep this at the end.
 
-// A token position representing a debug safe source (real) position,
-// non-debug safe source (synthetic) positions, or a classifying value used
+// A token position represents either a debug safe source (real) position,
+// non-debug safe unique (synthetic) position, or a classifying value used
 // by the profiler.
 class TokenPosition {
  public:
-  TokenPosition() : value_(kNoSource.value()) {}
+  intptr_t Hash() const;
 
-  explicit TokenPosition(intptr_t value) : value_(value) {}
-
+  // Returns whether the token positions are equal.  Defined for all token
+  // positions.
   bool operator==(const TokenPosition& b) const { return value() == b.value(); }
 
+  // Returns whether the token positions are not equal. Defined for all token
+  // positions.
   bool operator!=(const TokenPosition& b) const { return !(*this == b); }
 
-  bool operator<(const TokenPosition& b) const {
-    // TODO(johnmccutchan): Assert that this is a source position.
-    return value() < b.value();
+  // Returns whether the token position is less than [b]. Only defined for
+  // real token positions.
+  inline bool operator<(const TokenPosition& b) const {
+    return Pos() < b.Pos();
   }
 
-  bool operator>(const TokenPosition& b) const {
-    // TODO(johnmccutchan): Assert that this is a source position.
-    return b < *this;
+  // Returns whether the token position is greater than [b]. Only defined for
+  // real token positions.
+  inline bool operator>(const TokenPosition& b) const { return b < *this; }
+
+  // Returns whether the token position is less than or equal to [b]. Only
+  // defined for real token positions.
+  inline bool operator<=(const TokenPosition& b) const { return !(*this > b); }
+
+  // Returns whether the token position is greater than or equal to [b].  Only
+  // defined for real token positions.
+  inline bool operator>=(const TokenPosition& b) const { return !(*this < b); }
+
+  // For real token positions, returns whether this is between [a] and [b],
+  // inclusive. If [a] or [b] is non-real, they are treated as less than
+  // any real token position.
+  //
+  // For synthetic token positions, returns whether [a] or [b] equals this.
+  //
+  // For other token positions, always returns false.
+  bool IsWithin(const TokenPosition& a, const TokenPosition& b) const {
+    if (IsReal()) return (a.value() <= value()) && (value() <= b.value());
+    if (IsSynthetic()) return (a == *this) || (b == *this);
+    return false;
   }
 
-  bool operator<=(const TokenPosition& b) {
-    // TODO(johnmccutchan): Assert that this is a source position.
-    return !(*this > b);
+  // Returns [a] if both positions are not real, the real position if only one
+  // of [a] and [b] is real, or the minimum position of [a] and [b].
+  static const TokenPosition& Min(const TokenPosition& a,
+                                  const TokenPosition& b) {
+    if (!b.IsReal()) return a;
+    if (!a.IsReal()) return b;
+    return b.value() < a.value() ? b : a;
+  }
+  // Returns [a] if both positions are not real, the real position if only one
+  // of [a] and [b] is real, or the maximum position of [a] and [b].
+  static const TokenPosition& Max(const TokenPosition& a,
+                                  const TokenPosition& b) {
+    if (!b.IsReal()) return a;
+    if (!a.IsReal()) return b;
+    return b.value() > a.value() ? b : a;
   }
 
-  bool operator>=(const TokenPosition& b) {
-    // TODO(johnmccutchan): Assert that this is a source position.
-    return !(*this < b);
+  // Compares two arbitrary source positions for use in sorting, where a
+  // negative return means [a] sorts before [b], a return of 0 means [a] is the
+  // same as [b], and a positive return means [a] sorts after [b].
+  //
+  // Does _not_ correspond to the relational operators on token positions, as
+  // this also allows comparison of kNoSource, classifying, and synthetic token
+  // positions to each other.
+  static intptr_t CompareForSorting(const TokenPosition& a,
+                                    const TokenPosition& b) {
+    return a.value() - b.value();
   }
 
-  static const intptr_t kMaxSentinelDescriptors = 64;
+  static constexpr int32_t kMaxSentinelDescriptors = 64;
 
 #define DECLARE_VALUES(name, value)                                            \
-  static const intptr_t k##name##Pos = value;                                  \
+  static constexpr int32_t k##name##Pos = value;                               \
   static const TokenPosition k##name;
   SENTINEL_TOKEN_DESCRIPTORS(DECLARE_VALUES);
 #undef DECLARE_VALUES
-  static const intptr_t kMinSourcePos = 0;
+  // Check assumptions used in Is<X> methods below.
+#define CHECK_VALUES(name, value)                                              \
+  static_assert(k##name##Pos < 0, "Non-negative sentinel descriptor");         \
+  static_assert(                                                               \
+      k##name##Pos == kNoSourcePos || k##name##Pos <= kBoxPos,                 \
+      "Box sentinel descriptor is not greatest classifying sentinel value");   \
+  static_assert(kLastPos <= k##name##Pos,                                      \
+                "Last sentinel descriptor is not least sentinel valu");        \
+  SENTINEL_TOKEN_DESCRIPTORS(CHECK_VALUES);
+#undef CHECK_VALUES
+  static_assert(kLastPos > -kMaxSentinelDescriptors,
+                "More sentinel descriptors than expected");
+
+  static constexpr int32_t kMinSourcePos = 0;
   static const TokenPosition kMinSource;
-  static const intptr_t kMaxSourcePos = kSmiMax32 - kMaxSentinelDescriptors - 2;
+  static constexpr int32_t kMaxSourcePos =
+      kSmiMax32 - kMaxSentinelDescriptors - 2;
   static const TokenPosition kMaxSource;
 
-  // Decode from a snapshot.
-  static TokenPosition SnapshotDecode(int32_t value);
+  // Decode from a serialized form.
+  static TokenPosition Deserialize(int32_t value);
 
-  // Encode for writing into a snapshot.
-  int32_t SnapshotEncode();
+  // Encode into a serialized form.
+  int32_t Serialize() const;
 
   // Given a real token position, returns the next real token position.
   TokenPosition Next() {
@@ -101,88 +169,47 @@
     return TokenPosition(value_ + 1);
   }
 
-  // The raw value.
-  // TODO(johnmccutchan): Make this private.
-  intptr_t value() const { return value_; }
-
-  // Return the source position.
-  intptr_t Pos() const {
-    if (IsSynthetic()) {
-      return FromSynthetic().Pos();
-    }
+  // Return the source position for real token positions.
+  inline intptr_t Pos() const {
+    ASSERT(IsReal());
     return value_;
   }
 
   // Is |this| a classifying sentinel source position?
   // Classifying positions are used by the profiler to group instructions whose
   // cost isn't naturally attributable to a source location.
-  bool IsClassifying() const {
+  inline bool IsClassifying() const {
     return (value_ >= kBox.value()) && (value_ <= kLast.value());
   }
 
   // Is |this| the no source position sentinel?
-  bool IsNoSource() const { return *this == kNoSource; }
+  inline bool IsNoSource() const { return value_ == kNoSourcePos; }
 
   // Is |this| a synthetic source position?
   // Synthetic source positions are used by the profiler to attribute ticks to a
   // pieces of source, but ignored by the debugger as potential breakpoints.
-  bool IsSynthetic() const;
+  inline bool IsSynthetic() const { return value_ < kLastPos; }
 
   // Is |this| a real source position?
-  bool IsReal() const { return value_ >= kMinSourcePos; }
-
-  // Is |this| a source position?
-  bool IsSourcePosition() const { return IsReal() || IsSynthetic(); }
-
-  // Convert |this| into a real source position. Sentinel values remain
-  // unchanged.
-  TokenPosition SourcePosition() const { return FromSynthetic(); }
+  inline bool IsReal() const { return value_ >= kMinSourcePos; }
 
   // Is |this| a debug pause source position?
-  bool IsDebugPause() const {
-    // Sanity check some values here.
-    ASSERT(kNoSource.value() == kNoSourcePos);
-    ASSERT(kLast.value() < kNoSource.value());
-    ASSERT(kLast.value() > -kMaxSentinelDescriptors);
-    return IsReal();
-  }
+  inline bool IsDebugPause() const { return IsReal(); }
 
-  // Convert |this| into a synthetic source position. Sentinel values remain
-  // unchanged.
-  TokenPosition ToSynthetic() const {
-    const intptr_t value = value_;
-    if (IsClassifying() || IsNoSource()) {
-      return *this;
-    }
-    if (IsSynthetic()) {
-      return *this;
-    }
-    const TokenPosition synthetic_value =
-        TokenPosition((kLast.value() - 1) - value);
-    ASSERT(synthetic_value.IsSynthetic());
-    ASSERT(synthetic_value.value() < kLast.value());
-    return synthetic_value;
-  }
-
-  // Convert |this| from a synthetic source position. Sentinel values remain
-  // unchanged.
-  TokenPosition FromSynthetic() const {
-    const intptr_t synthetic_value = value_;
-    if (IsClassifying() || IsNoSource()) {
-      return *this;
-    }
-    if (!IsSynthetic()) {
-      return *this;
-    }
-    const TokenPosition value =
-        TokenPosition(-synthetic_value + (kLast.value() - 1));
-    ASSERT(!value.IsSynthetic());
-    return value;
+  // Creates a synthetic source position from a non-negative value.
+  static TokenPosition Synthetic(intptr_t value) {
+    ASSERT(value >= 0 && value <= kMaxSourcePos);
+    return TokenPosition((kLastPos - 1) - value);
   }
 
   const char* ToCString() const;
 
  private:
+  explicit TokenPosition(intptr_t value) : value_(value) {}
+
+  // The raw value of this TokenPosition.
+  intptr_t value() const { return value_; }
+
   int32_t value_;
 
   DISALLOW_ALLOCATION();
diff --git a/tools/VERSION b/tools/VERSION
index 46c6f42..c912081 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 157
+PRERELEASE 158
 PRERELEASE_PATCH 0
\ No newline at end of file