#CompileStatic used on plain Groovy scripts - groovy

I'm not sure if this is already possible but is it possible to use #CompileStatic on just plain groovy scripts without wrapping the script in a class?
I'm using the GroovyClassLoader to parse and compile scripts dynamically and so it would be nice if scripts could use #CompileStatic

You can add a compiler customizer:
configuration.addCompilationCustomizers(
new ASTTransformationCustomizer(CompileStatic))
If you were on the command line you could provide a --configscript config.groovy. This answer has an example of how the script should look like:
withConfig(configuration) {
ast(groovy.transform.CompileStatic)
}

You can add the #CompileStatic annotation to a class or a method.
It does not work for a whole script but you can add it to some methods or some classes of your script.
You have to add the import groovy.transform.CompileStatic in the imports of your script or class.
As an example you can try the following script in a GroovyConsole, commenting/uncommenting the method annotation:
import groovy.transform.CompileStatic
#CompileStatic
void loop() {
def d1 = new Date();
int[] t = new int[32*1024*1024]
(0 .. 32*1024*1024-1).each {
it -> t[it] = it
}
def d2 = new Date();
println d2.getTime() - d1.getTime()
}
loop()

Related

How is spock calling this function in this test?

I'm going through this example but something about it is very confusing to me: https://www.testcookbook.com/book/groovy/jenkins/intro-testing-job-dsl.html
In this test, how/what is executing getJobFiles()? I don't see it being called anywhere. Is there some magic with jobFiles? Is specifying jobFiles somehow calling getJobFiles?
import javaposse.jobdsl.dsl.DslScriptLoader
import javaposse.jobdsl.plugin.JenkinsJobManagement
import org.junit.ClassRule
import org.jvnet.hudson.test.JenkinsRule
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Unroll
class JobScriptsSpec extends Specification {
#Shared
#ClassRule
JenkinsRule jenkinsRule = new JenkinsRule()
#Unroll
def 'test script #file.name'(File file) {
given:
def jobManagement = new JenkinsJobManagement(System.out, [:], new File('.'))
when:
new DslScriptLoader(jobManagement).runScript(file.text)
then:
noExceptionThrown()
where:
file << jobFiles
}
static List<File> getJobFiles() {
List<File> files = []
new File('jobs').eachFileRecurse {
if (it.name.endsWith('.groovy')) {
files << it
}
}
files
}
}
Edit
It seems like jobFiles does call getJobFiles() but I don't understand how. Is this a groovy or spock feature? I've been trying to research this but can finding anything explaining this in detail.
This is standard Groovy functionality. You can abbreviate any getter call like
def file = new File("sarek-test-parent/sarek-test-common/src/main/java")
println file.name // getName()
println file.parent // getParent()
println file.absolutePath // getAbsolutePath()
println file.directory // isDirectory()
java
sarek-test-parent\sarek-test-common\src\main
C:\Users\alexa\Documents\java-src\Sarek\sarek-test-parent\sarek-test-common\src\main\java
true
The same works for setters:
new Person().name = "John" // setName("John")
new Person().zipCode = "12345" // setZipCode("12345")
Actually the second link provided by jaco0646 explains it, just his mixing up this simple fact with data providers clouds the explanation.
Edit
When Groovy determines that jobFiles does not refer to any existing variable, it considers the name as a Groovy property, which then allows it to take advantage of the shortcut notation for accessing properties.

importing groovy scripts from other scripts dynamically

