Version 2.12.0-82.0.dev

Merge commit '7837c384c00eb56058a66dcf72920cf9fc542eed' into 'dev'
diff --git a/DEPS b/DEPS
index 48e6ed1..8dd67be 100644
--- a/DEPS
+++ b/DEPS
@@ -130,7 +130,7 @@
   "path_rev": "62ecd5a78ffe5734d14ed0df76d20309084cd04a",
   "pedantic_rev": "a884ea2db943b8756cc94385990bd750aec06928",
   "ply_rev": "604b32590ffad5cbb82e4afef1d305512d06ae93",
-  "pool_rev": "eedbd5fde84f9a1a8da643b475305a81841da599",
+  "pool_rev": "7abe634002a1ba8a0928eded086062f1307ccfae",
   "protobuf_rev": "0d03fd588df69e9863e2a2efc0059dee8f18d5b2",
   "pub_rev": "228e69e53862879c283c42b98086aa7536012a66",
   "pub_semver_rev": "10569a1e867e909cf5db201f73118020453e5db8",
diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status
index e3e32f3..3e25e86 100644
--- a/runtime/tests/vm/vm.status
+++ b/runtime/tests/vm/vm.status
@@ -139,6 +139,10 @@
 [ $nnbd == legacy ]
 dart/*: SkipByDesign # Migrated tests are not supposed to run on non-NNBD bots.
 
+[ $runtime == vm ]
+dart/isolates/*: Pass, Slow # https://dartbug.com/36097: Slower while isolate groups are being gradually enabled in JIT mode.
+dart_2/isolates/*: Pass, Slow # https://dartbug.com/36097: Slower while isolate groups are being gradually enabled in JIT mode.
+
 [ $system == android ]
 dart/isolates/dart_api_create_lightweight_isolate_test: SkipByDesign # On android this test does not work due to not being able to identify library uri.
 dart/sdk_hash_test: SkipByDesign # The test doesn't know location of cross-platform gen_snapshot
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.cc b/runtime/vm/compiler/backend/flow_graph_compiler.cc
index c06d36d..213bb7a 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.cc
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.cc
@@ -1890,7 +1890,8 @@
     const String& target_name,
     const Array& arguments_descriptor,
     intptr_t num_args_tested,
-    const AbstractType& receiver_type) {
+    const AbstractType& receiver_type,
+    const Function& binary_smi_target) {
   if ((deopt_id_to_ic_data_ != NULL) &&
       ((*deopt_id_to_ic_data_)[deopt_id] != NULL)) {
     const ICData* res = (*deopt_id_to_ic_data_)[deopt_id];
@@ -1903,10 +1904,24 @@
     ASSERT(res->receivers_static_type() == receiver_type.raw());
     return res;
   }
-  const ICData& ic_data = ICData::ZoneHandle(
-      zone(), ICData::New(parsed_function().function(), target_name,
+
+  auto& ic_data = ICData::ZoneHandle(zone());
+  if (!binary_smi_target.IsNull()) {
+    ASSERT(num_args_tested == 2);
+    ASSERT(!binary_smi_target.IsNull());
+    GrowableArray<intptr_t> cids(num_args_tested);
+    cids.Add(kSmiCid);
+    cids.Add(kSmiCid);
+    ic_data = ICData::NewWithCheck(parsed_function().function(), target_name,
+                                   arguments_descriptor, deopt_id,
+                                   num_args_tested, ICData::kInstance, &cids,
+                                   binary_smi_target, receiver_type);
+  } else {
+    ic_data = ICData::New(parsed_function().function(), target_name,
                           arguments_descriptor, deopt_id, num_args_tested,
-                          ICData::kInstance, receiver_type));
+                          ICData::kInstance, receiver_type);
+  }
+
   if (deopt_id_to_ic_data_ != NULL) {
     (*deopt_id_to_ic_data_)[deopt_id] = &ic_data;
   }
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.h b/runtime/vm/compiler/backend/flow_graph_compiler.h
index 729c639..33f2627 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.h
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.h
@@ -911,11 +911,15 @@
   bool may_reoptimize() const { return may_reoptimize_; }
 
   // Use in unoptimized compilation to preserve/reuse ICData.
+  //
+  // If [binary_smi_target] is non-null and we have to create the ICData, the
+  // ICData will get an (kSmiCid, kSmiCid, binary_smi_target) entry.
   const ICData* GetOrAddInstanceCallICData(intptr_t deopt_id,
                                            const String& target_name,
                                            const Array& arguments_descriptor,
                                            intptr_t num_args_tested,
-                                           const AbstractType& receiver_type);
+                                           const AbstractType& receiver_type,
+                                           const Function& binary_smi_target);
 
   const ICData* GetOrAddStaticCallICData(intptr_t deopt_id,
                                          const Function& target,
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 4042a04..bd8e8b7 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -4815,8 +4815,36 @@
   }
 }
 
+static FunctionPtr FindBinarySmiOp(Zone* zone, const String& name) {
+  const auto& smi_class = Class::Handle(zone, Smi::Class());
+  auto& smi_op_target = Function::Handle(
+      zone, Resolver::ResolveDynamicAnyArgs(zone, smi_class, name));
+
+#if !defined(DART_PRECOMPILED_RUNTIME)
+  if (smi_op_target.IsNull() &&
+      Function::IsDynamicInvocationForwarderName(name)) {
+    const String& demangled = String::Handle(
+        zone, Function::DemangleDynamicInvocationForwarderName(name));
+    smi_op_target = Resolver::ResolveDynamicAnyArgs(zone, smi_class, demangled);
+  }
+#endif
+  return smi_op_target.raw();
+}
+
 void InstanceCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
   Zone* zone = compiler->zone();
+
+  UpdateReceiverSminess(zone);
+
+  auto& specialized_binary_smi_ic_stub = Code::ZoneHandle(zone);
+  auto& binary_smi_op_target = Function::Handle(zone);
+  if (!receiver_is_not_smi()) {
+    specialized_binary_smi_ic_stub = TwoArgsSmiOpInlineCacheEntry(token_kind());
+    if (!specialized_binary_smi_ic_stub.IsNull()) {
+      binary_smi_op_target = FindBinarySmiOp(zone, function_name());
+    }
+  }
+
   const ICData* call_ic_data = NULL;
   if (!FLAG_propagate_ic_data || !compiler->is_optimizing() ||
       (ic_data() == NULL)) {
@@ -4830,13 +4858,11 @@
 
     call_ic_data = compiler->GetOrAddInstanceCallICData(
         deopt_id(), function_name(), arguments_descriptor,
-        checked_argument_count(), receivers_static_type);
+        checked_argument_count(), receivers_static_type, binary_smi_op_target);
   } else {
     call_ic_data = &ICData::ZoneHandle(zone, ic_data()->raw());
   }
 
-  UpdateReceiverSminess(zone);
-
   if (compiler->is_optimizing() && HasICData()) {
     if (ic_data()->NumberOfUsedChecks() > 0) {
       const ICData& unary_ic_data =
@@ -4854,18 +4880,27 @@
     // Unoptimized code.
     compiler->AddCurrentDescriptor(PcDescriptorsLayout::kRewind, deopt_id(),
                                    token_pos());
-    bool is_smi_two_args_op = false;
-    const Code& stub =
-        Code::ZoneHandle(TwoArgsSmiOpInlineCacheEntry(token_kind()));
-    if (!stub.IsNull()) {
-      // We have a dedicated inline cache stub for this operation, add an
-      // an initial Smi/Smi check with count 0.
-      is_smi_two_args_op = call_ic_data->AddSmiSmiCheckForFastSmiStubs();
+
+    // If the ICData contains a (Smi, Smi, <binary-smi-op-target>) stub already
+    // we will call the specialized IC Stub that works as a normal IC Stub but
+    // has inlined fast path for the specific Smi operation.
+    bool use_specialized_smi_ic_stub = false;
+    if (!specialized_binary_smi_ic_stub.IsNull() &&
+        call_ic_data->NumberOfChecksIs(1)) {
+      GrowableArray<intptr_t> class_ids(2);
+      auto& target = Function::Handle();
+      call_ic_data->GetCheckAt(0, &class_ids, &target);
+      if (class_ids[0] == kSmiCid && class_ids[1] == kSmiCid &&
+          target.raw() == binary_smi_op_target.raw()) {
+        use_specialized_smi_ic_stub = true;
+      }
     }
-    if (is_smi_two_args_op) {
+
+    if (use_specialized_smi_ic_stub) {
       ASSERT(ArgumentCount() == 2);
-      compiler->EmitInstanceCallJIT(stub, *call_ic_data, deopt_id(),
-                                    token_pos(), locs(), entry_kind());
+      compiler->EmitInstanceCallJIT(specialized_binary_smi_ic_stub,
+                                    *call_ic_data, deopt_id(), token_pos(),
+                                    locs(), entry_kind());
     } else {
       compiler->GenerateInstanceCall(deopt_id(), token_pos(), locs(),
                                      *call_ic_data, entry_kind(),
diff --git a/runtime/vm/flag_list.h b/runtime/vm/flag_list.h
index 022032b..dd6c196 100644
--- a/runtime/vm/flag_list.h
+++ b/runtime/vm/flag_list.h
@@ -119,7 +119,7 @@
   P(enable_ffi, bool, true, "Disable to make importing dart:ffi an error.")    \
   P(fields_may_be_reset, bool, false,                                          \
     "Don't optimize away static field initialization")                         \
-  C(force_clone_compiler_objects, false, false, bool, false,                   \
+  P(force_clone_compiler_objects, bool, false,                                 \
     "Force cloning of objects needed in compiler (ICData and Field).")         \
   P(getter_setter_ratio, int, 13,                                              \
     "Ratio of getter/setter usage used for double field unboxing heuristics")  \
diff --git a/runtime/vm/flags.cc b/runtime/vm/flags.cc
index 901856c..f3fc0ef 100644
--- a/runtime/vm/flags.cc
+++ b/runtime/vm/flags.cc
@@ -466,6 +466,30 @@
     PrintFlags();
   }
 
+  // TODO(dartbug.com/36097): Support for isolate groups in JIT mode is
+  // in-development. We will start with very conservative settings. As we make
+  // more of our compiler, runtime as well as generated code re-entrant we'll
+  // graudally remove those restrictions.
+
+#if !defined(DART_PRCOMPILED_RUNTIME)
+  if (!FLAG_precompiled_mode && FLAG_enable_isolate_groups) {
+    // Our compiler should not make rely on a global field being initialized at
+    // compile-time, since that compiled code might be re-used in another
+    // isolate that has not yet initialized the global field.
+    FLAG_fields_may_be_reset = true;
+
+    // We will start by only allowing compilation to unoptimized code.
+    FLAG_optimization_counter_threshold = -1;
+    FLAG_background_compilation = false;
+    FLAG_force_clone_compiler_objects = true;
+
+    // To eliminate potential flakiness, we will start by disabling field guards
+    // and CHA-based compilations.
+    FLAG_use_field_guards = false;
+    FLAG_use_cha_deopt = false;
+  }
+#endif  // !defined(DART_PRCOMPILED_RUNTIME)
+
   initialized_ = true;
   return NULL;
 }
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 9c47fa6..88ec7dc 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -11326,51 +11326,78 @@
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 }
 
+// Returns the index in the given source string for the given (1-based) absolute
+// line and column numbers. The line and column offsets are used to calculate
+// the absolute line and column number for the starting index in the source.
+//
+// If the given line number is outside the range of lines represented by the
+// source, the given column number invalid for the given line, or a negative
+// starting index is given, a negative value is returned to indicate failure.
+static intptr_t GetRelativeSourceIndex(const String& src,
+                                       intptr_t line,
+                                       intptr_t line_offset = 0,
+                                       intptr_t column = 1,
+                                       intptr_t column_offset = 0,
+                                       intptr_t starting_index = 0) {
+  if (starting_index < 0 || line < 1 || column < 1) {
+    return -1;
+  }
+  intptr_t len = src.Length();
+  intptr_t current_line = line_offset + 1;
+  intptr_t current_index = starting_index;
+  for (; current_index < len; current_index++) {
+    if (current_line == line) {
+      break;
+    }
+    const uint16_t c = src.CharAt(current_index);
+    if (c == '\n' || c == '\r') {
+      current_line++;
+    }
+    if (c == '\r' && current_index + 1 < len &&
+        src.CharAt(current_index + 1) == '\n') {
+      // \r\n is treated as a single line terminator.
+      current_index++;
+    }
+  }
+  if (current_line != line) {
+    return -1;
+  }
+  // Only adjust with column offset when still on the first line.
+  intptr_t current_column = 1 + (line == line_offset + 1 ? column_offset : 0);
+  for (; current_index < len; current_index++, current_column++) {
+    if (current_column == column) {
+      return current_index;
+    }
+    const uint16_t c = src.CharAt(current_index);
+    if (c == '\n' || c == '\r') {
+      break;
+    }
+  }
+  // Check for a column value representing the source's end.
+  if (current_column == column) {
+    return current_index;
+  }
+  return -1;
+}
+
 StringPtr Script::GetLine(intptr_t line_number, Heap::Space space) const {
   const String& src = String::Handle(Source());
   if (src.IsNull()) {
     return Symbols::OptimizedOut().raw();
   }
-  intptr_t target_line = line_number - line_offset();
-  intptr_t current_line = 1;
-  intptr_t start = 0;
-  // First find the right line, if present...
-  for (; start < src.Length(); start++) {
-    if (current_line == target_line) {
+  const intptr_t start =
+      GetRelativeSourceIndex(src, line_number, line_offset());
+  if (start < 0) {
+    return Symbols::Empty().raw();
+  }
+  intptr_t end = start;
+  for (; end < src.Length(); end++) {
+    const uint16_t c = src.CharAt(end);
+    if (c == '\n' || c == '\r') {
       break;
     }
-    const uint16_t c = src.CharAt(start);
-    // Only count '\r' as a line terminator if not followed by a '\n'.
-    if (c == '\n' || (c == '\r' && (start + 1 >= src.Length() ||
-                                    src.CharAt(start + 1) != '\n'))) {
-      current_line++;
-    }
   }
-  if (current_line == target_line) {
-    // ... and then find its end, excluding any line terminator.
-    intptr_t end = start;
-    for (; end < src.Length(); end++) {
-      const uint16_t c = src.CharAt(end);
-      if (c == '\n' || c == '\r') {
-        break;
-      }
-    }
-    // Return the contents of the line.
-    return String::SubString(src, start, end - start, space);
-  }
-
-  // Not found, so return the empty string.
-  return Symbols::Empty().raw();
-}
-
-StringPtr Script::GetSnippet(TokenPosition from, TokenPosition to) const {
-  intptr_t from_line;
-  intptr_t from_column;
-  intptr_t to_line;
-  intptr_t to_column;
-  GetTokenLocation(from, &from_line, &from_column);
-  GetTokenLocation(to, &to_line, &to_column);
-  return GetSnippet(from_line, from_column, to_line, to_column);
+  return String::SubString(src, start, end - start, space);
 }
 
 StringPtr Script::GetSnippet(intptr_t from_line,
@@ -11381,49 +11408,17 @@
   if (src.IsNull()) {
     return Symbols::OptimizedOut().raw();
   }
-  intptr_t length = src.Length();
-  intptr_t line = 1 + line_offset();
-  intptr_t column = 1;
-  intptr_t scan_position = 0;
-  intptr_t snippet_start = -1;
-  intptr_t snippet_end = -1;
-  if (from_line - line_offset() == 1) {
-    column += col_offset();
-  }
 
-  while (scan_position != length) {
-    if (snippet_start == -1) {
-      if ((line == from_line) && (column == from_column)) {
-        snippet_start = scan_position;
-      }
-    }
-
-    char c = src.CharAt(scan_position);
-    if (c == '\n') {
-      line++;
-      column = 0;
-    } else if (c == '\r') {
-      line++;
-      column = 0;
-      if ((scan_position + 1 != length) &&
-          (src.CharAt(scan_position + 1) == '\n')) {
-        scan_position++;
-      }
-    }
-    scan_position++;
-    column++;
-
-    if ((line == to_line) && (column == to_column)) {
-      snippet_end = scan_position;
-      break;
-    }
+  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.
+  const intptr_t end = GetRelativeSourceIndex(
+      src, to_line, from_line - 1, to_column, from_column - 1, start);
+  // Only need to check end, because a negative start results in a negative end.
+  if (end < 0) {
+    return String::null();
   }
-  String& snippet = String::Handle();
-  if ((snippet_start != -1) && (snippet_end != -1)) {
-    snippet =
-        String::SubString(src, snippet_start, snippet_end - snippet_start);
-  }
-  return snippet.raw();
+  return String::SubString(src, start, end - start);
 }
 
 ScriptPtr Script::New() {
@@ -15215,46 +15210,6 @@
   }
 }
 
-// Add an initial Smi/Smi check with count 0.
-bool ICData::AddSmiSmiCheckForFastSmiStubs() const {
-  bool is_smi_two_args_op = false;
-
-  ASSERT(NumArgsTested() == 2);
-  Zone* zone = Thread::Current()->zone();
-  const String& name = String::Handle(zone, target_name());
-  const Class& smi_class = Class::Handle(zone, Smi::Class());
-  Function& smi_op_target = Function::Handle(
-      zone, Resolver::ResolveDynamicAnyArgs(zone, smi_class, name));
-
-#if !defined(DART_PRECOMPILED_RUNTIME)
-  if (smi_op_target.IsNull() &&
-      Function::IsDynamicInvocationForwarderName(name)) {
-    const String& demangled = String::Handle(
-        zone, Function::DemangleDynamicInvocationForwarderName(name));
-    smi_op_target = Resolver::ResolveDynamicAnyArgs(zone, smi_class, demangled);
-  }
-#endif
-
-  if (NumberOfChecksIs(0)) {
-    GrowableArray<intptr_t> class_ids(2);
-    class_ids.Add(kSmiCid);
-    class_ids.Add(kSmiCid);
-    AddCheck(class_ids, smi_op_target);
-    // 'AddCheck' sets the initial count to 1.
-    SetCountAt(0, 0);
-    is_smi_two_args_op = true;
-  } else if (NumberOfChecksIs(1)) {
-    GrowableArray<intptr_t> class_ids(2);
-    Function& target = Function::Handle();
-    GetCheckAt(0, &class_ids, &target);
-    if ((target.raw() == smi_op_target.raw()) && (class_ids[0] == kSmiCid) &&
-        (class_ids[1] == kSmiCid)) {
-      is_smi_two_args_op = true;
-    }
-  }
-  return is_smi_two_args_op;
-}
-
 bool ICData::ValidateInterceptor(const Function& target) const {
 #if !defined(DART_PRECOMPILED_RUNTIME)
   const String& name = String::Handle(target_name());
@@ -15847,41 +15802,63 @@
   return result.raw();
 }
 
+ICDataPtr ICData::NewWithCheck(const Function& owner,
+                               const String& target_name,
+                               const Array& arguments_descriptor,
+                               intptr_t deopt_id,
+                               intptr_t num_args_tested,
+                               RebindRule rebind_rule,
+                               GrowableArray<intptr_t>* cids,
+                               const Function& target,
+                               const AbstractType& receiver_type) {
+  ASSERT((cids != nullptr) && !target.IsNull());
+  ASSERT(cids->length() == num_args_tested);
+
+  Zone* zone = Thread::Current()->zone();
+  const auto& result = ICData::Handle(
+      zone,
+      NewDescriptor(zone, owner, target_name, arguments_descriptor, deopt_id,
+                    num_args_tested, rebind_rule, receiver_type));
+
+  const intptr_t kNumEntries = 2;  // 1 entry and a sentinel.
+  const intptr_t entry_len =
+      TestEntryLengthFor(num_args_tested, result.is_tracking_exactness());
+  const auto& array =
+      Array::Handle(zone, Array::New(kNumEntries * entry_len, Heap::kOld));
+
+  auto& cid = Smi::Handle(zone);
+  for (intptr_t i = 0; i < num_args_tested; ++i) {
+    cid = Smi::New((*cids)[i]);
+    array.SetAt(i, cid);
+  }
+  array.SetAt(CountIndexFor(num_args_tested), Object::smi_zero());
+  array.SetAt(TargetIndexFor(num_args_tested), target);
+  WriteSentinel(array, entry_len);
+  result.set_entries(array);
+
+  return result.raw();
+}
+
 ICDataPtr ICData::NewForStaticCall(const Function& owner,
                                    const Function& target,
                                    const Array& arguments_descriptor,
                                    intptr_t deopt_id,
                                    intptr_t num_args_tested,
                                    RebindRule rebind_rule) {
+  // See `MethodRecognizer::NumArgsCheckedForStaticCall`.
+  ASSERT(num_args_tested == 0 || num_args_tested == 2);
   ASSERT(!target.IsNull());
 
   Zone* zone = Thread::Current()->zone();
   const auto& target_name = String::Handle(zone, target.name());
-  const auto& result = ICData::Handle(
-      zone, NewDescriptor(zone, owner, target_name, arguments_descriptor,
-                          deopt_id, num_args_tested, rebind_rule,
-                          Object::null_abstract_type()));
-
-  const intptr_t kNumEntries = 2;  // 1 entry and a sentinel.
-  const intptr_t entry_len =
-      TestEntryLengthFor(num_args_tested, /*tracking_exactness=*/false);
-  const auto& array =
-      Array::Handle(zone, Array::New(kNumEntries * entry_len, Heap::kOld));
-
-  // See `MethodRecognizer::NumArgsCheckedForStaticCall`.
-  ASSERT(num_args_tested == 0 || num_args_tested == 2);
+  GrowableArray<intptr_t> cids(num_args_tested);
   if (num_args_tested == 2) {
-    const auto& object_cid = Smi::Handle(zone, Smi::New(kObjectCid));
-    array.SetAt(0, object_cid);
-    array.SetAt(1, object_cid);
+    cids.Add(kObjectCid);
+    cids.Add(kObjectCid);
   }
