Version 2.12.0-159.0.dev

Merge commit '5021f7dc1950bd36ff77be515b2acdf653be9a06' into 'dev'
diff --git a/DEPS b/DEPS
index 37c7c81..4f282eb 100644
--- a/DEPS
+++ b/DEPS
@@ -39,7 +39,7 @@
 
   # Checked-in SDK version. The checked-in SDK is a Dart SDK distribution in a
   # cipd package used to run Dart scripts in the build and test infrastructure.
-  "sdk_tag": "version:2.12.0-0.0.dev",
+  "sdk_tag": "version:2.12.0-133.2.beta",
 
   # co19 is a cipd package. Use update.sh in tests/co19[_2] to update these
   # hashes. It requires access to the dart-build-access group, which EngProd
diff --git a/runtime/lib/errors.cc b/runtime/lib/errors.cc
index 049e919..24d84ab 100644
--- a/runtime/lib/errors.cc
+++ b/runtime/lib/errors.cc
@@ -82,23 +82,25 @@
   const Script& script = Script::Handle(FindScript(&iterator));
 
   // Initialize argument 'failed_assertion' with source snippet.
-  intptr_t from_line, from_column;
-  script.GetTokenLocation(assertion_start, &from_line, &from_column);
-  intptr_t to_line, to_column;
-  script.GetTokenLocation(assertion_end, &to_line, &to_column);
+  auto& condition_text = String::Handle();
   // Extract the assertion condition text (if source is available).
-  auto& condition_text = String::Handle(
-      script.GetSnippet(from_line, from_column, to_line, to_column));
+  intptr_t from_line = -1, from_column = -1;
+  if (script.GetTokenLocation(assertion_start, &from_line, &from_column)) {
+    // Extract the assertion condition text (if source is available).
+    intptr_t to_line, to_column;
+    script.GetTokenLocation(assertion_end, &to_line, &to_column);
+    condition_text =
+        script.GetSnippet(from_line, from_column, to_line, to_column);
+  }
   if (condition_text.IsNull()) {
     condition_text = Symbols::OptimizedOut().raw();
   }
   args.SetAt(0, condition_text);
 
   // Initialize location arguments starting at position 1.
-  // Do not set a column if the source has been generated as it will be wrong.
   args.SetAt(1, String::Handle(script.url()));
   args.SetAt(2, Smi::Handle(Smi::New(from_line)));
-  args.SetAt(3, Smi::Handle(Smi::New(script.HasSource() ? from_column : -1)));
+  args.SetAt(3, Smi::Handle(Smi::New(from_column)));
   args.SetAt(4, message);
 
   Exceptions::ThrowByType(Exceptions::kAssertion, args);
@@ -181,8 +183,8 @@
   iterator.NextFrame();  // Skip native call.
   const Script& script = Script::Handle(Exceptions::GetCallerScript(&iterator));
   args.SetAt(0, String::Handle(script.url()));
-  intptr_t line;
-  script.GetTokenLocation(fallthrough_pos, &line, NULL);
+  intptr_t line = -1;
+  script.GetTokenLocation(fallthrough_pos, &line);
   args.SetAt(1, Smi::Handle(Smi::New(line)));
 
   Exceptions::ThrowByType(Exceptions::kFallThrough, args);
@@ -208,8 +210,8 @@
   const Script& script = Script::Handle(Exceptions::GetCallerScript(&iterator));
   args.SetAt(0, class_name);
   args.SetAt(1, String::Handle(script.url()));
-  intptr_t line;
-  script.GetTokenLocation(error_pos, &line, NULL);
+  intptr_t line = -1;
+  script.GetTokenLocation(error_pos, &line);
   args.SetAt(2, Smi::Handle(Smi::New(line)));
 
   Exceptions::ThrowByType(Exceptions::kAbstractClassInstantiation, args);
diff --git a/runtime/lib/mirrors.cc b/runtime/lib/mirrors.cc
index eddf299..a3ca6fe 100644
--- a/runtime/lib/mirrors.cc
+++ b/runtime/lib/mirrors.cc
@@ -1628,17 +1628,8 @@
   }
 
   const String& uri = String::Handle(zone, script.url());
-  intptr_t from_line = 0;
-  intptr_t from_col = 0;
-  if (script.HasSource()) {
-    script.GetTokenLocation(token_pos, &from_line, &from_col);
-  } else {
-    // Avoid the slow path of printing the token stream when precise source
-    // information is not available.
-    script.GetTokenLocation(token_pos, &from_line, NULL);
-  }
-  // We should always have at least the line number.
-  ASSERT(from_line != 0);
+  intptr_t from_line = 0, from_col = 0;
+  script.GetTokenLocation(token_pos, &from_line, &from_col);
   return CreateSourceLocation(uri, from_line, from_col);
 }
 
