blob: 0693ac2910bd931cf870c8b11a5274958aa641f6 [file] [log] [blame]
/*
* Copyright 2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.transform
import org.codehaus.groovy.control.CompilationUnit
import org.codehaus.groovy.control.SourceUnit
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.transform.GroovyASTTransformation
import org.codehaus.groovy.transform.ASTTransformation
import org.codehaus.groovy.ast.ASTNode
import org.codehaus.groovy.ast.ModuleNode
import org.codehaus.groovy.control.CompilePhase
import org.codehaus.groovy.control.Phases
/**
* Tests whether local and global transforms are successfully detected, loaded,
* and run if a custom class loader is used for resolving compile dependencies
* that does not delegate to the compiler's defining class loader (e.g. to
* avoid pollution of the compile classpath).
*
* @author Peter Niederwieser
*/
class TransformsAndCustomClassLoadersTest extends GroovyTestCase {
void testLocalTransform() {
def resolvingLoader = new GroovyProjectClassesLoader()
def transformLoader = new GroovyClassLoader(TransformsAndCustomClassLoadersTest.classLoader)
checkIsIsolated(resolvingLoader)
def clazz = compileAndLoadClass("@Immutable class Foo { String bar }", resolvingLoader, transformLoader)
checkIsImmutable(clazz)
}
void testGlobalTransform() {
def resolvingLoader = new GroovyProjectClassesLoader()
def transformLoader = new ToUpperCaseTransformLoader()
checkIsIsolated(resolvingLoader)
def clazz = compileAndLoadClass("class Foo {}", resolvingLoader, transformLoader)
assert clazz
assert clazz.name == "FOO"
}
private compileAndLoadClass(String source, GroovyClassLoader dependencyLoader, GroovyClassLoader transformLoader) {
def unit = new CompilationUnit(null, null, dependencyLoader, transformLoader)
unit.addSource(new SourceUnit("", source, new CompilerConfiguration(), null, null))
unit.compile(Phases.CLASS_GENERATION)
def classInfo = unit.classes[0]
assert classInfo
return transformLoader.defineClass(classInfo.name, classInfo.bytes)
}
private checkIsIsolated(ClassLoader loader) {
def clazz = loader.loadClass(CompilationUnit.name)
assert clazz
assert clazz != CompilationUnit
}
private checkIsImmutable(Class clazz) {
try {
def foo = clazz.newInstance(["setting property"] as Object[])
foo.bar = "updating property"
fail()
} catch (ReadOnlyPropertyException expected) {}
}
}
/**
* A class loader that can load classes in the Groovy project,
* but does so without delegating to another class loader.
*/
class GroovyProjectClassesLoader extends GroovyClassLoader {
private bootstrapClassLoader = new URLClassLoader([] as URL[], (ClassLoader) null)
GroovyProjectClassesLoader() {
super(null, null, false)
for (url in getGroovyLoaderURLs())
addURL(url)
checkCanLoadGroovyClasses()
checkCanLoadOrgCodehausGroovyClasses()
}
private URL[] getGroovyLoaderURLs() {
def groovyLoader = Closure.classLoader
if (groovyLoader instanceof URLClassLoader)
return groovyLoader.URLs
else
assert false, "sorry, GroovyProjectClassesLoader doesn't work in this class loader environment"
}
private checkCanLoadGroovyClasses() {
assert loadClass(GroovyShell.name)
}
private checkCanLoadOrgCodehausGroovyClasses() {
assert loadClass(CompilationUnit.name)
}
@Override
synchronized Class loadClass(String name, boolean resolve) {
def clazz = doLoadClass(name)
if (resolve) resolveClass(clazz)
return clazz
}
private Class doLoadClass(String name) {
def clazz = findLoadedClass(name)
if (clazz != null) return clazz
if (name.startsWith("java."))
return bootstrapClassLoader.loadClass(name)
return findClass(name)
}
}
class ToUpperCaseTransformLoader extends GroovyClassLoader {
ToUpperCaseTransformLoader() {
super(CompilationUnit.classLoader)
}
Enumeration getResources(String name) {
if (name.equals("META-INF/services/org.codehaus.groovy.transform.ASTTransformation"))
return Collections.enumeration([getURL()])
else
return super.getResources(name)
}
def getURL() {
return new FakeURLFactory().createURL("org.codehaus.groovy.transform.ToUpperCaseTransform")
}
}
@GroovyASTTransformation(phase = CompilePhase.CONVERSION)
class ToUpperCaseTransform implements ASTTransformation {
void visit(ASTNode[] nodes, SourceUnit source) {
assert nodes[0] instanceof ModuleNode
for (clazz in nodes[0].classes)
clazz.name = clazz.name.toUpperCase()
}
}