Skip to content
Permalink
Browse files
add GroovyShell#withConfig(Closure) and refactor some unit tests
  • Loading branch information
eric-milles committed Apr 24, 2022
1 parent 059f54f commit 4964a5e104109ca2cdab785f1bb4bbe36c84bb1f
Showing 14 changed files with 123 additions and 156 deletions.
@@ -19,6 +19,7 @@

package org.codehaus.groovy.control.customizers.builder

import groovy.transform.AutoFinal
import groovy.transform.CompileStatic
import org.codehaus.groovy.control.CompilerConfiguration

@@ -27,22 +28,28 @@ import org.codehaus.groovy.control.CompilerConfiguration
* various compilation customizers by hand, you may use this builder instead, which provides a
* shorter syntax and removes most of the verbosity.
*/
@CompileStatic
@AutoFinal @CompileStatic
class CompilerCustomizationBuilder extends FactoryBuilderSupport {
CompilerCustomizationBuilder() {
registerFactories()
}

static CompilerConfiguration withConfig(CompilerConfiguration config,
@DelegatesTo(type = 'org.codehaus.groovy.control.customizers.builder.CompilerCustomizationBuilder') Closure code) {
CompilerCustomizationBuilder builder = new CompilerCustomizationBuilder()
config.invokeMethod('addCompilationCustomizers', builder.invokeMethod('customizers', code))
static CompilerConfiguration withConfig(CompilerConfiguration config, @DelegatesTo(type='org.codehaus.groovy.control.customizers.builder.CompilerCustomizationBuilder') Closure spec) {
config.invokeMethod('addCompilationCustomizers', new CompilerCustomizationBuilder().invokeMethod('customizers', spec))
config
}

//--------------------------------------------------------------------------

CompilerCustomizationBuilder() {
registerFactory('customizers', new CustomizersFactory()) // root

registerFactory('ast', new ASTTransformationCustomizerFactory())
registerFactory('imports', new ImportCustomizerFactory())
registerFactory('inline', new InlinedASTCustomizerFactory())
registerFactory('secureAst', new SecureASTCustomizerFactory())
registerFactory('source', new SourceAwareCustomizerFactory())
}

@Override
@SuppressWarnings('Instanceof')
protected Object postNodeCompletion(final Object parent, final Object node) {
protected Object postNodeCompletion(Object parent, Object node) {
Object value = super.postNodeCompletion(parent, node)
Object factory = getContextAttribute(CURRENT_FACTORY)
if (factory instanceof PostCompletionFactory) {
@@ -51,13 +58,4 @@ class CompilerCustomizationBuilder extends FactoryBuilderSupport {
}
value
}

private void registerFactories() {
registerFactory('ast', new ASTTransformationCustomizerFactory())
registerFactory('customizers', new CustomizersFactory())
registerFactory('imports', new ImportCustomizerFactory())
registerFactory('inline', new InlinedASTCustomizerFactory())
registerFactory('secureAst', new SecureASTCustomizerFactory())
registerFactory('source', new SourceAwareCustomizerFactory())
}
}
@@ -18,7 +18,6 @@
*/
package groovy.grape;

import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.security.PrivilegedAction;
import java.util.Collections;
@@ -121,10 +120,9 @@ public static synchronized GrapeEngine getInstance() {
if (instance == null) {
try {
// by default use GrapeIvy
//TODO META-INF/services resolver?
// TODO: META-INF/services resolver?
instance = (GrapeEngine) Class.forName("groovy.grape.GrapeIvy").getDeclaredConstructor().newInstance();
} catch (InstantiationException | ClassNotFoundException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
//LOGME
} catch (ReflectiveOperationException ignore) {
}
}
return instance;
@@ -39,6 +39,7 @@
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import static org.codehaus.groovy.control.ResolveVisitor.EMPTY_STRING_ARRAY;
import static org.codehaus.groovy.runtime.InvokerHelper.MAIN_METHOD_NAME;

/**
@@ -47,17 +48,35 @@
public class GroovyShell extends GroovyObjectSupport {

public static final String DEFAULT_CODE_BASE = "/groovy/shell";
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final String CONFIGURATION_CUSTOMIZER = "org.codehaus.groovy.control.customizers.builder.CompilerCustomizationBuilder";

private final Binding context;
private final AtomicInteger counter = new AtomicInteger(0);
private final CompilerConfiguration config;
private final GroovyClassLoader loader;

public static void main(String[] args) {
public static void main(final String[] args) {
GroovyMain.main(args);
}

/**
* @since 4.0.3
*/
public static GroovyShell withConfig(@DelegatesTo(type = CONFIGURATION_CUSTOMIZER) final Closure<Void> spec) {
//TODO return new GroovyShell(CompilerCustomizationBuilder.withConfig(new CompilerConfiguration(), spec));
CompilerConfiguration config = new CompilerConfiguration();
try {
Class.forName(CONFIGURATION_CUSTOMIZER)
.getDeclaredMethod("withConfig", CompilerConfiguration.class, Closure.class)
.invoke(null, config, spec);
} catch (ReflectiveOperationException e) {
throw new GroovyRuntimeException(e);
}
return new GroovyShell(config);
}

//--------------------------------------------------------------------------

public GroovyShell() {
this(null, new Binding());
}
@@ -22,39 +22,34 @@
import groovy.util.FactoryBuilderSupport;
import org.codehaus.groovy.control.customizers.CompilationCustomizer;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
* This factory generates an array of compilation customizers.
*
* @since 2.1.0
*/
@SuppressWarnings({"unchecked","rawtypes"})
public class CustomizersFactory extends AbstractFactory implements PostCompletionFactory {
private static final CompilationCustomizer[] EMPTY_COMPILATIONCUSTOMIZER_ARRAY = new CompilationCustomizer[0];

@Override
public Object newInstance(final FactoryBuilderSupport builder, final Object name, final Object value, final Map attributes) throws InstantiationException, IllegalAccessException {
return new LinkedList();
return new ArrayList();
}

@Override
@SuppressWarnings("unchecked")
public void setChild(final FactoryBuilderSupport builder, final Object parent, final Object child) {
if (parent instanceof Collection && child instanceof CompilationCustomizer) {
((Collection) parent).add(child);
}
}


@Override
@SuppressWarnings("unchecked")
public Object postCompleteNode(final FactoryBuilderSupport factory, final Object parent, final Object node) {
if (node instanceof List) {
List col = (List) node;
return col.toArray(EMPTY_COMPILATIONCUSTOMIZER_ARRAY);
if (node instanceof Collection) {
return ((Collection) node).toArray(new CompilationCustomizer[0]);
}
return node;
}
@@ -22,13 +22,11 @@ import groovy.test.GroovyAssert
import groovy.test.GroovyTestCase
import groovy.transform.TypeChecked
import groovy.xml.MarkupBuilder
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.control.MultipleCompilationErrorsException
import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer

import static asciidoctor.Utils.stripAsciidocMarkup

class TypeCheckingExtensionSpecTest extends GroovyTestCase {
final class TypeCheckingExtensionSpecTest extends GroovyTestCase {

void testIntro() {
def out = new PrintWriter(new ByteArrayOutputStream())
@@ -613,13 +611,11 @@ new DelegateTest().delegate()
}

private def assertScriptWithExtension(String extensionName, String code, Closure<Void> configurator=null) {
def config = new CompilerConfiguration()
config.addCompilationCustomizers(
new ASTTransformationCustomizer(TypeChecked, extensions:[extensionName]))
def binding = new Binding()
def shell = new GroovyShell(binding,config)
def shell = GroovyShell.withConfig {
ast(TypeChecked, extensions: [extensionName])
}
if (configurator) {
configurator.call(binding)
configurator.call(shell.context)
}
shell.evaluate(code)
}
@@ -18,29 +18,23 @@
*/
package groovy.bugs

import groovy.test.GroovyTestCase
import org.codehaus.groovy.ast.ClassCodeVisitorSupport
import org.codehaus.groovy.control.SourceUnit
import org.codehaus.groovy.ast.expr.MethodCallExpression
import org.codehaus.groovy.ast.MethodNode
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.control.customizers.CompilationCustomizer
import org.codehaus.groovy.control.CompilePhase
import org.codehaus.groovy.classgen.GeneratorContext
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.ast.expr.MethodCallExpression
import org.codehaus.groovy.control.SourceUnit
import org.junit.Test

import static groovy.test.GroovyAssert.assertScript

class DirectMethodCallWithVargsTest extends GroovyTestCase {
final class DirectMethodCallWithVargsTest {

@Test
void testDirectMethodCallWithVargs() {
def config = new CompilerConfiguration()
config.addCompilationCustomizers(
new MyCustomizer()
)
GroovyShell shell = new GroovyShell(config)
shell.evaluate '''
assertScript shell, '''
def foo(String... args) {
(args as List).join(',')
}
assert foo() == ''
assert foo('1') == '1'
assert foo('1','2','3') == '1,2,3'
@@ -50,37 +44,30 @@ class DirectMethodCallWithVargsTest extends GroovyTestCase {
def b = '2'
def c = '3'
assert foo(a,b,c) == '1,2,3'
'''
}

@Test
void testDirectMethodCallWithPrimitiveVargs() {
def config = new CompilerConfiguration()
config.addCompilationCustomizers(
new MyCustomizer()
)
GroovyShell shell = new GroovyShell(config)
shell.evaluate '''
assertScript shell, '''
def foo(int... args) {
(args as List).join(',')
}
assert foo() == ''
assert foo(1) == '1'
assert foo(1,2,3) == '1,2,3'
assert foo(1,2,3,4) == '1,2,3,4'
'''
}

void testDirectMethodCallWithArgPlusVargs() {
def config = new CompilerConfiguration()
config.addCompilationCustomizers(
new MyCustomizer()
)
GroovyShell shell = new GroovyShell(config)
shell.evaluate '''
@Test
void testDirectMethodCallWithArgumentAndVargs() {
assertScript shell, '''
def foo(String prefix, String... args) {
prefix+(args as List).join(',')
}
assert foo('A') == 'A'
assert foo('A','1') == 'A1'
assert foo('A','1','2','3') == 'A1,2,3'
@@ -90,71 +77,53 @@ class DirectMethodCallWithVargsTest extends GroovyTestCase {
def b = '2'
def c = '3'
assert foo('A',a,b,c) == 'A1,2,3'
'''
}

void testDirectMethodCallWithPrefixAndPrimitiveVargs() {
def config = new CompilerConfiguration()
config.addCompilationCustomizers(
new MyCustomizer()
)
GroovyShell shell = new GroovyShell(config)
shell.evaluate '''
@Test
void testDirectMethodCallWithArgumentAndPrimitiveVargs() {
assertScript shell, '''
def foo(int prefix, int... args) {
"$prefix"+(args as List).join(',')
}
assert foo(1) == '1'
assert foo(1,1) == '11'
assert foo(1,1,2,3) == '11,2,3'
assert foo(1,1,2,3,4) == '11,2,3,4'
'''
}

private static class MyCustomizer extends CompilationCustomizer {
//--------------------------------------------------------------------------

MyCustomizer() {
super(CompilePhase.CANONICALIZATION)
}

@Override
void call(final SourceUnit source, final GeneratorContext context, final ClassNode classNode) {
def visitor = new MethodCallVisitor(source)
classNode.methods.each { visitor.visitMethod(it) }
private final GroovyShell shell = GroovyShell.withConfig {
inline(phase: 'CANONICALIZATION') { sourceUnit, x, classNode ->
def visitor = new MethodCallVisitor(sourceUnit)
classNode.methods.each(visitor.&acceptMethod)
visitor.visitClass(classNode)
}
}

private static class MethodCallVisitor extends ClassCodeVisitorSupport {
private final SourceUnit unit
private MethodNode fooMethod
final SourceUnit sourceUnit

MethodCallVisitor(SourceUnit source) {
unit = source
}

@Override
protected SourceUnit getSourceUnit() {
return unit
MethodCallVisitor(final SourceUnit unit) {
sourceUnit = unit
}

@Override
void visitMethod(final MethodNode node) {
super.visitMethod(node)
if (node.name=='foo') {
void acceptMethod(final MethodNode node) {
if (node.name == 'foo') {
fooMethod = node
}
}


@Override
void visitMethodCallExpression(final MethodCallExpression call) {
super.visitMethodCallExpression(call)
if (call.methodAsString=='foo') {
if (call.methodAsString == 'foo') {
call.methodTarget = fooMethod
}
}


}
}

0 comments on commit 4964a5e

Please sign in to comment.