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(); }