[vm, bytecode] Fix memory leak in calls from compiled code to interpreted code.

Change-Id: If82d371fc49892508dd33cd837e6b06b40325a48
Reviewed-on: https://dart-review.googlesource.com/c/87264
Reviewed-by: RĂ©gis Crelier <regis@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index 81a7ce9..8c5e663 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -2650,16 +2650,21 @@
   ASSERT(Function::HasBytecode(function));
   ASSERT(interpreter != NULL);
 #endif
-  const Object& result = Object::Handle(
-      thread->zone(), interpreter->Call(function, argdesc, argc, argv, thread));
+  RawObject* result = interpreter->Call(function, argdesc, argc, argv, thread);
   DEBUG_ASSERT(thread->top_exit_frame_info() == exit_fp);
-  if (result.IsError()) {
+  if (RawObject::IsErrorClassId(result->GetClassIdMayBeSmi())) {
+    // Must not allocate handles in the caller's zone.
+    StackZone stack_zone(thread);
+    // Protect the result in a handle before transitioning, which may trigger
+    // GC.
+    const Error& error =
+        Error::Handle(stack_zone.GetZone(), static_cast<RawError*>(result));
     // Propagating an error may cause allocation. Check if we need to block for
     // a safepoint by switching to "in VM" execution state.
     TransitionGeneratedToVM transition(thread);
-    Exceptions::PropagateError(Error::Cast(result));
+    Exceptions::PropagateError(error);
   }
-  return result.raw();
+  return result;
 #endif  // defined(DART_PRECOMPILED_RUNTIME)
 }