private boolean performCompilationInner(List<String> vmOptions,
                                          List<String> args,
                                          StringWriter out,
                                          StringWriter err,
                                          Map<String, List<String>> outputs,
                                          CompileContext context,
                                          ModuleChunk chunk) {
    final ClassLoader jpsLoader = Thread.currentThread().getContextClassLoader();
    try {
      // We have to set context class loader in order because greclipse will create child GroovyClassLoader,
      // and will use context class loader as parent.
      //
      // Here's what happens if we leave jpsLoader:
      // 1. org.codehaus.groovy.transform.ASTTransformationCollectorCodeVisitor
      //    is loaded with GreclipseMain's class loader, i.e. myGreclipseLoader;
      // 2. org.codehaus.groovy.transform.ASTTransformation inside ASTTransformationCollectorCodeVisitor.verifyClass
      //    is loaded with ASTTransformationCollectorCodeVisitor' loader, i.e. myGreclipseLoader;
      // 3. transformation GroovyClassLoader is created with context class loader (jpsLoader) as a parent;
      // 4. some CoolTransform implements ASTTransformation is loaded with GroovyClassLoader;
      // 5. ASTTransformation supertype of CoolTransform is loaded with GroovyClassLoader too;
      // 6. GroovyClassLoader asks its parent, which is jpsLoader, it doesn't know about ASTTransformation
      //    => GroovyClassLoader loads ASTTransformation by itself;
      // 7. there are two different ASTTransformation class instances
      //    => we get ASTTransformation.class.isAssignableFrom(klass) = false
      //    => compilation fails with error.
      //
      // If we set context classloader here, then in the 6th step parent loader will be myGreclipseLoader,
      // and ASTTransformation class will be returned from myGreclipseLoader, and the compilation won't fail.
      Thread.currentThread().setContextClassLoader(myGreclipseLoader);

      if (vmOptions.isEmpty()) {
        Class<?> mainClass = Class.forName(GreclipseMain.class.getName(), true, myGreclipseLoader);
        Constructor<?> constructor = mainClass.getConstructor(PrintWriter.class, PrintWriter.class, Map.class);
        Method compileMethod = mainClass.getMethod("compile", String[].class);
        Object main = constructor.newInstance(new PrintWriter(out), new PrintWriter(err), outputs);
        return (Boolean)compileMethod.invoke(main, new Object[]{ArrayUtilRt.toStringArray(args)});
      }
      else {
        final List<String> cmd = ExternalProcessUtil.buildJavaCommandLine(
          ForkedGroovyc.getJavaExecutable(chunk),
          "org.jetbrains.jps.incremental.groovy.GreclipseMain",
          Collections.emptyList(), Arrays.asList(myGreclipseJar,
                                                 Objects.requireNonNull(PathManager.getJarForClass(GreclipseMain.class)).toAbsolutePath()
                                                   .toString()),
          vmOptions,
          args
        );
        final Process process = Runtime.getRuntime().exec(ArrayUtilRt.toStringArray(cmd));
        ProcessHandler handler = new BaseOSProcessHandler(process, StringUtil.join(cmd, " "), null) {
          @NotNull
          @Override
          public Future<?> executeTask(@NotNull Runnable task) {
            return SharedThreadPool.getInstance().submit(task);
          }

          @Override
          public void notifyTextAvailable(@NotNull String text, @NotNull Key outputType) {
            if (outputType == ProcessOutputType.STDERR) {
              err.append(text);
            }
            if (outputType == ProcessOutputType.STDOUT) {
              out.append(text);
            }
          }
        };
        handler.startNotify();
        handler.waitFor();
        return true;
      }
    }
    catch (Exception e) {
      context.processMessage(CompilerMessage.createInternalBuilderError(getPresentableName(), e));
      return false;
    }
    finally {
      Thread.currentThread().setContextClassLoader(jpsLoader);
    }
  }
private boolean performCompilationInner(List<String> vmOptions,
                                          List<String> args,
                                          StringWriter out,
                                          StringWriter err,
                                          Map<String, List<String>> outputs,
                                          CompileContext context,
                                          ModuleChunk chunk)