I am trying to load 2 groovy scripts from a remote location (say S3). The first script is straight forward. It names a package a.b and the file itself is called c.dsl.
package a.b;
int i = 10;
public void anotherfunc() {};
private void anotheractivityFunc() { int k = 9;};
The second groovy script has the following code in it (it is named s.dsl)
package c.d;
// Notice the import of the other script file here
import a.b.c;
int i = 10;
c myself;
public void func() {};
private void activityFunc()
{
int k = 9;
c nbn;
};
This script is trying to create an object of the class defined in the first script. I run this through with this code
CustomClassLoader loader = new CustomClassLoader(GroovyDSLTest.class.getClassLoader(), configuration);
GroovyShell shell = new GroovyShell(loader, configuration);
Script script = shell.parse(scriptText, "s.dsl");
CustomClassLoader is defined with this function
public Class loadClass(name, ...)
{
try
{
return super.loadClass(name, lookupScriptFiles, preferClassOverScript, resolve);
}
catch (ClassNotFoundException e)
{
if (name.equals("a.b.c"))
// Notice how I handle the import a.b.c from the second groovy file
return parseClass(<text from c.dsl>, "c.dsl");
throw e;
}
}
I get a compile error when I do this. It loads the a.b.c file cleanly. But when I try to make a member variable "c" it fails.
s.dsl: 1: [Static type checking] - The variable [myself] is undeclared.
# line 8, column 3.
c myself;
^
s.dsl: 1: [Static type checking] - Cannot find matching method c.d.s#c(java.lang.Object). Please check if the declared type is right and if the method exists.
# line 8, column 1.
c myself;
^
Why is it not able to find c? I was able to print the classloader as it loads classes and it did load a.b.c. Changing the above to use a.b.c instead of just "c" does not work as well. What is the name of the class that groovy generates from the script file. I want to be able to access this class and create objects of it.
How about using #BaseScript?
You can create a class that you want to import:
abstract class Something extends Script {
...
}
And #BaseScript can help you to import it: #groovy.transform.BaseScript Something something
See: http://docs.groovy-lang.org/2.5.2/html/gapi/groovy/transform/BaseScript.html
Grrrr... I figured this out.
a) one needs to call in loadClass setClassCacheEntry();
b) Also naming a class "c" does not seem to work. I renamed it to MyClass and it seems to work

Passing parameters from Java to Groovy in SOAPUI