-  array.SetAt(CountIndexFor(num_args_tested), Object::smi_zero());
-  array.SetAt(TargetIndexFor(num_args_tested), target);
-  WriteSentinel(array, entry_len);
-
-  result.set_entries(array);
-
-  return result.raw();
+  return ICData::NewWithCheck(owner, target_name, arguments_descriptor,
+                              deopt_id, num_args_tested, rebind_rule, &cids,
+                              target, Object::null_abstract_type());
 }
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index d9429bb..df9585d 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -2086,9 +2086,6 @@
 
   void DebugDump() const;
 
-  // Returns true if this is a two arg smi operation.
-  bool AddSmiSmiCheckForFastSmiStubs() const;
-
   // Adding checks.
 
   // Adds one more class test to ICData. Length of 'classes' must be equal to
@@ -2159,6 +2156,19 @@
       intptr_t num_args_tested,
       RebindRule rebind_rule,
       const AbstractType& receiver_type = Object::null_abstract_type());
+
+  // Similar to [New] makes the ICData have an initial (cids, target) entry.
+  static ICDataPtr NewWithCheck(
+      const Function& owner,
+      const String& target_name,
+      const Array& arguments_descriptor,
+      intptr_t deopt_id,
+      intptr_t num_args_tested,
+      RebindRule rebind_rule,
+      GrowableArray<intptr_t>* cids,
+      const Function& target,
+      const AbstractType& receiver_type = Object::null_abstract_type());
+
   static ICDataPtr NewForStaticCall(const Function& owner,
                                     const Function& target,
                                     const Array& arguments_descriptor,
@@ -4487,7 +4497,6 @@
 
   LibraryPtr FindLibrary() const;
   StringPtr GetLine(intptr_t line_number, Heap::Space space = Heap::kNew) const;
-  StringPtr GetSnippet(TokenPosition from, TokenPosition to) const;
   StringPtr GetSnippet(intptr_t from_line,
                        intptr_t from_column,
                        intptr_t to_line,
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 47f28ff..94e3747 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -2356,9 +2356,11 @@
 
 static void CheckLinesWithOffset(Zone* zone, const intptr_t offset) {
   const char* url_chars = "";
-  // Eight lines, mix of \n, \r, \r\n line terminators, lines 3, 4, 7, and 8
+  // Nine lines, mix of \n, \r, \r\n line terminators, lines 3, 4, 7, and 8
   // are non-empty. Ends with a \r as a double-check that the \r followed by
   // \n check doesn't go out of bounds.
+  //
+  // Line starts:             1 2 3    4      5 6   7    8    9
   const char* source_chars = "\n\nxyz\nabc\r\n\n\r\ndef\rghi\r";
   const String& url = String::Handle(zone, String::New(url_chars));
   const String& source = String::Handle(zone, String::New(source_chars));
@@ -2383,6 +2385,11 @@
   EXPECT_STREQ("def", str.ToCString());
   str = script.GetLine(offset + 8);
   EXPECT_STREQ("ghi", str.ToCString());
+  str = script.GetLine(offset + 9);
+  EXPECT_STREQ("", str.ToCString());
+  // Using "column" of \r at end of line for to_column.
+  str = script.GetSnippet(offset + 3, 1, offset + 7, 4);
+  EXPECT_STREQ("xyz\nabc\r\n\n\r\ndef", str.ToCString());
   // Lines not in the range of (1-based) line indices in the source should
   // return the empty string.
   str = script.GetLine(-500);
@@ -2397,12 +2404,23 @@
     str = script.GetLine(3);  // Absolute, not relative to offset.
     EXPECT_STREQ("", str.ToCString());
   }
+  str = script.GetLine(offset - 500);
+  EXPECT_STREQ("", str.ToCString());
   str = script.GetLine(offset);
   EXPECT_STREQ("", str.ToCString());
-  str = script.GetLine(offset + 9);
+  str = script.GetLine(offset + 10);
   EXPECT_STREQ("", str.ToCString());
   str = script.GetLine(offset + 10000);
   EXPECT_STREQ("", str.ToCString());
+  // Snippets not contained within the source should be the null string.
+  str = script.GetSnippet(-1, 1, 2, 2);
+  EXPECT(str.IsNull());
+  str = script.GetSnippet(offset - 1, 1, offset + 2, 2);
+  EXPECT(str.IsNull());
+  str = script.GetSnippet(offset + 5, 15, offset + 6, 2);
+  EXPECT(str.IsNull());
+  str = script.GetSnippet(offset + 20, 1, offset + 30, 1);
+  EXPECT(str.IsNull());
 }
 
 ISOLATE_UNIT_TEST_CASE(Script) {
@@ -2442,6 +2460,10 @@
     auto& str = String::Handle(Z);
     str = script.GetLine(1);
     EXPECT_STREQ("abc", str.ToCString());
+    str = script.GetSnippet(1, 1, 1, 2);
+    EXPECT_STREQ("a", str.ToCString());
+    str = script.GetSnippet(1, 2, 1, 4);
+    EXPECT_STREQ("bc", str.ToCString());
     // Lines not in the source should return the empty string.
     str = script.GetLine(-500);
     EXPECT_STREQ("", str.ToCString());
@@ -2451,6 +2473,13 @@
     EXPECT_STREQ("", str.ToCString());
     str = script.GetLine(10000);
     EXPECT_STREQ("", str.ToCString());
+    // Snippets not contained within the source should be the null string.
+    str = script.GetSnippet(-1, 1, 1, 2);
+    EXPECT(str.IsNull());
+    str = script.GetSnippet(2, 1, 2, 2);
+    EXPECT(str.IsNull());
+    str = script.GetSnippet(1, 1, 1, 5);
+    EXPECT(str.IsNull());
   }
 
   TransitionVMToNative transition(thread);
diff --git a/tests/lib/lib_vm.status b/tests/lib/lib_vm.status
index b28a2fa..c693924 100644
--- a/tests/lib/lib_vm.status
+++ b/tests/lib/lib_vm.status
@@ -2,6 +2,9 @@
 # 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.
 
+[ $runtime == vm ]
+isolate/*: Pass, Slow # https://dartbug.com/36097: Slower while isolate groups are being gradually enabled in JIT mode.
+
 [ $runtime != vm ]
 isolate/native_wrapper_message_test: Skip # A VM specific test.
 
diff --git a/tests/lib_2/lib_2_vm.status b/tests/lib_2/lib_2_vm.status
index 68e3420..4820450 100644
--- a/tests/lib_2/lib_2_vm.status
+++ b/tests/lib_2/lib_2_vm.status
@@ -2,6 +2,9 @@
 # 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.
 
+[ $runtime == vm ]
+isolate/*: Pass, Slow # https://dartbug.com/36097: Slower while isolate groups are being gradually enabled in JIT mode.
+
 [ $runtime != vm ]
 isolate/native_wrapper_message_test: Skip # A VM specific test.
 
diff --git a/tools/VERSION b/tools/VERSION
index 5c1cbeb..127cde4 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 81
+PRERELEASE 82
 PRERELEASE_PATCH 0
\ No newline at end of file