diff --git a/runtime/platform/assert.h b/runtime/platform/assert.h
index c31c1cc..64351b6 100644
--- a/runtime/platform/assert.h
+++ b/runtime/platform/assert.h
@@ -140,7 +140,12 @@
 
 inline void Expect::StringEquals(const char* expected, const char* actual) {
   if (strcmp(expected, actual) == 0) return;
-  Fail("expected:\n<\"%s\">\nbut was:\n<\"%s\">", expected, actual);
+  if (actual == nullptr) {
+    Fail("expected:\n<\"%s\">\nbut was nullptr", expected);
+  } else {
+    if (strcmp(expected, actual) == 0) return;
+    Fail("expected:\n<\"%s\">\nbut was:\n<\"%s\">", expected, actual);
+  }
 }
 
 inline void Expect::IsSubstring(const char* needle, const char* haystack) {
diff --git a/runtime/vm/code_descriptors.cc b/runtime/vm/code_descriptors.cc
index e75d702..6d6ef31 100644
--- a/runtime/vm/code_descriptors.cc
+++ b/runtime/vm/code_descriptors.cc
@@ -437,7 +437,7 @@
 
   int32_t current_pc_offset = 0;
   function_stack->Add(&root_);
-  token_positions->Add(CodeSourceMapBuilder::kInitialPosition);
+  token_positions->Add(InitialPosition());
 
   while (stream.PendingBytes() > 0) {
     uint8_t opcode = stream.Read<uint8_t>();
@@ -459,7 +459,7 @@
         int32_t func = stream.Read<int32_t>();
         function_stack->Add(
             &Function::Handle(Function::RawCast(functions_.At(func))));
-        token_positions->Add(CodeSourceMapBuilder::kInitialPosition);
+        token_positions->Add(InitialPosition());
         break;
       }
       case CodeSourceMapBuilder::kPopFunction: {
@@ -603,7 +603,7 @@
 
   int32_t current_pc_offset = 0;
   function_stack.Add(&root_);
-  token_positions.Add(CodeSourceMapBuilder::kInitialPosition);
+  token_positions.Add(InitialPosition());
 
   THR_Print("Source positions for function '%s' {\n",
             root_.ToFullyQualifiedCString());
@@ -630,7 +630,7 @@
         int32_t func = stream.Read<int32_t>();
         function_stack.Add(
             &Function::Handle(Function::RawCast(functions_.At(func))));
-        token_positions.Add(CodeSourceMapBuilder::kInitialPosition);
+        token_positions.Add(InitialPosition());
         break;
       }
       case CodeSourceMapBuilder::kPopFunction: {
diff --git a/runtime/vm/code_descriptors.h b/runtime/vm/code_descriptors.h
index bb2f773..8bc6350 100644
--- a/runtime/vm/code_descriptors.h
+++ b/runtime/vm/code_descriptors.h
@@ -292,6 +292,16 @@
   intptr_t GetNullCheckNameIndexAt(int32_t pc_offset);
 
  private:
+  static const TokenPosition& InitialPosition() {
+    if (FLAG_precompiled_mode) {
+      // In precompiled mode, the CodeSourceMap stores lines instead of
+      // real token positions and uses kNoSourcePos for no line information.
+      return TokenPosition::kNoSource;
+    } else {
+      return CodeSourceMapBuilder::kInitialPosition;
+    }
+  }
+
   // Reads a TokenPosition value from a CSM, handling the different encoding for
   // when non-symbolic stack traces are enabled.
   static TokenPosition ReadPosition(ReadStream* stream);
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.cc b/runtime/vm/compiler/backend/flow_graph_compiler.cc
index 13200a2..7962ff2 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.cc
@@ -535,13 +535,13 @@
   }
   const Script& script =
       Script::Handle(zone(), instr->env()->function().script());
-  intptr_t line_nr;
-  intptr_t column_nr;
-  script.GetTokenLocation(instr->token_pos(), &line_nr, &column_nr);
-  const String& line = String::Handle(zone(), script.GetLine(line_nr));
-  assembler()->Comment("Line %" Pd " in '%s':\n           %s", line_nr,
-                       instr->env()->function().ToFullyQualifiedCString(),
-                       line.ToCString());
+  intptr_t line_nr, column_nr;
+  if (script.GetTokenLocation(instr->token_pos(), &line_nr, &column_nr)) {
+    const String& line = String::Handle(zone(), script.GetLine(line_nr));
+    assembler()->Comment("Line %" Pd " in '%s':\n           %s", line_nr,
+                         instr->env()->function().ToFullyQualifiedCString(),
+                         line.ToCString());
+  }
 }
 
 static bool IsPusher(Instruction* instr) {
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index 6120b67..5db0e49 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -567,7 +567,7 @@
   const TokenPosition& token_pos = TokenPos();
   if ((line_number_ < 0) && token_pos.IsReal()) {
     const Script& script = Script::Handle(SourceScript());
-    script.GetTokenLocation(token_pos, &line_number_, nullptr);
+    script.GetTokenLocation(token_pos, &line_number_, &column_number_);
   }
   return line_number_;
 }
