protected ClassVisitor createClassVisitor() {
    CompilerConfiguration config = getConfiguration();
    int computeMaxStackAndFrames = ClassWriter.COMPUTE_MAXS;
    if (CompilerConfiguration.isPostJDK7(config.getTargetBytecode())
        || Boolean.TRUE.equals(config.getOptimizationOptions().get("indy"))) {
      computeMaxStackAndFrames += ClassWriter.COMPUTE_FRAMES;
    }
    return new ClassWriter(computeMaxStackAndFrames) {
      private ClassNode getClassNode(String name) {
        // try classes under compilation
        CompileUnit cu = getAST();
        ClassNode cn = cu.getClass(name);
        if (cn != null) return cn;
        // try inner classes
        cn = cu.getGeneratedInnerClass(name);
        if (cn != null) return cn;
        // try class loader classes
        try {
          cn = ClassHelper.make(cu.getClassLoader().loadClass(name, false, true), false);
        } catch (Exception e) {
          throw new GroovyBugError(e);
        }
        return cn;
      }

      private ClassNode getCommonSuperClassNode(ClassNode c, ClassNode d) {
        // adapted from ClassWriter code
        if (c.isDerivedFrom(d)) return d;
        if (d.isDerivedFrom(c)) return c;
        if (c.isInterface() || d.isInterface()) return ClassHelper.OBJECT_TYPE;
        do {
          c = c.getSuperClass();
        } while (c != null && !d.isDerivedFrom(c));
        if (c == null) return ClassHelper.OBJECT_TYPE;
        return c;
      }

      @Override
      protected String getCommonSuperClass(String arg1, String arg2) {
        ClassNode a = getClassNode(arg1.replace('/', '.'));
        ClassNode b = getClassNode(arg2.replace('/', '.'));
        return getCommonSuperClassNode(a, b).getName().replace('.', '/');
      }
    };
  }
 /**
  * Indicates if a class is recompilable. Recompilable means, that the classloader will try to
  * locate a groovy source file for this class and then compile it again, adding the resulting
  * class as entry to the cache. Giving null as class is like a recompilation, so the method should
  * always return true here. Only classes that are implementing GroovyObject are compilable and
  * only if the timestamp in the class is lower than Long.MAX_VALUE.
  *
  * <p>NOTE: First the parent loaders will be asked and only if they don't return a class the
  * recompilation will happen. Recompilation also only happen if the source file is newer.
  *
  * @param cls the class to be tested. If null the method should return true
  * @return true if the class should be compiled again
  * @see #isSourceNewer(URL, Class)
  */
 protected boolean isRecompilable(Class cls) {
   if (cls == null) return true;
   if (cls.getClassLoader() == this) return false;
   if (recompile == null && !config.getRecompileGroovySource()) return false;
   if (recompile != null && !recompile) return false;
   if (!GroovyObject.class.isAssignableFrom(cls)) return false;
   long timestamp = getTimeStamp(cls);
   if (timestamp == Long.MAX_VALUE) return false;
   return true;
 }
 /**
  * creates a GroovyClassLoader.
  *
  * @param parent the parent class loader
  * @param config the compiler configuration
  * @param useConfigurationClasspath determines if the configurations classpath should be added
  */
 public GroovyClassLoader(
     ClassLoader parent, CompilerConfiguration config, boolean useConfigurationClasspath) {
   super(EMPTY_URL_ARRAY, parent);
   if (config == null) config = CompilerConfiguration.DEFAULT;
   this.config = config;
   if (useConfigurationClasspath) {
     for (String path : config.getClasspath()) {
       this.addClasspath(path);
     }
   }
 }
  /**
   * Configures its debugging mode and classloader classpath from a given compiler configuration.
   * This cannot be done more than once due to limitations in {@link java.net.URLClassLoader
   * URLClassLoader}.
   */
  public void configure(CompilerConfiguration configuration) {
    super.configure(configuration);
    this.debug = configuration.getDebug();

    if (!this.configured && this.classLoader instanceof GroovyClassLoader) {
      appendCompilerConfigurationClasspathToClassLoader(
          configuration, (GroovyClassLoader) this.classLoader);
    }

    this.configured = true;
  }
  private Class doParseClass(GroovyCodeSource codeSource) {
    validate(codeSource);
    Class answer; // Was neither already loaded nor compiling, so compile and add to cache.
    CompilationUnit unit = createCompilationUnit(config, codeSource.getCodeSource());
    if (recompile != null && recompile || recompile == null && config.getRecompileGroovySource()) {
      unit.addFirstPhaseOperation(
          TimestampAdder.INSTANCE, CompilePhase.CLASS_GENERATION.getPhaseNumber());
    }
    SourceUnit su = null;
    File file = codeSource.getFile();
    if (file != null) {
      su = unit.addSource(file);
    } else {
      URL url = codeSource.getURL();
      if (url != null) {
        su = unit.addSource(url);
      } else {
        su = unit.addSource(codeSource.getName(), codeSource.getScriptText());
      }
    }

    ClassCollector collector = createCollector(unit, su);
    unit.setClassgenCallback(collector);
    int goalPhase = Phases.CLASS_GENERATION;
    if (config != null && config.getTargetDirectory() != null) goalPhase = Phases.OUTPUT;
    unit.compile(goalPhase);

    answer = collector.generatedClass;
    String mainClass = su.getAST().getMainClassName();
    for (Object o : collector.getLoadedClasses()) {
      Class clazz = (Class) o;
      String clazzName = clazz.getName();
      definePackage(clazzName);
      setClassCacheEntry(clazz);
      if (clazzName.equals(mainClass)) answer = clazz;
    }
    return answer;
  }
  /**
   * Decides if the given source is newer than a class.
   *
   * @param source the source we may want to compile
   * @param cls the former class
   * @return true if the source is newer, false else
   * @throws IOException if it is not possible to open an connection for the given source
   * @see #getTimeStamp(Class)
   */
  protected boolean isSourceNewer(URL source, Class cls) throws IOException {
    long lastMod;

    // Special handling for file:// protocol, as getLastModified() often reports
    // incorrect results (-1)
    if (isFile(source)) {
      // Coerce the file URL to a File
      // See ClassNodeResolver.isSourceNewer for another method that replaces '|' with ':'.
      // WTF: Why is this done and where is it documented?
      String path = source.getPath().replace('/', File.separatorChar).replace('|', ':');
      File file = new File(path);
      lastMod = file.lastModified();
    } else {
      URLConnection conn = source.openConnection();
      lastMod = conn.getLastModified();
      conn.getInputStream().close();
    }
    long classTime = getTimeStamp(cls);
    return classTime + config.getMinimumRecompilationInterval() < lastMod;
  }
 /**
  * (Re)Compiles the given source. This method starts the compilation of a given source, if the
  * source has changed since the class was created. For this isSourceNewer is called.
  *
  * @param source the source pointer for the compilation
  * @param className the name of the class to be generated
  * @param oldClass a possible former class
  * @return the old class if the source wasn't new enough, the new class else
  * @throws CompilationFailedException if the compilation failed
  * @throws IOException if the source is not readable
  * @see #isSourceNewer(URL, Class)
  */
 protected Class recompile(URL source, String className, Class oldClass)
     throws CompilationFailedException, IOException {
   if (source != null) {
     // found a source, compile it if newer
     if ((oldClass != null && isSourceNewer(source, oldClass)) || (oldClass == null)) {
       synchronized (sourceCache) {
         String name = source.toExternalForm();
         sourceCache.remove(name);
         if (isFile(source)) {
           try {
             return parseClass(
                 new GroovyCodeSource(new File(source.toURI()), config.getSourceEncoding()));
           } catch (URISyntaxException e) {
             // do nothing and fall back to the other version
           }
         }
         return parseClass(source.openStream(), name);
       }
     }
   }
   return oldClass;
 }
 /**
  * Parses the given file into a Java class capable of being run
  *
  * @param file the file name to parse
  * @return the main class defined in the given script
  */
 public Class parseClass(File file) throws CompilationFailedException, IOException {
   return parseClass(new GroovyCodeSource(file, config.getSourceEncoding()));
 }
  /**
   * Initializes the CompilationUnit with a CodeSource for controlling security stuff, a class
   * loader for loading classes, and a class loader for loading AST transformations. <b>Note</b> The
   * transform loader must be able to load compiler classes. That means
   * CompilationUnit.class.classLoader must be at last a parent to transformLoader. The other loader
   * has no such constraint.
   *
   * @param transformLoader - the loader for transforms
   * @param loader - loader used to resolve classes against during compilation
   * @param security - security setting for the compilation
   * @param configuration - compilation configuration
   */
  public CompilationUnit(
      CompilerConfiguration configuration,
      CodeSource security,
      GroovyClassLoader loader,
      GroovyClassLoader transformLoader,
      boolean allowTransforms,
      String localTransformsToRunOnReconcile) {
    super(configuration, loader, null);

    // GRECLISE start
    this.allowTransforms = allowTransforms;
    // GRECLISE end
    this.astTransformationsContext = new ASTTransformationsContext(this, transformLoader);
    this.names = new ArrayList<String>();
    this.queuedSources = new LinkedList<SourceUnit>();
    this.sources = new HashMap<String, SourceUnit>();
    this.summariesBySourceName = new HashMap();
    this.summariesByPublicClassName = new HashMap();
    this.classSourcesByPublicClassName = new HashMap();

    this.ast = new CompileUnit(this.classLoader, security, this.configuration);
    this.generatedClasses = new ArrayList<GroovyClass>();

    this.verifier = new Verifier();
    this.resolveVisitor = new ResolveVisitor(this);
    this.staticImportVisitor = new StaticImportVisitor();
    this.optimizer = new OptimizerVisitor(this);
    // GRECLIPSE start
    if (localTransformsToRunOnReconcile == null) {
      this.localTransformsToRunOnReconcile = new ArrayList<String>(); // Collections.emptyList();
      this.localTransformsToRunOnReconcile.add("*");
    } else {
      this.localTransformsToRunOnReconcile = new ArrayList<String>();
      try {
        StringTokenizer st = new StringTokenizer(localTransformsToRunOnReconcile, ",");
        while (st.hasMoreElements()) {
          String classname = st.nextToken();
          this.localTransformsToRunOnReconcile.add(classname);
        }
      } catch (Exception e) {
        // presumed security exception
      }
    }
    // GRECLIPSE end

    phaseOperations = new LinkedList[Phases.ALL + 1];
    newPhaseOperations = new LinkedList[Phases.ALL + 1];
    for (int i = 0; i < phaseOperations.length; i++) {
      phaseOperations[i] = new LinkedList();
      newPhaseOperations[i] = new LinkedList();
    }
    addPhaseOperation(
        new SourceUnitOperation() {
          public void call(SourceUnit source) throws CompilationFailedException {
            source.parse();
          }
        },
        Phases.PARSING);
    addPhaseOperation(convert, Phases.CONVERSION);
    addPhaseOperation(
        new PrimaryClassNodeOperation() {
          public void call(SourceUnit source, GeneratorContext context, ClassNode classNode)
              throws CompilationFailedException {
            EnumVisitor ev = new EnumVisitor(CompilationUnit.this, source);
            ev.visitClass(classNode);
          }
        },
        Phases.CONVERSION);
    addPhaseOperation(resolve, Phases.SEMANTIC_ANALYSIS);
    addPhaseOperation(staticImport, Phases.SEMANTIC_ANALYSIS);
    addPhaseOperation(
        new PrimaryClassNodeOperation() {
          @Override
          public void call(SourceUnit source, GeneratorContext context, ClassNode classNode)
              throws CompilationFailedException {
            InnerClassVisitor iv = new InnerClassVisitor(CompilationUnit.this, source);
            iv.visitClass(classNode);
          }
        },
        Phases.SEMANTIC_ANALYSIS);
    addPhaseOperation(compileCompleteCheck, Phases.CANONICALIZATION);
    addPhaseOperation(classgen, Phases.CLASS_GENERATION);
    // GRECLIPSE: start: skip output phase
    // addPhaseOperation(output);

    addPhaseOperation(
        new PrimaryClassNodeOperation() {
          @Override
          public void call(SourceUnit source, GeneratorContext context, ClassNode classNode)
              throws CompilationFailedException {
            AnnotationCollectorTransform.ClassChanger actt =
                new AnnotationCollectorTransform.ClassChanger();
            actt.transformClass(classNode);
          }
        },
        Phases.SEMANTIC_ANALYSIS);
    ASTTransformationVisitor.addPhaseOperations(this);
    addPhaseOperation(
        new PrimaryClassNodeOperation() {
          @Override
          public void call(SourceUnit source, GeneratorContext context, ClassNode classNode)
              throws CompilationFailedException {
            StaticVerifier sv = new StaticVerifier();
            sv.visitClass(classNode, source);
          }
        },
        Phases.SEMANTIC_ANALYSIS);
    addPhaseOperation(
        new PrimaryClassNodeOperation() {
          @Override
          public void call(SourceUnit source, GeneratorContext context, ClassNode classNode)
              throws CompilationFailedException {
            InnerClassCompletionVisitor iv =
                new InnerClassCompletionVisitor(CompilationUnit.this, source);
            iv.visitClass(classNode);
          }
        },
        Phases.CANONICALIZATION);
    addPhaseOperation(
        new PrimaryClassNodeOperation() {
          public void call(SourceUnit source, GeneratorContext context, ClassNode classNode)
              throws CompilationFailedException {
            EnumCompletionVisitor ecv = new EnumCompletionVisitor(CompilationUnit.this, source);
            ecv.visitClass(classNode);
          }
        },
        Phases.CANONICALIZATION);

    // apply configuration customizers if any
    if (configuration != null) {
      final List<CompilationCustomizer> customizers = configuration.getCompilationCustomizers();
      for (CompilationCustomizer customizer : customizers) {
        addPhaseOperation(customizer, customizer.getPhase().getPhaseNumber());
      }
    }
    this.classgenCallback = null;
    this.classNodeResolver = new ClassNodeResolver();
  }