Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

...

Code Block
import org.codehaus.groovy.control.CompilerConfiguration
 
// create a configuration
def config = new CompilerConfiguration()
 
// tweak the configuration
config.addCompilationCustomizers(...)
 
// run your script
def shell = new GroovyShell(config)
shell.evaluate(script)

...

Code Block
import org.codehaus.groovy.control.customizers.ImportCustomizer
 
def icz = new ImportCustomizer()
 
// "normal" import
icz.addImports('java.util.concurrent.atomic.AtomicInteger', 'java.util.concurrent.ConcurrentHashMap')
 
// "aliases" import
icz.addImport('CHM', 'java.util.concurrent.ConcurrentHashMap')
 
// "static" import
icz.addStaticImport('java.lang.Math', 'PI') // import static java.lang.Math.PIPi
 
// "aliased static" import
ica.addStaticImport('pi', 'java.lang.Math', 'PI') // import static java.lang.Math.PI as pi
 
// "star" import
icz.addStarImport 'java.util.concurrent' // import java.util.concurrent.*
 
// "static star" import
icz.addStaticStar 'java.lang.Math' // import static java.lang.Math.*
 
 

The AST transformation customizer

...

Code Block
import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
import groovy.transformutil.logging.Log
 
def acz = new ASTTransformationCustomizer(Log)
configconfiguration.addCompilationCustomizers(acz)

...

Code Block
final expression = new AstBuilder().buildFromCode(CompilePhase.CONVERSION) {->     true }.expression[0]
customizer = new ASTTransformationCustomizer(ConditionalInterrupt, value:expression, thrown:SecurityException)
configuration.addCompilationCustomizers(customizer)
def shell = new GroovyShell(configuration)
shouldFail(SecurityException) {
    shell.evaluate("""
                // equivalent to adding @ConditionalInterrupt(value={true}, thrown: SecurityException)
                class MyClass {
                    void doIt() { }
                }
                new MyClass().doIt()
            """)
}

...

Code Block
org.codehaus.groovy.control.customizers.SecureASTCustomizer
 
def scz = new SecureASTCustomizer()
scz.with {
    closuresAllowed = false // user will not be able to write closures
    methodDefinitionAllowed = false // user will not be able to define methods
    importsWhitelist = [] // empty whitelist means imports are disallowed
    staticImportsWhitelist = [] // same for static imports
    staticStarImportsWhitelist = ['java.lang.Math'] // only java.lang.Math is allowed
      // the list of tokens the user can find
	// constants are defined in org.codehaus.groovy.syntax.Types
    tokensWhitelist = [
            PLUS,
            MINUS,
            MULTIPLY,
            DIVIDE,
            MOD,
            POWER,
            PLUS_PLUS,
            MINUS_MINUS,
            COMPARE_EQUAL,
            COMPARE_NOT_EQUAL,
            COMPARE_LESS_THAN,
            COMPARE_LESS_THAN_EQUAL,
            COMPARE_GREATER_THAN,
            COMPARE_GREATER_THAN_EQUAL,
    ].asImmutable()
    
	// limit the types of constants that a user can define to number types only
    constantTypesClassesWhiteList = [
            Integer,
            Float,
            Long,
            Double,
            BigDecimal,
            Integer.TYPE,
            Long.TYPE,
            Float.TYPE,
            Double.TYPE
    ].asImmutable()
      // method calls are only allowed if the receiver is of one of those types
    // be careful, it's not a runtime type!
    receiversClassesWhiteList = [
            Math,
            Integer,
            Float,
            Double,
            Long,
            BigDecimal
    ].asImmutable()
}

...

Code Block
import org.codehaus.groovy.control.customizers.SourceAwareCustomizer
import org.codehaus.groovy.control.customizers.ImportCustomizer
 
def delegate = new ImportCustomizer()
def sac = new SourceAwareCustomizer(delegate)

...

Code Block
// the customizer will only be applied to classes ending with 'Bean'
sac.baseNameValidator = { baseName ->
   baseName.endsWith 'Bean'
}
 
// the customizer will only be applied to files which extension is '.spec'
sac.extensionValidator = { ext -> ext == 'spec' }
 
// source unit validation
sac.sourceUnitValidator = { sourceUnit -> ... }

...

Code Block
import org.codehaus.groovy.control.CompilerConfiguration
import static org.codehaus.groovy.control.customizers.builder.CompilerCustomizationBuilder.withConfig
 
def conf = new CompilerConfiguration()
withConfig(conf) {
   ...
}

...

Imports

Code Block
withConfig(confconfiguration) {
   imports { // imports customizer
	  normal 'my.package.MyClass' // a normal import
      alias 'AI', 'java.util.concurrent.atomic.AtomicInteger' // an aliased import
      star 'java.util.concurrent' // star imports
      staticMember 'java.lang.Math', 'PI' // static import
      staticMember 'pi', 'java.lang.Math', 'PI' // aliased static import
   }
}

...

Code Block
// apply @Log transparently
withConfig(conf) {
   ast(Log)
}
 
// apply @Log, with a different logger name
withConfig(conf) {
   ast(Log, value: 'LOGGER')
}

...

Code Block
// apply CompileStatic AST annotation on .sgroovy files
withConfig(conf)configuration){
    source(extension: 'sgroovy') {
        ast(CompileStatic)
    }
}

// apply CompileStatic AST annotation on .sgroovy or .sg files
withConfig(conf)configuration){
    source(extensions: ['sgroovy','sg']) {
        ast(CompileStatic)
    }
}

// apply CompileStatic AST annotation on .sgroovy or .sg files
withConfig(conf)configuration) {
    source(extensionValidator: { it.name in ['sgroovy','sg']}) {
        ast(CompileStatic)
    }
}

// apply CompileStatic AST annotation on files whose name is 'foo'
withConfig(conf)configuration) {
    source(basename: 'foo') {
        ast(CompileStatic)
    }
}

// apply CompileStatic AST annotation on files whose name is 'foo' or 'bar'
withConfig(conf)configuration) {
    source(basenames: ['foo', 'bar']) {
        ast(CompileStatic)
    }
}

// apply CompileStatic AST annotation on files whose name is 'foo' or 'bar'
withConfig(conf)(basenamesconfiguration) {
    source(basenameValidator: { it in ['foo', 'bar'] }) {
        ast(CompileStatic)
    }
}

// apply CompileStatic AST annotation on files that do not contain a class named 'Baz'
withConfig(conf)configuration) {
    source(unitValidator: { unit -> !unit.AST.classes.any { it.name == 'Baz' } }) {
        ast(CompileStatic)
    }
}

Inlining a customizer

Inlined customizer allows you to write a compilation customizer directly, without having to create a dedicated class for it.

Code Block
withConfig(confconfiguration) {
    inline(phase:'CONVERSION') { source, context, classNode ->
        println "visiting $classNode"
    }
}

...

Of course, the builder allows you to define multiple customizers at once:

Code Block
withConfig(confconfiguration) {
   ast(ToString)
   ast(EqualsAndHashCode)
}

...