@@ -1465,7 +1465,7 @@
   // Compute line number lazily since it causes scanning of the script.
   if (line_number_ < 0) {
     const Script& script = Script::Handle(SourceCode());
-    script.GetTokenLocation(token_pos_, &line_number_, NULL);
+    script.GetTokenLocation(token_pos_, &line_number_);
   }
   return line_number_;
 }
@@ -2241,7 +2241,8 @@
     TokenPosition token_end_pos =
         TokenPosition::Min(next_closest_token_position, end_of_line_pos);
 
-    if ((exact_token_pos.IsReal() && (token_end_pos < exact_token_pos)) ||
+    if ((token_end_pos.IsReal() && 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.
@@ -2386,8 +2387,8 @@
     const TokenPosition begin_pos = best_fit_pos;
 
     TokenPosition end_of_line_pos = TokenPosition::kNoSource;
-    if (best_line == -1) {
-      script.GetTokenLocation(begin_pos, &best_line, nullptr);
+    if (best_line < 0) {
+      script.GetTokenLocation(begin_pos, &best_line);
     }
     ASSERT(best_line > 0);
     TokenPosition ignored = TokenPosition::kNoSource;
@@ -2754,8 +2755,8 @@
     MakeCodeBreakpointAt(func, loc);
   }
   if (FLAG_verbose_debug) {
-    intptr_t line_number;
-    intptr_t column_number;
+    intptr_t line_number = -1;
+    intptr_t column_number = -1;
     script.GetTokenLocation(breakpoint_pos, &line_number, &column_number);
     OS::PrintErr("Resolved code breakpoint for function '%s' at line %" Pd
                  " col %" Pd "\n",
@@ -2818,8 +2819,8 @@
   // initializer of a field at |token_pos|. Hence, Register an unresolved
   // breakpoint.
   if (FLAG_verbose_debug) {
-    intptr_t line_number;
-    intptr_t column_number;
+    intptr_t line_number = -1;
+    intptr_t column_number = -1;
     script.GetTokenLocation(token_pos, &line_number, &column_number);
     if (func.IsNull()) {
       OS::PrintErr(
@@ -3855,12 +3856,11 @@
                                                intptr_t column_number) {
   intptr_t line;
   intptr_t col;
-  script.GetTokenLocation(start_of_line, &line, &col);
-  if (line < 0) {
-    return TokenPosition::kNoSource;
+  if (script.GetTokenLocation(start_of_line, &line, &col)) {
+    return TokenPosition::Deserialize(start_of_line.Pos() +
+                                      (column_number - col));
   }
-  return TokenPosition::Deserialize(start_of_line.Pos() +
-                                    (column_number - col));
+  return TokenPosition::kNoSource;
 }
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
diff --git a/runtime/vm/deopt_instructions.cc b/runtime/vm/deopt_instructions.cc
index 54cac1a..f9e3662 100644
--- a/runtime/vm/deopt_instructions.cc
+++ b/runtime/vm/deopt_instructions.cc
@@ -382,14 +382,15 @@
     const Function& top_function = Function::Handle(code.function());
     const Script& script = Script::Handle(top_function.script());
     const TokenPosition token_pos = code.GetTokenIndexOfPC(top_frame->pc());
-    intptr_t line, column;
-    script.GetTokenLocation(token_pos, &line, &column);
-    String& line_string = String::Handle(script.GetLine(line));
     THR_Print("  Function: %s\n", top_function.ToFullyQualifiedCString());
-    char line_buffer[80];
-    Utils::SNPrint(line_buffer, sizeof(line_buffer), "  Line %" Pd ": '%s'",
-                   line, line_string.ToCString());
-    THR_Print("%s\n", line_buffer);
+    intptr_t line;
+    if (script.GetTokenLocation(token_pos, &line)) {
+      String& line_string = String::Handle(script.GetLine(line));
+      char line_buffer[80];
+      Utils::SNPrint(line_buffer, sizeof(line_buffer), "  Line %" Pd ": '%s'",
+                     line, line_string.ToCString());
+      THR_Print("%s\n", line_buffer);
+    }
     THR_Print("  Deopt args: %" Pd "\n", deopt_arg_count);
   }
 
diff --git a/runtime/vm/exceptions.cc b/runtime/vm/exceptions.cc
index 6cc253e..20edf4d 100644
--- a/runtime/vm/exceptions.cc
+++ b/runtime/vm/exceptions.cc
@@ -919,7 +919,7 @@
       zone, script.IsNull() ? Symbols::OptimizedOut().raw() : script.url());
   intptr_t line = -1;
   intptr_t column = -1;
-  if (!script.IsNull() && location.IsReal()) {
+  if (!script.IsNull()) {
     script.GetTokenLocation(location, &line, &column);
   }
   // Initialize '_url', '_line', and '_column' arguments.
diff --git a/runtime/vm/kernel.cc b/runtime/vm/kernel.cc
index 4a65c47..dea993b 100644
--- a/runtime/vm/kernel.cc
+++ b/runtime/vm/kernel.cc
@@ -35,7 +35,7 @@
   }
 }
 
-void KernelLineStartsReader::LocationForPosition(intptr_t position,
+bool KernelLineStartsReader::LocationForPosition(intptr_t position,
                                                  intptr_t* line,
                                                  intptr_t* col) const {
   intptr_t line_count = line_starts_data_.Length();
@@ -45,46 +45,43 @@
     current_start += helper_->At(line_starts_data_, i);
     if (current_start > position) {
       *line = i;
-      if (col != NULL) {
+      if (col != nullptr) {
         *col = position - previous_start + 1;
       }
-      return;
+      return true;
     }
     if (current_start == position) {
       *line = i + 1;
-      if (col != NULL) {
+      if (col != nullptr) {
         *col = 1;
       }
-      return;
+      return true;
     }
     previous_start = current_start;
   }
 
-  // If the start of any of the lines did not cross |position|,
-  // then it means the position falls on the last line.
-  *line = line_count;
-  if (col != NULL) {
-    *col = position - current_start + 1;
-  }
+  return false;
 }
 
-void KernelLineStartsReader::TokenRangeAtLine(
-    intptr_t source_length,
+bool KernelLineStartsReader::TokenRangeAtLine(
     intptr_t line_number,
     TokenPosition* first_token_index,
     TokenPosition* last_token_index) const {
-  ASSERT(line_number <= line_starts_data_.Length());
+  if (line_number < 0 || line_number > line_starts_data_.Length()) {
+    return false;
+  }
   intptr_t cumulative = 0;
   for (intptr_t i = 0; i < line_number; ++i) {
     cumulative += helper_->At(line_starts_data_, i);
   }
   *first_token_index = dart::TokenPosition::Deserialize(cumulative);
   if (line_number == line_starts_data_.Length()) {
-    *last_token_index = dart::TokenPosition::Deserialize(source_length);
+    *last_token_index = *first_token_index;
   } else {
     *last_token_index = dart::TokenPosition::Deserialize(
         cumulative + helper_->At(line_starts_data_, line_number) - 1);
   }
+  return true;
 }
 
 int32_t KernelLineStartsReader::KernelInt8LineStartsHelper::At(
diff --git a/runtime/vm/kernel.h b/runtime/vm/kernel.h
index 79c5670..b9a582f 100644
--- a/runtime/vm/kernel.h
+++ b/runtime/vm/kernel.h
@@ -140,14 +140,21 @@
     return helper_->At(line_starts_data_, index);
   }
 
-  void LocationForPosition(intptr_t position,
-                           intptr_t* line,
-                           intptr_t* col) const;
+  // Returns whether the given offset corresponds to a valid source offset
+  // If it does, then *line and *column (if column is not nullptr) are set
+  // to the line and column the token starts at.
+  DART_WARN_UNUSED_RESULT bool LocationForPosition(
+      intptr_t position,
+      intptr_t* line,
+      intptr_t* col = nullptr) const;
 
-  void TokenRangeAtLine(intptr_t source_length,
-                        intptr_t line_number,
-                        dart::TokenPosition* first_token_index,
-                        dart::TokenPosition* last_token_index) const;
+  // Returns whether any tokens were found for the given line. When found,
+  // *first_token_index and *last_token_index are set to the first and
+  // last token on the line, respectively.
+  DART_WARN_UNUSED_RESULT bool TokenRangeAtLine(
+      intptr_t line_number,
+      dart::TokenPosition* first_token_index,
+      dart::TokenPosition* last_token_index) const;
 
  private:
   class KernelLineStartsHelper {
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 6cb5b65..8b790ce 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -9378,13 +9378,18 @@
   Zone* zone = Thread::Current()->zone();
   const Script& func_script = Script::Handle(zone, script());
 
-  intptr_t from_line;
-  intptr_t from_col;
-  intptr_t to_line;
-  intptr_t to_col;
-  intptr_t to_length;
-  func_script.GetTokenLocation(token_pos(), &from_line, &from_col);
-  func_script.GetTokenLocation(end_token_pos(), &to_line, &to_col, &to_length);
+  intptr_t from_line, from_col;
+  if (!func_script.GetTokenLocation(token_pos(), &from_line, &from_col)) {
+    return String::null();
+  }
+  intptr_t to_line, to_col;
+  if (!func_script.GetTokenLocation(end_token_pos(), &to_line, &to_col)) {
+    return String::null();
+  }
+  intptr_t to_length = func_script.GetTokenLength(end_token_pos());
+  if (to_length < 0) {
+    return String::null();
+  }
 
   if (to_length == 1) {
     // Handle special cases for end tokens of closures (where we exclude the
@@ -11269,70 +11274,79 @@
 }
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
-void Script::GetTokenLocation(TokenPosition token_pos,
+bool Script::GetTokenLocation(const TokenPosition& token_pos,
                               intptr_t* line,
-                              intptr_t* column,
-                              intptr_t* token_len) const {
+                              intptr_t* column) const {
   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;
-  }
+#if defined(DART_PRECOMPILED_RUNTIME)
   // Scripts in the AOT snapshot do not have a line starts array.
-#if !defined(DART_PRECOMPILED_RUNTIME)
-  if (!token_pos.IsReal()) return;
+  return false;
+#else
+  if (!token_pos.IsReal()) return false;
 
   auto const zone = Thread::Current()->zone();
   LookupSourceAndLineStarts(zone);
   const TypedData& line_starts_data = TypedData::Handle(zone, line_starts());
+  if (line_starts_data.IsNull()) return false;
   kernel::KernelLineStartsReader line_starts_reader(line_starts_data, zone);
-  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;
-      }
-    }
-  }
-#endif  // !defined(DART_PRECOMPILED_RUNTIME)
+  return line_starts_reader.LocationForPosition(token_pos.Pos(), line, column);
+#endif  // defined(DART_PRECOMPILED_RUNTIME)
 }
 
-void Script::TokenRangeAtLine(intptr_t line_number,
+intptr_t Script::GetTokenLength(const TokenPosition& token_pos) const {
+#if defined(DART_PRECOMPILED_RUNTIME)
+  // Scripts in the AOT snapshot do not have their source.
+  return -1;
+#else
+  if (!HasSource() || !token_pos.IsReal()) return -1;
+  auto const zone = Thread::Current()->zone();
+  LookupSourceAndLineStarts(zone);
+  // We don't explicitly save this data: Load the source and find it from there.
+  const String& source = String::Handle(zone, Source());
+  const intptr_t start = token_pos.Pos();
+  if (start >= source.Length()) return -1;  // Can't determine token_len.
+  intptr_t end = start;
+  if (IsIdentStartChar(source.CharAt(end++))) {
+    for (; end < source.Length(); ++end) {
+      if (!IsIdentChar(source.CharAt(end))) break;
+    }
+  }
+  return end - start;
+#endif
+}
+
+bool Script::TokenRangeAtLine(intptr_t line_number,
                               TokenPosition* first_token_index,
                               TokenPosition* last_token_index) const {
-  ASSERT(first_token_index != NULL && last_token_index != NULL);
-  ASSERT(line_number > 0);
-
+  ASSERT(first_token_index != nullptr && last_token_index != nullptr);
+#if defined(DART_PRECOMPILED_RUNTIME)
   // Scripts in the AOT snapshot do not have a line starts array.
-#if !defined(DART_PRECOMPILED_RUNTIME)
+  return false;
+#else
+  // Line numbers are 1-indexed.
+  if (line_number <= 0) return false;
   Zone* zone = Thread::Current()->zone();
   LookupSourceAndLineStarts(zone);
-  const String& source = String::Handle(zone, Source());
+  const TypedData& line_starts_data = TypedData::Handle(zone, line_starts());
+  kernel::KernelLineStartsReader line_starts_reader(line_starts_data, zone);
+  if (!line_starts_reader.TokenRangeAtLine(line_number, first_token_index,
+                                           last_token_index)) {
+    return false;
+  }
+#if defined(DEBUG)
   intptr_t source_length;
-  if (source.IsNull()) {
+  if (!HasSource()) {
     Smi& value = Smi::Handle(zone);
     const Array& debug_positions_array = Array::Handle(zone, debug_positions());
     value ^= debug_positions_array.At(debug_positions_array.Length() - 1);
     source_length = value.Value();
   } else {
+    const String& source = String::Handle(zone, Source());
     source_length = source.Length();
   }
-  const TypedData& line_starts_data = TypedData::Handle(zone, line_starts());
-  kernel::KernelLineStartsReader line_starts_reader(line_starts_data, zone);
-  line_starts_reader.TokenRangeAtLine(source_length, line_number,
-                                      first_token_index, last_token_index);
+  ASSERT(last_token_index->Serialize() <= source_length);
+#endif
+  return true;
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 }
 
@@ -11349,7 +11363,8 @@
                                        intptr_t column = 1,
                                        intptr_t column_offset = 0,
                                        intptr_t starting_index = 0) {
-  if (starting_index < 0 || line < 1 || column < 1) {
+  if (starting_index < 0 || line < 1 || column < 1 || line <= line_offset ||
+      (line == line_offset + 1 && column <= column_offset)) {
     return -1;
   }
   intptr_t len = src.Length();
@@ -11391,10 +11406,10 @@
 }
 
 StringPtr Script::GetLine(intptr_t line_number, Heap::Space space) const {
-  const String& src = String::Handle(Source());
-  if (src.IsNull()) {
+  if (!HasSource()) {
     return Symbols::OptimizedOut().raw();
   }
+  const String& src = String::Handle(Source());
   const intptr_t start =
       GetRelativeSourceIndex(src, line_number, line_offset());
   if (start < 0) {
@@ -11414,11 +11429,10 @@
                              intptr_t from_column,
                              intptr_t to_line,
                              intptr_t to_column) const {
-  const String& src = String::Handle(Source());
-  if (src.IsNull()) {
+  if (!HasSource()) {
     return Symbols::OptimizedOut().raw();
   }
-
+  const String& src = String::Handle(Source());
   const intptr_t start = GetRelativeSourceIndex(src, from_line, line_offset(),
                                                 from_column, col_offset());
   // Lines and columns are 1-based, so need to subtract one to get offsets.
@@ -24209,8 +24223,9 @@
 static void PrintSymbolicStackFrame(Zone* zone,
                                     BaseTextBuffer* buffer,
                                     const Function& function,
-                                    TokenPosition token_pos,
-                                    intptr_t frame_index) {
+                                    TokenPosition token_pos_or_line,
+                                    intptr_t frame_index,
+                                    bool is_line = false) {
   ASSERT(!function.IsNull());
   const auto& script = Script::Handle(zone, function.script());
   const char* function_name = function.QualifiedUserVisibleNameCString();
@@ -24226,13 +24241,14 @@
 
   intptr_t line = -1;
   intptr_t column = -1;
-  if (token_pos.IsReal()) {
-    if (FLAG_precompiled_mode) {
-      line = token_pos.Pos();
-    } else {
-      ASSERT(!script.IsNull());
-      script.GetTokenLocation(token_pos, &line, &column);
+  if (is_line) {
+    ASSERT(token_pos_or_line.IsNoSource() || token_pos_or_line.IsReal());
+    if (token_pos_or_line.IsReal()) {
+      line = token_pos_or_line.Pos();
     }
+  } else {
+    ASSERT(!script.IsNull());
+    script.GetTokenLocation(token_pos_or_line, &line, &column);
   }
   PrintSymbolicStackFrameIndex(buffer, frame_index);
   PrintSymbolicStackFrameBody(buffer, function_name, url, line, column);
@@ -24386,7 +24402,8 @@
         for (intptr_t j = inlined_functions.length() - 1; j >= 0; j--) {
           const auto& inlined = *inlined_functions[j];
           auto const pos = inlined_token_positions[j];
-          PrintSymbolicStackFrame(zone, &buffer, inlined, pos, frame_index);
+          PrintSymbolicStackFrame(zone, &buffer, inlined, pos, frame_index,
+                                  /*is_line=*/FLAG_precompiled_mode);
           frame_index++;
         }
         continue;
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index b94c5a3..3fe0d69 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -4543,16 +4543,20 @@
 
   void SetLocationOffset(intptr_t line_offset, intptr_t col_offset) const;
 
-  void GetTokenLocation(TokenPosition token_pos,
+  // Returns whether a line and column could be computed for the given token
+  // position and, if so, sets *line and *column (if not nullptr).
+  bool GetTokenLocation(const TokenPosition& token_pos,
                         intptr_t* line,
-                        intptr_t* column,
-                        intptr_t* token_len = NULL) const;
+                        intptr_t* column = nullptr) const;
 
-  // Returns index of first and last token on the given line. Returns both
-  // indices < 0 if no token exists on or after the line. If a token exists
-  // after, but not on given line, returns in *first_token_index the index of
-  // the first token after the line, and a negative value in *last_token_index.
-  void TokenRangeAtLine(intptr_t line_number,
+  // Returns the length of the token at the given position. If the length cannot
+  // be determined, returns a negative value.
+  intptr_t GetTokenLength(const TokenPosition& token_pos) const;
+
+  // Returns whether any tokens were found for the given line. When found,
+  // *first_token_index and *last_token_index are set to the first and
+  // last token on the line, respectively.
+  bool TokenRangeAtLine(intptr_t line_number,
                         TokenPosition* first_token_index,
                         TokenPosition* last_token_index) const;
 
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index fdd6784..5b280f2 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -292,7 +292,7 @@
   const Script& scr = Script::Handle(cls.script());
   intptr_t line;
   intptr_t col;
-  scr.GetTokenLocation(end_token_pos, &line, &col);
+  EXPECT(scr.GetTokenLocation(end_token_pos, &line, &col));
   EXPECT_EQ(9, line);
   EXPECT_EQ(1, col);
 }
diff --git a/runtime/vm/profiler_test.cc b/runtime/vm/profiler_test.cc
index 048a8bf..612e81a 100644
--- a/runtime/vm/profiler_test.cc
+++ b/runtime/vm/profiler_test.cc
@@ -262,36 +262,36 @@
 
   const char* CurrentToken() {
     if (!as_functions_) {
-      return NULL;
+      return nullptr;
     }
     ProfileFunction* func = GetFunction();
     const Function& function = *(func->function());
     if (function.IsNull()) {
       // No function.
-      return NULL;
+      return nullptr;
     }
     Zone* zone = Thread::Current()->zone();
     const Script& script = Script::Handle(zone, function.script());
     if (script.IsNull()) {
       // No script.
-      return NULL;
+      return nullptr;
     }
     ProfileFunctionSourcePosition pfsp(TokenPosition::kNoSource);
     if (!func->GetSinglePosition(&pfsp)) {
       // Not exactly one source position.
-      return NULL;
-    }
-    TokenPosition token_pos = pfsp.token_pos();
-    if (!token_pos.IsReal()) {
-      // Not a location in a script.
-      return NULL;
+      return nullptr;
     }
 
-    intptr_t line = 0, column = 0, token_len = 0;
-    script.GetTokenLocation(token_pos, &line, &column, &token_len);
-    const auto& str = String::Handle(
-        zone, script.GetSnippet(line, column, line, column + token_len));
-    return str.IsNull() ? NULL : str.ToCString();
+    const TokenPosition& token_pos = pfsp.token_pos();
+    intptr_t line, column;
+    if (script.GetTokenLocation(token_pos, &line, &column)) {
+      const intptr_t token_len = script.GetTokenLength(token_pos);
+      const auto& str = String::Handle(
+          zone, script.GetSnippet(line, column, line, column + token_len));
+      if (!str.IsNull()) return str.ToCString();
+    }
+    // Couldn't get line/number information.
+    return nullptr;
   }
 
   intptr_t CurrentInclusiveTicks() {
diff --git a/runtime/vm/report.cc b/runtime/vm/report.cc
index ded367d6..069b3be 100644
--- a/runtime/vm/report.cc
+++ b/runtime/vm/report.cc
@@ -38,29 +38,20 @@
       UNREACHABLE();
   }
   String& result = String::Handle();
-  if (!script.IsNull() && !String::Handle(script.Source()).IsNull()) {
+  if (!script.IsNull() && script.HasSource()) {
     const String& script_url = String::Handle(script.url());
-    if (token_pos.IsReal()) {
-      intptr_t line, column, token_len;
-      script.GetTokenLocation(token_pos, &line, &column, &token_len);
+    intptr_t line, column;
+    if (script.GetTokenLocation(token_pos, &line, &column)) {
+      const intptr_t token_len = script.GetTokenLength(token_pos);
       if (report_after_token) {
-        column += token_len;
+        column += token_len < 0 ? 1 : token_len;
       }
-      // Only report the line position if we have the original source. We still
-      // need to get a valid column so that we can report the ^ mark below the
-      // snippet.
       // Allocate formatted strings in old space as they may be created during
       // optimizing compilation. Those strings are created rarely and should not
       // polute old space.
-      if (script.HasSource()) {
-        result = String::NewFormatted(
-            Heap::kOld, "'%s': %s: line %" Pd " pos %" Pd ": ",
-            script_url.ToCString(), message_header, line, column);
-      } else {
-        result =
-            String::NewFormatted(Heap::kOld, "'%s': %s: line %" Pd ": ",
-                                 script_url.ToCString(), message_header, line);
-      }
+      result = String::NewFormatted(
+          Heap::kOld, "'%s': %s: line %" Pd " pos %" Pd ": ",
+          script_url.ToCString(), message_header, line, column);
       // Append the formatted error or warning message.
       const Array& strs = Array::Handle(Array::New(6, Heap::kOld));
       strs.SetAt(0, result);
diff --git a/runtime/vm/simulator_arm.cc b/runtime/vm/simulator_arm.cc
index 020839e..67c7cbb 100644
--- a/runtime/vm/simulator_arm.cc
+++ b/runtime/vm/simulator_arm.cc
@@ -281,15 +281,18 @@
   const Script& script = Script::Handle(function.script());
   const String& func_name = String::Handle(function.QualifiedScrubbedName());
   const String& url = String::Handle(script.url());
-  intptr_t line = -1;
-  intptr_t column = -1;
-  if (token_pos.IsReal()) {
-    script.GetTokenLocation(token_pos, &line, &column);
+  intptr_t line, column;
+  if (script.GetTokenLocation(token_pos, &line, &column)) {
+    OS::PrintErr(
+        "pc=0x%" Px " fp=0x%" Px " sp=0x%" Px " %s%s (%s:%" Pd ":%" Pd ")", pc,
+        fp, sp, is_optimized ? (is_inlined ? "inlined " : "optimized ") : "",
+        func_name.ToCString(), url.ToCString(), line, column);
+
+  } else {
+    OS::PrintErr("pc=0x%" Px " fp=0x%" Px " sp=0x%" Px " %s%s (%s)", pc, fp, sp,
+                 is_optimized ? (is_inlined ? "inlined " : "optimized ") : "",
+                 func_name.ToCString(), url.ToCString());
   }
-  OS::PrintErr(
-      "pc=0x%" Px " fp=0x%" Px " sp=0x%" Px " %s%s (%s:%" Pd ":%" Pd ")", pc,
-      fp, sp, is_optimized ? (is_inlined ? "inlined " : "optimized ") : "",
-      func_name.ToCString(), url.ToCString(), line, column);
 #if defined(DART_PRECOMPILED_RUNTIME)
   intptr_t offset;
   auto const symbol_name =
diff --git a/runtime/vm/simulator_arm64.cc b/runtime/vm/simulator_arm64.cc
index 24936ec..b39c371 100644
--- a/runtime/vm/simulator_arm64.cc
+++ b/runtime/vm/simulator_arm64.cc
@@ -309,15 +309,17 @@
   const Script& script = Script::Handle(function.script());
   const String& func_name = String::Handle(function.QualifiedScrubbedName());
   const String& url = String::Handle(script.url());
-  intptr_t line = -1;
-  intptr_t column = -1;
-  if (token_pos.IsReal()) {
-    script.GetTokenLocation(token_pos, &line, &column);
+  intptr_t line, column;
+  if (script.GetTokenLocation(token_pos, &line, &column)) {
+    OS::PrintErr(
+        "pc=0x%" Px " fp=0x%" Px " sp=0x%" Px " %s%s (%s:%" Pd ":%" Pd ")", pc,
+        fp, sp, is_optimized ? (is_inlined ? "inlined " : "optimized ") : "",
+        func_name.ToCString(), url.ToCString(), line, column);
+  } else {
+    OS::PrintErr("pc=0x%" Px " fp=0x%" Px " sp=0x%" Px " %s%s (%s)", pc, fp, sp,
+                 is_optimized ? (is_inlined ? "inlined " : "optimized ") : "",
+                 func_name.ToCString(), url.ToCString());
   }
-  OS::PrintErr(
-      "pc=0x%" Px " fp=0x%" Px " sp=0x%" Px " %s%s (%s:%" Pd ":%" Pd ")", pc,
-      fp, sp, is_optimized ? (is_inlined ? "inlined " : "optimized ") : "",
-      func_name.ToCString(), url.ToCString(), line, column);
 #if defined(DART_PRECOMPILED_RUNTIME)
   intptr_t offset;
   auto const symbol_name =
diff --git a/tools/VERSION b/tools/VERSION
index c912081..5e98251 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 158
+PRERELEASE 159
 PRERELEASE_PATCH 0
\ No newline at end of file