@Nullable
  private String evaluateExpression(
      final @NotNull String isolateId,
      final @Nullable Frame vmTopFrame,
      final @Nullable XExpression xExpression) {
    final String evalText = xExpression == null ? null : xExpression.getExpression();
    if (vmTopFrame == null || StringUtil.isEmptyOrSpaces(evalText)) return null;

    final Ref<String> evalResult = new Ref<>();
    final Semaphore semaphore = new Semaphore();
    semaphore.down();

    myDebugProcess
        .getVmServiceWrapper()
        .evaluateInFrame(
            isolateId,
            vmTopFrame,
            evalText,
            new XDebuggerEvaluator.XEvaluationCallback() {
              @Override
              public void evaluated(@NotNull final XValue result) {
                if (result instanceof DartVmServiceValue) {
                  evalResult.set(
                      getSimpleStringPresentation(((DartVmServiceValue) result).getInstanceRef()));
                }
                semaphore.up();
              }

              @Override
              public void errorOccurred(@NotNull final String errorMessage) {
                evalResult.set(
                    "Failed to evaluate log expression [" + evalText + "]: " + errorMessage);
                semaphore.up();
              }
            },
            true);

    semaphore.waitFor(1000);
    return evalResult.get();
  }
  @Override
  public void received(@NotNull final String streamId, @NotNull final Event event) {
    switch (event.getKind()) {
      case BreakpointAdded:
        // TODO Respond to breakpoints added by the observatory.
        // myBreakpointHandler.vmBreakpointAdded(null, event.getIsolate().getId(),
        // event.getBreakpoint());
        break;
      case BreakpointRemoved:
        break;
      case BreakpointResolved:
        myBreakpointHandler.breakpointResolved(event.getBreakpoint());
        break;
      case Extension:
        break;
      case GC:
        break;
      case Inspect:
        break;
      case IsolateExit:
        myDebugProcess.isolateExit(event.getIsolate());
        break;
      case IsolateRunnable:
        break;
      case IsolateStart:
        break;
      case IsolateUpdate:
        break;
      case None:
        break;
      case PauseBreakpoint:
      case PauseException:
      case PauseInterrupted:
        myDebugProcess.isolateSuspended(event.getIsolate());

        ApplicationManager.getApplication()
            .executeOnPooledThread(
                () -> {
                  final ElementList<Breakpoint> breakpoints =
                      event.getKind() == EventKind.PauseBreakpoint
                          ? event.getPauseBreakpoints()
                          : null;
                  final InstanceRef exception =
                      event.getKind() == EventKind.PauseException ? event.getException() : null;
                  onIsolatePaused(
                      event.getIsolate(),
                      breakpoints,
                      exception,
                      event.getTopFrame(),
                      event.getAtAsyncSuspension());
                });
        break;
      case PausePostRequest:
        // We get this event after an isolate reload call, when pause after reload has been
        // requested.
        // TODO: Set current breakpoints; resume the isolate.
        myDebugProcess.getVmServiceWrapper().resumeIsolate(event.getIsolate().getId(), null);
        break;
      case PauseExit:
        break;
      case PauseStart:
        myDebugProcess.getVmServiceWrapper().handleIsolate(event.getIsolate(), true);
        break;
      case Resume:
        myDebugProcess.isolateResumed(event.getIsolate());
        break;
      case ServiceExtensionAdded:
        break;
      case VMUpdate:
        break;
      case WriteEvent:
        myDebugProcess.handleWriteEvent(event.getBytes());
        break;
      case Unknown:
        break;
    }
  }
  void onIsolatePaused(
      @NotNull final IsolateRef isolateRef,
      @Nullable final ElementList<Breakpoint> vmBreakpoints,
      @Nullable final InstanceRef exception,
      @Nullable final Frame vmTopFrame,
      boolean atAsyncSuspension) {
    if (vmTopFrame == null) {
      myDebugProcess.getSession().positionReached(new XSuspendContext() {});
      return;
    }

    final DartVmServiceSuspendContext suspendContext =
        new DartVmServiceSuspendContext(
            myDebugProcess, isolateRef, vmTopFrame, exception, atAsyncSuspension);
    final XStackFrame xTopFrame = suspendContext.getActiveExecutionStack().getTopFrame();
    final XSourcePosition sourcePosition = xTopFrame == null ? null : xTopFrame.getSourcePosition();

    if (vmBreakpoints == null || vmBreakpoints.isEmpty()) {
      final StepOption latestStep = myDebugProcess.getVmServiceWrapper().getLatestStep();

      if (latestStep == StepOption.Over
          && equalSourcePositions(myLatestSourcePosition, sourcePosition)) {
        // continue stepping to change current line
        myDebugProcess.getVmServiceWrapper().resumeIsolate(isolateRef.getId(), latestStep);
      } else {
        myLatestSourcePosition = sourcePosition;
        myDebugProcess.getSession().positionReached(suspendContext);
      }
    } else {
      if (vmBreakpoints.size() > 1) {
        // Shouldn't happen. IDE doesn't allow to set 2 breakpoints on one line.
        LOG.error(vmBreakpoints.size() + " breakpoints hit in one shot.");
      }

      // Remove any temporary (run to cursor) breakpoints.
      myBreakpointHandler.removeTemporaryBreakpoints(isolateRef.getId());

      final XLineBreakpoint<XBreakpointProperties> xBreakpoint =
          myBreakpointHandler.getXBreakpoint(vmBreakpoints.get(0));

      if (xBreakpoint == null) {
        // breakpoint could be set in the Observatory
        myLatestSourcePosition = sourcePosition;
        myDebugProcess.getSession().positionReached(suspendContext);
        return;
      }

      if ("false"
          .equals(
              evaluateExpression(
                  isolateRef.getId(), vmTopFrame, xBreakpoint.getConditionExpression()))) {
        myDebugProcess.getVmServiceWrapper().resumeIsolate(isolateRef.getId(), null);
        return;
      }

      myLatestSourcePosition = sourcePosition;

      final String logExpression =
          evaluateExpression(isolateRef.getId(), vmTopFrame, xBreakpoint.getLogExpressionObject());
      final boolean suspend =
          myDebugProcess.getSession().breakpointReached(xBreakpoint, logExpression, suspendContext);
      if (!suspend) {
        myDebugProcess.getVmServiceWrapper().resumeIsolate(isolateRef.getId(), null);
      }
    }
  }