I have a java program as below,
package test;
class HelloWorld {
public static void main(String[] args) {
System.out.println("Hi");
String a="World";
}
}
And groovy script as below,
import test.HelloWorld
HelloWorld.main(null)
And I am using this groovy script in SOAPUI after keeping the jar file in /bin/ext SOAPUI folder.
I am able to execute this groovy script in console.
But my requirement is,I need to pass variable say "a" in java program in SOAPUI test input.
For eg:
Add test input in soapui
<add>
<a> "Variable a" </a>
<b>5</b>
</add>
I want to refer that variable coming out of java program in this test input.Please let me know the way.
Java and Groovy integrates smooth. However to access the String a value a must be and attribute in object not a variable in a method. Let me explain; in your case for example in the Java part instead of a main method create an object like:
package test;
class HelloWorld {
private String a;
public HelloWorld(){
this.a = "World";
}
public String getA(){
return this.a;
}
}
Compile and add the jars to SOAPUI_HOME/bin/ext.
Then from groovy script in SOAPUI you can instantiate the class and get the value:
import test.HelloWorld
def a = new HelloWorld().getA()
log.info a // this prints --> World
If besides you want to use this value in a request you've to set the property value at some test level (for example in testCase):
import test.HelloWorld
def a = new HelloWorld().getA()
log.info a // this prints --> World
testRunner.testCase.setPropertyValue('myVar',a)
Now that your variable is set you can use the follow notation to use it in your requests: ${#TestCase#myVar}, in your request sample:
<add>
<a>${#TestCase#myVar}</a>
<b>5</b>
</add>
Hope it helps,

Groovy Compile time AST transformation: Assignment to a field

I'm currently trying to implement some Groovy compile time AST transformations, but I ran into trouble:
How do I specify an AST transformation for an assignment statement to a field? i.e. the AST transformation should transform the following code:
class MyClass {
#MyTransformation
String myField
public void init() {
}
}
into something like
class MyClass {
String myField
public void init() {
this.myField = "initialized!"
}
}
I tried it with this AST builder invocation:
def ast = new AstBuilder().buildFromSpec {
expression{
declaration {
variable "myField"
token "="
constant "initialized!"
}
}
}
But after inserting the resulting statement in the "init" method of the declaring class, it instead inserted a variable assignment, as in
java.lang.Object myField = "initialized!"
I looked through the examples incorporated in the Ast Builder TestCase, but they only cover field declaration in the class body, not assignments to fields. My own tries using fieldNode all resulted in compiler errors. I set the compile phase to INSTRUCTION_SELECTION; I think this should be fine.
How do I achieve this? A solution based on the AstBuilder#buildFromSpec method is preferred, but any help would be highly appreciated.
I usually recommand not to use the AST builder. It's good for prototyping, but you don't really control what it generates. In particular, here, it's not capable of handling the fact that the variable expression you create should reference the field node. AST Builder is very nice to learn about the AST, but shouldn't be used in production code IMHO.
Here is a self contained example that demonstrates how you can acheive what you want. The code inside #ASTTest would correspond to your transform code:
import groovy.transform.ASTTest
import org.codehaus.groovy.ast.expr.BinaryExpression
import org.codehaus.groovy.ast.expr.VariableExpression
import org.codehaus.groovy.ast.expr.ConstantExpression
import org.codehaus.groovy.ast.stmt.ExpressionStatement
import org.codehaus.groovy.syntax.Token
import org.codehaus.groovy.syntax.Types
class MyClass {
String myField
#ASTTest(phase=SEMANTIC_ANALYSIS,value={
def classNode = node.declaringClass
def field = classNode.getDeclaredField('myField')
def assignment = new BinaryExpression(
new VariableExpression(field),
Token.newSymbol(Types.EQUAL, 0, 0),
new ConstantExpression('initialized!')
)
node.code.addStatement(new ExpressionStatement(assignment))
})
public void init() {
}
}
def c = new MyClass()
c.init()
println c.myField
Hope this helps!

Black listing synchronized keyword for groovy scripts

There is an application where users can provide custom groovy scripts. They can write their own functions in those scripts. I want to restrict people from using the 'synchronized' keyword as well as some other keywords in these scripts. For example it should not be possible to write a function like below.
public synchronized void test() {
}
I am creating a CompilerConfiguration and using the SecureASTCustomizer. However adding org.codehaus.groovy.syntax.Types.KEYWORD_SYNCHRONIZED to the list of black listed tokens doesn't seem to do the job. (if I add org.codehaus.groovy.syntax.Types.PLUS it's preventing the usage of '+' within scripts.. but doesn't seem to do the job for synchronized)
Any ideas on how to achieve this ...
You can do something like this:
import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.syntax.SyntaxException
import org.codehaus.groovy.ast.ClassNode
import org.codehaus.groovy.control.SourceUnit
import org.codehaus.groovy.classgen.GeneratorContext
class SynchronizedRemover extends org.codehaus.groovy.control.customizers.CompilationCustomizer {
SynchronizedRemover() {
super(org.codehaus.groovy.control.CompilePhase.CONVERSION)
}
void call(final SourceUnit source, final GeneratorContext context, final ClassNode classNode) {
classNode.methods.each { mn ->
if (mn.modifiers & 0x0020) { // 0x0020 is for synchronized
source.addError(new SyntaxException("Synchronized is not allowed", mn.lineNumber, mn.columnNumber))
}
}
}
}
def config = new CompilerConfiguration()
config.addCompilationCustomizers(new SynchronizedRemover())
def shell = new GroovyShell(config)
shell.evaluate '''
class Foo { public synchronized void foo() { println 'bar' } }
'''
The idea is to create a compilation customizer that checks generated classes and for each method, add an error if the synchronized modifier is present. For synchronized block inside methods, you can probably use the SecureASTCustomizer with a custom statement checker.

Resources