private static Object invokeRemote(@NotNull Method localMethod,
                                     @NotNull Method remoteMethod,
                                     @NotNull Object remoteObj,
                                     Object @Nullable [] args,
                                     @Nullable ClassLoader loader,
                                     boolean substituteClassLoader)
    throws Exception {
    boolean canThrowError = false;
    try {
      Object result = remoteMethod.invoke(remoteObj, args);
      canThrowError = true;
      return handleRemoteResult(result, localMethod.getReturnType(), loader, substituteClassLoader);
    }
    catch (InvocationTargetException e) {
      Throwable cause = e.getCause(); // root cause may go deeper than we need, so leave it like this
      if (cause instanceof ServerError) cause = ObjectUtils.chooseNotNull(cause.getCause(), cause);
      if (cause instanceof RuntimeException) throw (RuntimeException)cause;
      else if (canThrowError && cause instanceof Error || cause instanceof LinkageError) throw (Error)cause;
      else if (cause instanceof Exception && canThrow(cause, localMethod)) throw (Exception)cause;
      throw new RuntimeException(cause);
    }
  }
private static Object invokeRemote(@NotNull Method localMethod,
                                     @NotNull Method remoteMethod,
                                     @NotNull Object remoteObj,
                                     Object @Nullable [] args,
                                     @Nullable ClassLoader loader,
                                     boolean substituteClassLoader)