SlideShare a Scribd company logo
1 of 75
Copyright © 2011, Oracle and/or its affiliates. All rights reserved.   Insert Information Protection Policy Classification from Slide 8
1
DSLs with Groovy
Jim Driscoll
     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.   Insert Information Protection Policy Classification from Slide 8
 2
Talk Agenda

    •   Introduction
    •   Executing Programs
    •   The Nouns - Objects
    •   The Verbs - Functions
    •   The AST




    Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
3
Introductions

    • Experiences
    • What is a DSL
    • Why Groovy?




    Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
4
Experience

    • ADFm
           – Part of Oracle’s ADF Framework
    • Makes heavy use of Groovy as a scripting language
           – (But almost all the extensions are written in Java)
    • Extensions include
           –      Accessing Field (Attribute) names as read-only variables
           –      Associating functions with tables (Views) at runtime
           –      Creating new top-level functions at runtime
           –      A security overlay to existing classes
           –      Extending existing classes with new functions
    Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
5
What’s a DSL

    • In general terms...
           – A purpose built language to solve a specific task
                    • For example, LOGO, SQL, YACC, HTML
    • Some (i.e. Fowler) advocate it be minimal
           – But that’s not what we’re talking about today
    • For our purposes today:
           – Creating a custom language via adding and removing language
             features until you have what your users need
           – We’ll mostly focus on adding features today
           – (But I’ll show you how to take stuff out too.)
    Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
6
Why Groovy?

    • Groovy is very extensible
           – Language features (via AST Transforms, Customizers)
           – Object behavior (via MOP, invokeMethod, getProperty)
    • Groovy runs in a JVM
           – And most Java source will run as Groovy without modification
    • Groovy is very dynamic
           – Optional (duck) typing
           – Easy inline compilation
    • Groovy’s Open Source
           – With an incredibly helpful community
    Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
7
Some Caveats

    • I’m not an expert in Groovy
           – So many of my examples are in Java
    • There are many different ways to do almost everything
           – There are at least 7 different ways use the String “System.exit(0)”
             to shut down the VM.
    • Groovy 1.8 adds even more functionality
           – I haven’t used it in production yet.
           – But I’ll still talk about CompilationCustomizers today
    • This is a BOF - I’d love any comments realtime

    Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
8
Talk Agenda

    •   Introduction
    •   Executing Programs
    •   The Nouns - Objects
    •   The Verbs - Functions
    •   The AST




    Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
9
Executing Programs

     • You’ll want to run user provided scripts at runtime
     • Simplest way is with GroovyShell.evaluate(String)
            – GroovyShell.evaluate(“println ‘hello world’”);
            – but this is expensive...
     • Separate parsing from execution:
            – Script script = GroovyShell.parse(String)
                     • Time measured in seconds (you’re running a compiler)
                     • Offers an intercept point for caching
            – Script.run()


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
10
Executing Scripts

     import groovy.lang.GroovyShell;
     import groovy.lang.Script;

     public class JavaOne01 {

         public static void main(String[] args) {
           GroovyShell shell = new GroovyShell();
           Script script = shell.parse("println 'hello world'");
           script.run();
         }
     }




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
11
Under the hood

     • Groovy will take your unqualified text, and place it in the
       Script’s run method.
     • run has a return of type Object
     • If you don’t have a return value, the final statement
       executed will be turned into one
            – thus “x == y” is turned into “return x == y”, which is a Boolean
     • You can provide inline methods...
            – “int test() {return 1} n test()+2” is valid, returns 3
            – but you can also run the test() method separately via reflection

     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
12
Talk Agenda

     •   Introduction
     •   Executing Programs
     •   The Nouns - Objects
     •   The Verbs - Functions
     •   The AST




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
13
Adding Objects via Bindings

     • A Binding is a way to pass values in/out of a script
            – Used as unqualified variables (“println name”)
            – declared variables (def, or a type) go in their own memory space
     • Groovy provides a default Binding, and you can write
       your own
     • Use it to provide:
            – Constant values
            – read only variables
            – default values

     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
14
Sample Binding

                   public class MyBinding extends groovy.lang.Binding {
                     public Object getVariable(String name) {
                        if ("name".equals(name)) {
                            return "Jim";
                        } else if ("date".equals(name)) {
                            return new Date();
                        }
                        return super.getVariable(name);
                     }
                     public void setVariable(String name, Object value) {
                        if ("date".equals(name) || "name".equals(name)) {
                            throw new RuntimeException("variable "+name+" is read only");
                        }
                        super.setVariable(name, value);
                     }
                   }

     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
15
Sample Binding

                   public class MyBinding extends groovy.lang.Binding {
                     public Object getVariable(String name) {
                        if ("name".equals(name)) {
                            return "Jim";
                        } else if ("date".equals(name)) {
                            return new Date();
                        }
                        return super.getVariable(name);
                     }
                     public void setVariable(String name, Object value) {
                        if ("date".equals(name) || "name".equals(name)) {
                            throw new RuntimeException("variable "+name+" is read only");
                        }
                        super.setVariable(name, value);
                     }
                   }

     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
16
Sample Binding

                   public class MyBinding extends groovy.lang.Binding {
                     public Object getVariable(String name) {
                        if ("name".equals(name)) {
                            return "Jim";
                        } else if ("date".equals(name)) {
                            return new Date();
                        }
                        return super.getVariable(name);
                     }
                     public void setVariable(String name, Object value) {
                        if ("date".equals(name) || "name".equals(name)) {
                            throw new RuntimeException("variable "+name+" is read only");
                        }
                        super.setVariable(name, value);
                     }
                   }

     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
17
Using the binding
     package javaone02;                                                     The script:
     import groovy.lang.GroovyShell;
     import groovy.lang.Script;                                             println ‘hello ‘ + name
                                                                            println ‘it is now: ‘ + date
     public class JavaOne02 {

          public static void main(String[] args) {                          The output is:
            String runMe =
                  "println 'hello '+ name n"+                              hello Jim
                  "println 'it is now: ' +date";
            GroovyShell shell = new GroovyShell();                          it is now: Wed Sep 28 18:35:41 PDT 2011
            Script script = shell.parse(runMe);
            script.setBinding(new MyBinding());
            script.run();
          }
     }


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
18
Talk Agenda

     •   Introduction
     •   Executing Programs
     •   The Nouns - Objects
     •   The Verbs - Functions
     •   The AST




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
19
The ScriptBaseClass

     • Allows you to declare top level methods
     • Inserted as part of the CompilerConfiguration
            – An optional parameter to GroovyShell
     • Combine this with Groovy’s optional parenthesis...
            – (And optional chaining, introduced in 1.8...)
            – And simple constructs like LOGO’s turtle manipulation language
              start to come within reach




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
20
Turtle.groovy
     class Turtle {
        int distance = 0;
        def forward(int i) {
           distance += i
           println "moved forward "+i
           return this
        }
        def right(int i) {
           println "steering wheel stuck"
           return this
        }
        def traveled() {
           println "total distance traveled " + distance
           return this
        }
     }




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
21
Turtle.groovy
     class Turtle {
        int distance = 0;
        def forward(int i) {
           distance += i
           println "moved forward "+i
           return this
        }
        def right(int i) {
           println "steering wheel stuck"
           return this
        }
        def traveled() {
           println "total distance traveled " + distance
           return this
        }
     }




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
22
BaseScript.groovy
     abstract class BaseScript extends Script {
       Turtle t = new Turtle();
       def forward(int i) {
          t.forward(i)
       }
       def right(int i) {
          t.right(i)
       }
       def traveled() {
          t.traveled()
       }
     }




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
23
Main Class
     import groovy.lang.GroovyShell;
     import groovy.lang.Script;
     import org.codehaus.groovy.control.CompilerConfiguration;

     public class JavaOne03BaseScript {
       public static void main(String[] args) {
          CompilerConfiguration config = new CompilerConfiguration();
          config.setScriptBaseClass("BaseScript");
          GroovyShell shell = new GroovyShell(config);
          Script script = shell.parse("forward 10 right 3 forward 5 traveled()");
          script.run();
       }
     }

     Outputs:
     moved forward 10
     steering wheel stuck
     moved forward 5
     total distance traveled 15
      Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
24
Main Class
     import groovy.lang.GroovyShell;
     import groovy.lang.Script;
     import org.codehaus.groovy.control.CompilerConfiguration;

     public class JavaOne03BaseScript {
       public static void main(String[] args) {
          CompilerConfiguration config = new CompilerConfiguration();
          config.setScriptBaseClass("BaseScript");
          GroovyShell shell = new GroovyShell(config);
          Script script = shell.parse("forward 10 right 3 forward 5 traveled()");
          script.run();
       }
     }

     Outputs:
     moved forward 10
     steering wheel stuck
     moved forward 5
     total distance traveled 15
      Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
25
Main Class
     import groovy.lang.GroovyShell;
     import groovy.lang.Script;
     import org.codehaus.groovy.control.CompilerConfiguration;

     public class JavaOne03BaseScript {
       public static void main(String[] args) {
          CompilerConfiguration config = new CompilerConfiguration();
          config.setScriptBaseClass("BaseScript");
          GroovyShell shell = new GroovyShell(config);
          Script script = shell.parse("forward 10 right 3 forward 5 traveled()");
          script.run();
       }
     }

     Outputs:
     moved forward 10
     steering wheel stuck
     moved forward 5
     total distance traveled 15
      Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
26
Main Class
     import groovy.lang.GroovyShell;
     import groovy.lang.Script;
     import org.codehaus.groovy.control.CompilerConfiguration;

     public class JavaOne03BaseScript {
       public static void main(String[] args) {
          CompilerConfiguration config = new CompilerConfiguration();
          config.setScriptBaseClass("BaseScript");
          GroovyShell shell = new GroovyShell(config);
          Script script = shell.parse("forward 10 right 3 forward 5 traveled()");
          script.run();
       }
     }

     Outputs:
     moved forward 10
     steering wheel stuck
     moved forward 5
     total distance traveled 15
      Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
27
invokeMethod

     • invokeMethod is a standard method, inherited from
       GroovyObject
     • Allows dynamic execution of methods
     • Used with BaseScript, it allows using a context object
     • One of many ways to do the same thing in Groovy
     • Can test before you run with
            – object.metaClass.respondsTo(object,name,*args)



     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
28
Objects - Cat and Dog

     class Dog {
        def speak() {
          println "woof"
        }
     }
     class Cat {
        def speak() {
          println "meow"
        }
     }



     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
29
Binding
 class MyBinding extends Binding {
    def a = new Dog();
    def getVariable(String name) {
      if (name == "animal") {
          return a
      }
      return super.getVariable(name);
    }
    void setVariable(String name, Object value) {
      if ("animal".equals(name)) {
          throw new RuntimeException("variable "+name+" is read only");
      }
      super.setVariable(name, value);
    }
    void changeAnimal(def animal) {
      a = animal;
    }
 }


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
30
Binding
 class MyBinding extends Binding {
    def a = new Dog();
    def getVariable(String name) {
      if (name == "animal") {
          return a
      }
      return super.getVariable(name);
    }
    void setVariable(String name, Object value) {
      if ("animal".equals(name)) {
          throw new RuntimeException("variable "+name+" is read only");
      }
      super.setVariable(name, value);
    }
    void changeAnimal(def animal) {
      a = animal;
    }
 }


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
31
Binding
 class MyBinding extends Binding {
    def a = new Dog();
    def getVariable(String name) {
      if (name == "animal") {
          return a
      }
      return super.getVariable(name);
    }
    void setVariable(String name, Object value) {
      if ("animal".equals(name)) {
          throw new RuntimeException("variable "+name+" is read only");
      }
      super.setVariable(name, value);
    }
    void changeAnimal(def animal) {
      a = animal;
    }
 }


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
32
BaseScript.groovy
     abstract class BaseScript extends Script {
       def invokeMethod(String name, args) {
          animal."$name"(*args)
       }
       def change(String animal) {
          if (animal == "dog") {
              binding.changeAnimal(new Dog())
          } else if (animal == "cat") {
              binding.changeAnimal(new Cat())
          }
       }
     }



     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
33
BaseScript.groovy
     abstract class BaseScript extends Script {
       def invokeMethod(String name, args) {
          animal."$name"(*args)
       }
       def change(String animal) {
          if (animal == "dog") {
              binding.changeAnimal(new Dog())
          } else if (animal == "cat") {
              binding.changeAnimal(new Cat())
          }
       }
     }



     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
34
BaseScript.groovy
     abstract class BaseScript extends Script {
       def invokeMethod(String name, args) {
          animal."$name"(*args)
       }
       def change(String animal) {
          if (animal == "dog") {
              binding.changeAnimal(new Dog())
          } else if (animal == "cat") {
              binding.changeAnimal(new Cat())
          }
       }
     }



     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
35
BaseScript.groovy
     abstract class BaseScript extends Script {
       def invokeMethod(String name, args) {
          animal."$name"(*args)
       }
       def change(String animal) {
          if (animal == "dog") {
              binding.changeAnimal(new Dog())
          } else if (animal == "cat") {
              binding.changeAnimal(new Cat())
          }
       }
     }



     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
36
BaseScript.groovy
     abstract class BaseScript extends Script {
       def invokeMethod(String name, args) {
          animal."$name"(*args)
       }
       def change(String animal) {
          if (animal == "dog") {
              binding.changeAnimal(new Dog())
          } else if (animal == "cat") {
              binding.changeAnimal(new Cat())
          }
       }
     }



     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
37
Results

     The Script:
     speak()
     change(‘cat’)
     speak()
     change(‘dog’)
     animal.speak()

     Produces the Output:
     woof
     meow
     woof


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
38
get/setProperty

     • Properties are accessed via “object.propertyname”
     • Properties can be either fields or accessors/mutators
            – foo.bar can either be get/setBar, or the variable bar in foo
     • Like invokeMethod, this behavior can be overridden
     • Beware the “Java Field” operator - .@, which bypasses
       the get/setProperty methods




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
39
Talk Agenda

     •   Introduction
     •   Executing Programs
     •   The Nouns - Objects
     •   The Verbs - Functions
     •   The AST




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
40
Compilation Customizers

     •   New in 1.8
     •   Replace and/or augment ASTTransformations
     •   Modify code as part of the compilation process
     •   For example:
            – adding imports (via ImportCompilationCustomizer)
            – disabling language features (via SecureASTCustomizer)
            – automatically applying ASTTransforms (either local or global) (via
              ASTTranformationCustomizer)


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
41
Adding Imports
 import groovy.lang.GroovyShell;
 import groovy.lang.Script;
 import org.codehaus.groovy.control.CompilerConfiguration;
 import org.codehaus.groovy.control.customizers.ImportCustomizer;

 public class JavaOne05Customizer {

       public static void main(String[] args) {
         CompilerConfiguration config = new CompilerConfiguration();
         ImportCustomizer customizer = new ImportCustomizer();
         customizer.addStaticStars("java.lang.Math");
         config.addCompilationCustomizers(customizer);
         GroovyShell shell = new GroovyShell(config);               Output:
         Script script = shell.parse("println PI; println cos(1)"); 3.141592653589793
         script.run();                                              0.5403023058681398
       }
 }


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
42
Adding Imports
 import groovy.lang.GroovyShell;
 import groovy.lang.Script;
 import org.codehaus.groovy.control.CompilerConfiguration;
 import org.codehaus.groovy.control.customizers.ImportCustomizer;

 public class JavaOne05Customizer {

       public static void main(String[] args) {
         CompilerConfiguration config = new CompilerConfiguration();
         ImportCustomizer customizer = new ImportCustomizer();
         customizer.addStaticStars("java.lang.Math");
         config.addCompilationCustomizers(customizer);
         GroovyShell shell = new GroovyShell(config);               Output:
         Script script = shell.parse("println PI; println cos(1)"); 3.141592653589793
         script.run();                                              0.5403023058681398
       }
 }


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
43
Adding Imports
 import groovy.lang.GroovyShell;
 import groovy.lang.Script;
 import org.codehaus.groovy.control.CompilerConfiguration;
 import org.codehaus.groovy.control.customizers.ImportCustomizer;

 public class JavaOne05Customizer {

       public static void main(String[] args) {
         CompilerConfiguration config = new CompilerConfiguration();
         ImportCustomizer customizer = new ImportCustomizer();
         customizer.addStaticStars("java.lang.Math");
         config.addCompilationCustomizers(customizer);
         GroovyShell shell = new GroovyShell(config);               Output:
         Script script = shell.parse("println PI; println cos(1)"); 3.141592653589793
         script.run();                                              0.5403023058681398
       }
 }


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
44
Disallowing ++ Operations
 Script script;
 CompilerConfiguration config = new CompilerConfiguration();
 SecureASTCustomizer customizer = new SecureASTCustomizer();
 List<Integer> tokens = new ArrayList<Integer>();
 tokens.add(Types.PLUS_PLUS);
 customizer.setTokensBlacklist(tokens);
 config.addCompilationCustomizers(customizer);
 GroovyShell shell = new GroovyShell(config);
 try {
    script = shell.parse("def i = 1 ; println i++");
 } catch (MultipleCompilationErrorsException mcee) {
    Throwable t = mcee.getErrorCollector().getException(0);
    System.out.println(t.getClass().getName());
    System.out.println(t.getMessage());
    return;                                          Output:
 }                                                   java.lang.SecurityException
 script.run();                                       Token ("++" at 1:22: "++" ) is   not allowed


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
45
Disallowing ++ Operations
 Script script;
 CompilerConfiguration config = new CompilerConfiguration();
 SecureASTCustomizer customizer = new SecureASTCustomizer();
 List<Integer> tokens = new ArrayList<Integer>();
 tokens.add(Types.PLUS_PLUS);
 customizer.setTokensBlacklist(tokens);
 config.addCompilationCustomizers(customizer);
 GroovyShell shell = new GroovyShell(config);
 try {
    script = shell.parse("def i = 1 ; println i++");
 } catch (MultipleCompilationErrorsException mcee) {
    Throwable t = mcee.getErrorCollector().getException(0);
    System.out.println(t.getClass().getName());
    System.out.println(t.getMessage());
    return;                                          Output:
 }                                                   java.lang.SecurityException
 script.run();                                       Token ("++" at 1:22: "++" ) is   not allowed


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
46
Disallowing ++ Operations
 Script script;
 CompilerConfiguration config = new CompilerConfiguration();
 SecureASTCustomizer customizer = new SecureASTCustomizer();
 List<Integer> tokens = new ArrayList<Integer>();
 tokens.add(Types.PLUS_PLUS);
 customizer.setTokensBlacklist(tokens);
 config.addCompilationCustomizers(customizer);
 GroovyShell shell = new GroovyShell(config);
 try {
    script = shell.parse("def i = 1 ; println i++");
 } catch (MultipleCompilationErrorsException mcee) {
    Throwable t = mcee.getErrorCollector().getException(0);
    System.out.println(t.getClass().getName());
    System.out.println(t.getMessage());
    return;                                          Output:
 }                                                   java.lang.SecurityException
 script.run();                                       Token ("++" at 1:22: "++" ) is   not allowed


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
47
Disallowing ++ Operations
 Script script;
 CompilerConfiguration config = new CompilerConfiguration();
 SecureASTCustomizer customizer = new SecureASTCustomizer();
 List<Integer> tokens = new ArrayList<Integer>();
 tokens.add(Types.PLUS_PLUS);
 customizer.setTokensBlacklist(tokens);
 config.addCompilationCustomizers(customizer);
 GroovyShell shell = new GroovyShell(config);
 try {
    script = shell.parse("def i = 1 ; println i++");
 } catch (MultipleCompilationErrorsException mcee) {
    Throwable t = mcee.getErrorCollector().getException(0);
    System.out.println(t.getClass().getName());
    System.out.println(t.getMessage());
    return;                                          Output:
 }                                                   java.lang.SecurityException
 script.run();                                       Token ("++" at 1:22: "++" ) is   not allowed


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
48
Forbidding for loops
Script script;
CompilerConfiguration config = new CompilerConfiguration();
SecureASTCustomizer customizer = new SecureASTCustomizer();
List<Class<? extends Statement>> statements = new ArrayList<Class<? extends Statement>>();
statements.add(ForStatement.class);
customizer.setStatementsBlacklist(statements);
config.addCompilationCustomizers(customizer);
GroovyShell shell = new GroovyShell(config);
try {
   script = shell.parse("for (i in [1,2,3]) {}");
} catch (MultipleCompilationErrorsException mcee) {
   Throwable t = mcee.getErrorCollector().getException(0);
   System.out.println(t.getClass().getName());
   System.out.println(t.getMessage());
   return;
                                                  Output:
}                                                 java.lang.SecurityException
script.run();                                     ForStatements are not allowed


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
49
Forbidding for loops
Script script;
CompilerConfiguration config = new CompilerConfiguration();
SecureASTCustomizer customizer = new SecureASTCustomizer();
List<Class<? extends Statement>> statements = new ArrayList<Class<? extends Statement>>();
statements.add(ForStatement.class);
customizer.setStatementsBlacklist(statements);
config.addCompilationCustomizers(customizer);
GroovyShell shell = new GroovyShell(config);
try {
   script = shell.parse("for (i in [1,2,3]) {}");
} catch (MultipleCompilationErrorsException mcee) {
   Throwable t = mcee.getErrorCollector().getException(0);
   System.out.println(t.getClass().getName());
   System.out.println(t.getMessage());
   return;
                                                  Output:
}                                                 java.lang.SecurityException
script.run();                                     ForStatements are not allowed


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
50
Forbidding for loops
Script script;
CompilerConfiguration config = new CompilerConfiguration();
SecureASTCustomizer customizer = new SecureASTCustomizer();
List<Class<? extends Statement>> statements = new ArrayList<Class<? extends Statement>>();
statements.add(ForStatement.class);
customizer.setStatementsBlacklist(statements);
config.addCompilationCustomizers(customizer);
GroovyShell shell = new GroovyShell(config);
try {
   script = shell.parse("for (i in [1,2,3]) {}");
} catch (MultipleCompilationErrorsException mcee) {
   Throwable t = mcee.getErrorCollector().getException(0);
   System.out.println(t.getClass().getName());
   System.out.println(t.getMessage());
   return;
                                                  Output:
}                                                 java.lang.SecurityException
script.run();                                     ForStatements are not allowed


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
51
Forbidding for loops
Script script;
CompilerConfiguration config = new CompilerConfiguration();
SecureASTCustomizer customizer = new SecureASTCustomizer();
List<Class<? extends Statement>> statements = new ArrayList<Class<? extends Statement>>();
statements.add(ForStatement.class);
customizer.setStatementsBlacklist(statements);
config.addCompilationCustomizers(customizer);
GroovyShell shell = new GroovyShell(config);
try {
   script = shell.parse("for (i in [1,2,3]) {}");
} catch (MultipleCompilationErrorsException mcee) {
   Throwable t = mcee.getErrorCollector().getException(0);
   System.out.println(t.getClass().getName());
   System.out.println(t.getMessage());
   return;
                                                  Output:
}                                                 java.lang.SecurityException
script.run();                                     ForStatements are not allowed


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
52
Forbidding for loops
Script script;
CompilerConfiguration config = new CompilerConfiguration();
SecureASTCustomizer customizer = new SecureASTCustomizer();
List<Class<? extends Statement>> statements = new ArrayList<Class<? extends Statement>>();
statements.add(ForStatement.class);
customizer.setStatementsBlacklist(statements);
config.addCompilationCustomizers(customizer);
GroovyShell shell = new GroovyShell(config);
try {
   script = shell.parse("for (i in [1,2,3]) {}");
} catch (MultipleCompilationErrorsException mcee) {
   Throwable t = mcee.getErrorCollector().getException(0);
   System.out.println(t.getClass().getName());
   System.out.println(t.getMessage());
   return;
                                                  Output:
}                                                 java.lang.SecurityException
script.run();                                     ForStatements are not allowed


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
53
ASTTransformations

     • Used to modify generated code from the compiler
     • Either global or local
            – Globals are triggered by compile time configuration
            – Locals are triggered by Annotations
     • Groovy compiler generates an Abstract Syntax Tree
            – breaks code into tree of Statements
                     • which in turn are combinations of Statements and/or Expressions
     • You can examine code with groovyConsole to see the
       tree via Script -> Inspect AST

     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
54
Sample AST
     Sample code:
     def i = 1
     println i




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
55
A Simple Use of AST - Checking Bind Variables

     • An early problem we ran into
     • Need to determine if binding variables are valid
            – Most useful for read-only bindings
            – Check for things like misspelling (so, “usrename” vs. “username”)
     • Create a new CompilationCustomizer
     • Visitor pattern means we visit every VariableExpression
     • Test if it’s going against the supplied Binding


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
56
Scanning for Binding variables, part 1


     class ScriptVisitingCustomizer extends CompilationCustomizer {
        def visitor
        ScriptVisitingCustomizer() {super(CompilePhase.SEMANTIC_ANALYSIS)}
        void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException {
          for (method in classNode.methods) {
             if (method.name == 'run') method.code.visit(visitor)
          }
        }
     }

     class MyVisitor extends ClassCodeVisitorSupport {
        void visitVariableExpression(VariableExpression expression) {
          if (expression.accessedVariable instanceof DynamicVariable)
              println expression.name
        }
        protected SourceUnit getSourceUnit() {return source}
     }

     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
57
Scanning for Binding variables, part 1


     class ScriptVisitingCustomizer extends CompilationCustomizer {
        def visitor
        ScriptVisitingCustomizer() {super(CompilePhase.SEMANTIC_ANALYSIS)}
        void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException {
          for (method in classNode.methods) {
             if (method.name == 'run') method.code.visit(visitor)
          }
        }
     }

     class MyVisitor extends ClassCodeVisitorSupport {
        void visitVariableExpression(VariableExpression expression) {
          if (expression.accessedVariable instanceof DynamicVariable)
              println expression.name
        }
        protected SourceUnit getSourceUnit() {return source}
     }

     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
58
Scanning for Binding variables, part 1


     class ScriptVisitingCustomizer extends CompilationCustomizer {
        def visitor
        ScriptVisitingCustomizer() {super(CompilePhase.SEMANTIC_ANALYSIS)}
        void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException {
          for (method in classNode.methods) {
             if (method.name == 'run') method.code.visit(visitor)
          }
        }
     }

     class MyVisitor extends ClassCodeVisitorSupport {
        void visitVariableExpression(VariableExpression expression) {
          if (expression.accessedVariable instanceof DynamicVariable)
              println expression.name
        }
        protected SourceUnit getSourceUnit() {return source}
     }

     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
59
Scanning for Binding variables, part 2

     def scriptText = """
     println baz
     println foo
     def bar = 2
     println bar
     """

     CompilerConfiguration config = new CompilerConfiguration();
     def customizer = new ScriptVisitingCustomizer(visitor: new MyVisitor());   Output:
     config.addCompilationCustomizers(customizer);                              baz
                                                                                foo
     GroovyShell shell = new GroovyShell(config);
     Script script = shell.parse(scriptText);




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
60
Scanning for Binding variables, part 2

     def scriptText = """
     println baz
     println foo
     def bar = 2
     println bar
     """

     CompilerConfiguration config = new CompilerConfiguration();
     def customizer = new ScriptVisitingCustomizer(visitor: new MyVisitor());   Output:
     config.addCompilationCustomizers(customizer);                              baz
                                                                                foo
     GroovyShell shell = new GroovyShell(config);
     Script script = shell.parse(scriptText);




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
61
ASTTransformations

     • Numerous standard ASTTransforms are available
            – ThreadInterrupt - add Thread.currentThread().isInterrupted
              checks on loops, closures, first Statement in a Block
            – TimedInterrupt - as above, with a timeout check
            – ConditionalInterrupt - as above, with any closure
            – EqualsAndHashCode - create equals() and hashCode() methods
            – AutoClone - create clone() method
            – Immutable - create constructors, accessors, make final




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
62
Creating an ASTTransformation

     •   Create an Annotation
     •   Create an ASTTransformation class
     •   (Optional, but likely) Create a ClassCodeVisitor
     •   To use:
            – Decorate relevant object (method, class, etc) with annotation or
            – Use ASTTransformationCustomizer in GroovyShell




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
63
NameChange.groovy



     @Retention(RetentionPolicy.SOURCE)
     @Target(ElementType.TYPE)
     @GroovyASTTransformationClass("NameChangeASTTransformation")
     public @interface NameChange {
     }




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
64
NameChangeASTTransformation
@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
public class NameChangeASTTransformation implements ASTTransformation {
   public void visit(ASTNode[] nodes, SourceUnit source) {
     def visitor = new CustomClassVisitor();
     if (nodes.length != 2) throw new RuntimeException("exception");
     nodes[1].visitContents(visitor);
   }
}
class CustomClassVisitor extends ClassCodeVisitorSupport {
   protected SourceUnit getSourceUnit() {
     throw new UnsupportedOperationException("Not supported yet.");
   }
   public void visitConstantExpression(ConstantExpression expression) {
         if (expression.value == "Bob") {
             expression.value = "Alice"
         }
     super.visitConstantExpression(expression);
   }
}

     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
65
NameChangeASTTransformation
@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
public class NameChangeASTTransformation implements ASTTransformation {
   public void visit(ASTNode[] nodes, SourceUnit source) {
     def visitor = new CustomClassVisitor();
     if (nodes.length != 2) throw new RuntimeException("exception");
     nodes[1].visitContents(visitor);
   }
}
class CustomClassVisitor extends ClassCodeVisitorSupport {
   protected SourceUnit getSourceUnit() {
     throw new UnsupportedOperationException("Not supported yet.");
   }
   public void visitConstantExpression(ConstantExpression expression) {
         if (expression.value == "Bob") {
             expression.value = "Alice"
         }
     super.visitConstantExpression(expression);
   }
}

     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
66
NameChangeASTTransformation
@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
public class NameChangeASTTransformation implements ASTTransformation {
   public void visit(ASTNode[] nodes, SourceUnit source) {
     def visitor = new CustomClassVisitor();
     if (nodes.length != 2) throw new RuntimeException("exception");
     nodes[1].visitContents(visitor);
   }
}
class CustomClassVisitor extends ClassCodeVisitorSupport {
   protected SourceUnit getSourceUnit() {
     throw new UnsupportedOperationException("Not supported yet.");
   }
   public void visitConstantExpression(ConstantExpression expression) {
         if (expression.value == "Bob") {
             expression.value = "Alice"
         }
     super.visitConstantExpression(expression);
   }
}

     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
67
NameChangeASTTransformation
@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
public class NameChangeASTTransformation implements ASTTransformation {
   public void visit(ASTNode[] nodes, SourceUnit source) {
     def visitor = new CustomClassVisitor();
     if (nodes.length != 2) throw new RuntimeException("exception");
     nodes[1].visitContents(visitor);
   }
}
class CustomClassVisitor extends ClassCodeVisitorSupport {
   protected SourceUnit getSourceUnit() {
     throw new UnsupportedOperationException("Not supported yet.");
   }
   public void visitConstantExpression(ConstantExpression expression) {
         if (expression.value == "Bob") {
             expression.value = "Alice"
         }
     super.visitConstantExpression(expression);
   }
}

     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
68
Run with Annotation


                String runMe = "@NameChange n"+
                               "class Name {n"+
                               " static def name = 'Bob'n"+
                               "} n" +
                               "println Name.name";

                GroovyShell shell = new GroovyShell();
                Script script = shell.parse(runMe);
                script.run();
                                                                            Output:
                                                                            Alice




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
69
Run with Annotation

     String runMe = "class Name {n"+
                    "static def name = 'Bob'n"+
                    "} n" +
                    "println Name.name";

     CompilerConfiguration config = new CompilerConfiguration();
     ASTTransformationCustomizer customizer = new ASTTransformationCustomizer(NameChange.class);
     config.addCompilationCustomizers(customizer);

     GroovyShell shell = new GroovyShell(config);
     Script script = shell.parse(runMe);
                                                                            Output:
     script.run();                                                          Alice




     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
70
Securing Your Code

     • GroovyShell(ClassLoader,Binding,CompilerConfiguration)
     • GroovyShell.parse(GroovyCodeSource)
            – GroovyCodeSource(String script, String name, String codeBase)
     • SecureASTCustomizer
            – ArithmeticShell
            – static analysis only!
     • method/property wrapping



     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
71
Last Note: Naming Matters!

     This Code:                                                             Can take this as input:
     class Book {
        void name(String name) {                                            Book.create {
           println name                                                       name "DSLs in Action"
        }                                                                     loanedTo "Jim","Sint Si"
        void quantity(int i) {                                                quantity 1
           println i                                                        }
        }
        void loanedTo(String[] names) {
           println names
        }
        static create(closure) {
           def book = new Book()
           book.with closure
           return book
        }
     }                                                                      *example inspired by “DSLs in Action”*

     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
72
Resources

     • General Groovy Books
            – Groovy Programming: An Introduction for Java Programmers
                     • (K&R style simple book)
            – Programming Groovy
            – Groovy in Action
                     • (2nd Ed coming soon, with DSL chapters)
     • Domain Specific Languages
            – Groovy for Domain-Specific Languages
                     • (great discussion of MOP, Builders)
            – DSLs in Action
                     • (contains multiple language examples)
     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
73
Q&A


     Copyright © 2011, Oracle and/or its affiliates. All rights reserved.
74
Copyright © 2011, Oracle and/or its affiliates. All rights reserved.   Insert Information Protection Policy Classification from Slide 8
75

More Related Content

What's hot

Clojure, Plain and Simple
Clojure, Plain and SimpleClojure, Plain and Simple
Clojure, Plain and SimpleBen Mabey
 
Clojure for Java developers
Clojure for Java developersClojure for Java developers
Clojure for Java developersJohn Stevenson
 
Java 7 - What's New?
Java 7 - What's New?Java 7 - What's New?
Java 7 - What's New?Kobib9
 
JDK1.7 features
JDK1.7 featuresJDK1.7 features
JDK1.7 featuresindia_mani
 
The Ring programming language version 1.5.2 book - Part 176 of 181
The Ring programming language version 1.5.2 book - Part 176 of 181The Ring programming language version 1.5.2 book - Part 176 of 181
The Ring programming language version 1.5.2 book - Part 176 of 181Mahmoud Samir Fayed
 
The Ring programming language version 1.5.4 book - Part 180 of 185
The Ring programming language version 1.5.4 book - Part 180 of 185The Ring programming language version 1.5.4 book - Part 180 of 185
The Ring programming language version 1.5.4 book - Part 180 of 185Mahmoud Samir Fayed
 
JUnit5 and TestContainers
JUnit5 and TestContainersJUnit5 and TestContainers
JUnit5 and TestContainersSunghyouk Bae
 
Flutter 是什麼?用 Flutter 會省到時間嗎? @ GDG Devfest2020
Flutter 是什麼?用 Flutter 會省到時間嗎? @ GDG Devfest2020Flutter 是什麼?用 Flutter 會省到時間嗎? @ GDG Devfest2020
Flutter 是什麼?用 Flutter 會省到時間嗎? @ GDG Devfest2020Johnny Sung
 
Excuse me, sir, do you have a moment to talk about tests in Kotlin
Excuse me, sir, do you have a moment to talk about tests in KotlinExcuse me, sir, do you have a moment to talk about tests in Kotlin
Excuse me, sir, do you have a moment to talk about tests in Kotlinleonsabr
 
Priming Java for Speed at Market Open
Priming Java for Speed at Market OpenPriming Java for Speed at Market Open
Priming Java for Speed at Market OpenAzul Systems Inc.
 
Whats New in Java 5, 6, & 7 (Webinar Presentation - June 2013)
Whats New in Java 5, 6, & 7 (Webinar Presentation - June 2013)Whats New in Java 5, 6, & 7 (Webinar Presentation - June 2013)
Whats New in Java 5, 6, & 7 (Webinar Presentation - June 2013)DevelopIntelligence
 
Kotlin coroutines and spring framework
Kotlin coroutines and spring frameworkKotlin coroutines and spring framework
Kotlin coroutines and spring frameworkSunghyouk Bae
 
Getting started with Clojure
Getting started with ClojureGetting started with Clojure
Getting started with ClojureJohn Stevenson
 
4java Basic Syntax
4java Basic Syntax4java Basic Syntax
4java Basic SyntaxAdil Jafri
 

What's hot (20)

Java 7 New Features
Java 7 New FeaturesJava 7 New Features
Java 7 New Features
 
Clojure, Plain and Simple
Clojure, Plain and SimpleClojure, Plain and Simple
Clojure, Plain and Simple
 
Java 7 & 8 New Features
Java 7 & 8 New FeaturesJava 7 & 8 New Features
Java 7 & 8 New Features
 
JDK1.6
JDK1.6JDK1.6
JDK1.6
 
55 New Features in Java 7
55 New Features in Java 755 New Features in Java 7
55 New Features in Java 7
 
Clojure for Java developers
Clojure for Java developersClojure for Java developers
Clojure for Java developers
 
Java 7 - What's New?
Java 7 - What's New?Java 7 - What's New?
Java 7 - What's New?
 
JDK1.7 features
JDK1.7 featuresJDK1.7 features
JDK1.7 features
 
The Ring programming language version 1.5.2 book - Part 176 of 181
The Ring programming language version 1.5.2 book - Part 176 of 181The Ring programming language version 1.5.2 book - Part 176 of 181
The Ring programming language version 1.5.2 book - Part 176 of 181
 
The Ring programming language version 1.5.4 book - Part 180 of 185
The Ring programming language version 1.5.4 book - Part 180 of 185The Ring programming language version 1.5.4 book - Part 180 of 185
The Ring programming language version 1.5.4 book - Part 180 of 185
 
JUnit5 and TestContainers
JUnit5 and TestContainersJUnit5 and TestContainers
JUnit5 and TestContainers
 
Flutter 是什麼?用 Flutter 會省到時間嗎? @ GDG Devfest2020
Flutter 是什麼?用 Flutter 會省到時間嗎? @ GDG Devfest2020Flutter 是什麼?用 Flutter 會省到時間嗎? @ GDG Devfest2020
Flutter 是什麼?用 Flutter 會省到時間嗎? @ GDG Devfest2020
 
Excuse me, sir, do you have a moment to talk about tests in Kotlin
Excuse me, sir, do you have a moment to talk about tests in KotlinExcuse me, sir, do you have a moment to talk about tests in Kotlin
Excuse me, sir, do you have a moment to talk about tests in Kotlin
 
Priming Java for Speed at Market Open
Priming Java for Speed at Market OpenPriming Java for Speed at Market Open
Priming Java for Speed at Market Open
 
2 P Seminar
2 P Seminar2 P Seminar
2 P Seminar
 
Whats New in Java 5, 6, & 7 (Webinar Presentation - June 2013)
Whats New in Java 5, 6, & 7 (Webinar Presentation - June 2013)Whats New in Java 5, 6, & 7 (Webinar Presentation - June 2013)
Whats New in Java 5, 6, & 7 (Webinar Presentation - June 2013)
 
Kotlin coroutines and spring framework
Kotlin coroutines and spring frameworkKotlin coroutines and spring framework
Kotlin coroutines and spring framework
 
Clojure: a LISP for the JVM
Clojure: a LISP for the JVMClojure: a LISP for the JVM
Clojure: a LISP for the JVM
 
Getting started with Clojure
Getting started with ClojureGetting started with Clojure
Getting started with Clojure
 
4java Basic Syntax
4java Basic Syntax4java Basic Syntax
4java Basic Syntax
 

Similar to Groovy DSLs (JavaOne Presentation)

An Introduction to Gradle for Java Developers
An Introduction to Gradle for Java DevelopersAn Introduction to Gradle for Java Developers
An Introduction to Gradle for Java DevelopersKostas Saidis
 
Boosting Your Testing Productivity with Groovy
Boosting Your Testing Productivity with GroovyBoosting Your Testing Productivity with Groovy
Boosting Your Testing Productivity with GroovyJames Williams
 
Javaone2008 Bof 5101 Groovytesting
Javaone2008 Bof 5101 GroovytestingJavaone2008 Bof 5101 Groovytesting
Javaone2008 Bof 5101 GroovytestingAndres Almiray
 
Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy PluginsPaul King
 
Infinum android talks_10_getting groovy on android
Infinum android talks_10_getting groovy on androidInfinum android talks_10_getting groovy on android
Infinum android talks_10_getting groovy on androidInfinum
 
GTAC Boosting your Testing Productivity with Groovy
GTAC Boosting your Testing Productivity with GroovyGTAC Boosting your Testing Productivity with Groovy
GTAC Boosting your Testing Productivity with GroovyAndres Almiray
 
Groovy & Grails: Scripting for Modern Web Applications
Groovy & Grails: Scripting for Modern Web ApplicationsGroovy & Grails: Scripting for Modern Web Applications
Groovy & Grails: Scripting for Modern Web Applicationsrohitnayak
 
Groovy - Grails as a modern scripting language for Web applications
Groovy - Grails as a modern scripting language for Web applicationsGroovy - Grails as a modern scripting language for Web applications
Groovy - Grails as a modern scripting language for Web applicationsIndicThreads
 
Gg Code Mash2009 20090106
Gg Code Mash2009 20090106Gg Code Mash2009 20090106
Gg Code Mash2009 20090106Jim Shingler
 
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume LaforgeGroovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume LaforgeGuillaume Laforge
 
Turtle Graphics in Groovy
Turtle Graphics in GroovyTurtle Graphics in Groovy
Turtle Graphics in GroovyJim Driscoll
 
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...Paul King
 
Back to the future with Java 7 (Geekout June/2011)
Back to the future with Java 7 (Geekout June/2011)Back to the future with Java 7 (Geekout June/2011)
Back to the future with Java 7 (Geekout June/2011)Martijn Verburg
 
Scala, Akka, and Play: An Introduction on Heroku
Scala, Akka, and Play: An Introduction on HerokuScala, Akka, and Play: An Introduction on Heroku
Scala, Akka, and Play: An Introduction on HerokuHavoc Pennington
 
Introduction to Oracle Groovy
Introduction to Oracle GroovyIntroduction to Oracle Groovy
Introduction to Oracle GroovyDeepak Bhagat
 
Gradleintroduction 111010130329-phpapp01
Gradleintroduction 111010130329-phpapp01Gradleintroduction 111010130329-phpapp01
Gradleintroduction 111010130329-phpapp01Tino Isnich
 

Similar to Groovy DSLs (JavaOne Presentation) (20)

An Introduction to Gradle for Java Developers
An Introduction to Gradle for Java DevelopersAn Introduction to Gradle for Java Developers
An Introduction to Gradle for Java Developers
 
Boosting Your Testing Productivity with Groovy
Boosting Your Testing Productivity with GroovyBoosting Your Testing Productivity with Groovy
Boosting Your Testing Productivity with Groovy
 
Javaone2008 Bof 5101 Groovytesting
Javaone2008 Bof 5101 GroovytestingJavaone2008 Bof 5101 Groovytesting
Javaone2008 Bof 5101 Groovytesting
 
Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy Plugins
 
What's New in Groovy 1.6?
What's New in Groovy 1.6?What's New in Groovy 1.6?
What's New in Groovy 1.6?
 
55j7
55j755j7
55j7
 
Infinum android talks_10_getting groovy on android
Infinum android talks_10_getting groovy on androidInfinum android talks_10_getting groovy on android
Infinum android talks_10_getting groovy on android
 
GTAC Boosting your Testing Productivity with Groovy
GTAC Boosting your Testing Productivity with GroovyGTAC Boosting your Testing Productivity with Groovy
GTAC Boosting your Testing Productivity with Groovy
 
Groovy & Grails: Scripting for Modern Web Applications
Groovy & Grails: Scripting for Modern Web ApplicationsGroovy & Grails: Scripting for Modern Web Applications
Groovy & Grails: Scripting for Modern Web Applications
 
Groovy - Grails as a modern scripting language for Web applications
Groovy - Grails as a modern scripting language for Web applicationsGroovy - Grails as a modern scripting language for Web applications
Groovy - Grails as a modern scripting language for Web applications
 
Gg Code Mash2009 20090106
Gg Code Mash2009 20090106Gg Code Mash2009 20090106
Gg Code Mash2009 20090106
 
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume LaforgeGroovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
Groovy and Grails in Action - Devoxx 2008 - University - Guillaume Laforge
 
Turtle Graphics in Groovy
Turtle Graphics in GroovyTurtle Graphics in Groovy
Turtle Graphics in Groovy
 
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
Industrial Strength Groovy - Tools for the Professional Groovy Developer: Pau...
 
Back to the future with Java 7 (Geekout June/2011)
Back to the future with Java 7 (Geekout June/2011)Back to the future with Java 7 (Geekout June/2011)
Back to the future with Java 7 (Geekout June/2011)
 
Scala, Akka, and Play: An Introduction on Heroku
Scala, Akka, and Play: An Introduction on HerokuScala, Akka, and Play: An Introduction on Heroku
Scala, Akka, and Play: An Introduction on Heroku
 
Introduction to Oracle Groovy
Introduction to Oracle GroovyIntroduction to Oracle Groovy
Introduction to Oracle Groovy
 
Gradle Introduction
Gradle IntroductionGradle Introduction
Gradle Introduction
 
Gradleintroduction 111010130329-phpapp01
Gradleintroduction 111010130329-phpapp01Gradleintroduction 111010130329-phpapp01
Gradleintroduction 111010130329-phpapp01
 
Groovy & Grails
Groovy & GrailsGroovy & Grails
Groovy & Grails
 

Recently uploaded

Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersThousandEyes
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...shyamraj55
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxKatpro Technologies
 
Azure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAzure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAndikSusilo4
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticscarlostorres15106
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Paola De la Torre
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 

Recently uploaded (20)

Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
Azure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAzure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & Application
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 

Groovy DSLs (JavaOne Presentation)

  • 1. Copyright © 2011, Oracle and/or its affiliates. All rights reserved. Insert Information Protection Policy Classification from Slide 8 1
  • 2. DSLs with Groovy Jim Driscoll Copyright © 2011, Oracle and/or its affiliates. All rights reserved. Insert Information Protection Policy Classification from Slide 8 2
  • 3. Talk Agenda • Introduction • Executing Programs • The Nouns - Objects • The Verbs - Functions • The AST Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 3
  • 4. Introductions • Experiences • What is a DSL • Why Groovy? Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 4
  • 5. Experience • ADFm – Part of Oracle’s ADF Framework • Makes heavy use of Groovy as a scripting language – (But almost all the extensions are written in Java) • Extensions include – Accessing Field (Attribute) names as read-only variables – Associating functions with tables (Views) at runtime – Creating new top-level functions at runtime – A security overlay to existing classes – Extending existing classes with new functions Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 5
  • 6. What’s a DSL • In general terms... – A purpose built language to solve a specific task • For example, LOGO, SQL, YACC, HTML • Some (i.e. Fowler) advocate it be minimal – But that’s not what we’re talking about today • For our purposes today: – Creating a custom language via adding and removing language features until you have what your users need – We’ll mostly focus on adding features today – (But I’ll show you how to take stuff out too.) Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 6
  • 7. Why Groovy? • Groovy is very extensible – Language features (via AST Transforms, Customizers) – Object behavior (via MOP, invokeMethod, getProperty) • Groovy runs in a JVM – And most Java source will run as Groovy without modification • Groovy is very dynamic – Optional (duck) typing – Easy inline compilation • Groovy’s Open Source – With an incredibly helpful community Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 7
  • 8. Some Caveats • I’m not an expert in Groovy – So many of my examples are in Java • There are many different ways to do almost everything – There are at least 7 different ways use the String “System.exit(0)” to shut down the VM. • Groovy 1.8 adds even more functionality – I haven’t used it in production yet. – But I’ll still talk about CompilationCustomizers today • This is a BOF - I’d love any comments realtime Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 8
  • 9. Talk Agenda • Introduction • Executing Programs • The Nouns - Objects • The Verbs - Functions • The AST Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 9
  • 10. Executing Programs • You’ll want to run user provided scripts at runtime • Simplest way is with GroovyShell.evaluate(String) – GroovyShell.evaluate(“println ‘hello world’”); – but this is expensive... • Separate parsing from execution: – Script script = GroovyShell.parse(String) • Time measured in seconds (you’re running a compiler) • Offers an intercept point for caching – Script.run() Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 10
  • 11. Executing Scripts import groovy.lang.GroovyShell; import groovy.lang.Script; public class JavaOne01 { public static void main(String[] args) { GroovyShell shell = new GroovyShell(); Script script = shell.parse("println 'hello world'"); script.run(); } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 11
  • 12. Under the hood • Groovy will take your unqualified text, and place it in the Script’s run method. • run has a return of type Object • If you don’t have a return value, the final statement executed will be turned into one – thus “x == y” is turned into “return x == y”, which is a Boolean • You can provide inline methods... – “int test() {return 1} n test()+2” is valid, returns 3 – but you can also run the test() method separately via reflection Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 12
  • 13. Talk Agenda • Introduction • Executing Programs • The Nouns - Objects • The Verbs - Functions • The AST Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 13
  • 14. Adding Objects via Bindings • A Binding is a way to pass values in/out of a script – Used as unqualified variables (“println name”) – declared variables (def, or a type) go in their own memory space • Groovy provides a default Binding, and you can write your own • Use it to provide: – Constant values – read only variables – default values Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 14
  • 15. Sample Binding public class MyBinding extends groovy.lang.Binding { public Object getVariable(String name) { if ("name".equals(name)) { return "Jim"; } else if ("date".equals(name)) { return new Date(); } return super.getVariable(name); } public void setVariable(String name, Object value) { if ("date".equals(name) || "name".equals(name)) { throw new RuntimeException("variable "+name+" is read only"); } super.setVariable(name, value); } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 15
  • 16. Sample Binding public class MyBinding extends groovy.lang.Binding { public Object getVariable(String name) { if ("name".equals(name)) { return "Jim"; } else if ("date".equals(name)) { return new Date(); } return super.getVariable(name); } public void setVariable(String name, Object value) { if ("date".equals(name) || "name".equals(name)) { throw new RuntimeException("variable "+name+" is read only"); } super.setVariable(name, value); } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 16
  • 17. Sample Binding public class MyBinding extends groovy.lang.Binding { public Object getVariable(String name) { if ("name".equals(name)) { return "Jim"; } else if ("date".equals(name)) { return new Date(); } return super.getVariable(name); } public void setVariable(String name, Object value) { if ("date".equals(name) || "name".equals(name)) { throw new RuntimeException("variable "+name+" is read only"); } super.setVariable(name, value); } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 17
  • 18. Using the binding package javaone02; The script: import groovy.lang.GroovyShell; import groovy.lang.Script; println ‘hello ‘ + name println ‘it is now: ‘ + date public class JavaOne02 { public static void main(String[] args) { The output is: String runMe = "println 'hello '+ name n"+ hello Jim "println 'it is now: ' +date"; GroovyShell shell = new GroovyShell(); it is now: Wed Sep 28 18:35:41 PDT 2011 Script script = shell.parse(runMe); script.setBinding(new MyBinding()); script.run(); } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 18
  • 19. Talk Agenda • Introduction • Executing Programs • The Nouns - Objects • The Verbs - Functions • The AST Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 19
  • 20. The ScriptBaseClass • Allows you to declare top level methods • Inserted as part of the CompilerConfiguration – An optional parameter to GroovyShell • Combine this with Groovy’s optional parenthesis... – (And optional chaining, introduced in 1.8...) – And simple constructs like LOGO’s turtle manipulation language start to come within reach Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 20
  • 21. Turtle.groovy class Turtle { int distance = 0; def forward(int i) { distance += i println "moved forward "+i return this } def right(int i) { println "steering wheel stuck" return this } def traveled() { println "total distance traveled " + distance return this } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 21
  • 22. Turtle.groovy class Turtle { int distance = 0; def forward(int i) { distance += i println "moved forward "+i return this } def right(int i) { println "steering wheel stuck" return this } def traveled() { println "total distance traveled " + distance return this } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 22
  • 23. BaseScript.groovy abstract class BaseScript extends Script { Turtle t = new Turtle(); def forward(int i) { t.forward(i) } def right(int i) { t.right(i) } def traveled() { t.traveled() } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 23
  • 24. Main Class import groovy.lang.GroovyShell; import groovy.lang.Script; import org.codehaus.groovy.control.CompilerConfiguration; public class JavaOne03BaseScript { public static void main(String[] args) { CompilerConfiguration config = new CompilerConfiguration(); config.setScriptBaseClass("BaseScript"); GroovyShell shell = new GroovyShell(config); Script script = shell.parse("forward 10 right 3 forward 5 traveled()"); script.run(); } } Outputs: moved forward 10 steering wheel stuck moved forward 5 total distance traveled 15 Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 24
  • 25. Main Class import groovy.lang.GroovyShell; import groovy.lang.Script; import org.codehaus.groovy.control.CompilerConfiguration; public class JavaOne03BaseScript { public static void main(String[] args) { CompilerConfiguration config = new CompilerConfiguration(); config.setScriptBaseClass("BaseScript"); GroovyShell shell = new GroovyShell(config); Script script = shell.parse("forward 10 right 3 forward 5 traveled()"); script.run(); } } Outputs: moved forward 10 steering wheel stuck moved forward 5 total distance traveled 15 Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 25
  • 26. Main Class import groovy.lang.GroovyShell; import groovy.lang.Script; import org.codehaus.groovy.control.CompilerConfiguration; public class JavaOne03BaseScript { public static void main(String[] args) { CompilerConfiguration config = new CompilerConfiguration(); config.setScriptBaseClass("BaseScript"); GroovyShell shell = new GroovyShell(config); Script script = shell.parse("forward 10 right 3 forward 5 traveled()"); script.run(); } } Outputs: moved forward 10 steering wheel stuck moved forward 5 total distance traveled 15 Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 26
  • 27. Main Class import groovy.lang.GroovyShell; import groovy.lang.Script; import org.codehaus.groovy.control.CompilerConfiguration; public class JavaOne03BaseScript { public static void main(String[] args) { CompilerConfiguration config = new CompilerConfiguration(); config.setScriptBaseClass("BaseScript"); GroovyShell shell = new GroovyShell(config); Script script = shell.parse("forward 10 right 3 forward 5 traveled()"); script.run(); } } Outputs: moved forward 10 steering wheel stuck moved forward 5 total distance traveled 15 Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 27
  • 28. invokeMethod • invokeMethod is a standard method, inherited from GroovyObject • Allows dynamic execution of methods • Used with BaseScript, it allows using a context object • One of many ways to do the same thing in Groovy • Can test before you run with – object.metaClass.respondsTo(object,name,*args) Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 28
  • 29. Objects - Cat and Dog class Dog { def speak() { println "woof" } } class Cat { def speak() { println "meow" } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 29
  • 30. Binding class MyBinding extends Binding { def a = new Dog(); def getVariable(String name) { if (name == "animal") { return a } return super.getVariable(name); } void setVariable(String name, Object value) { if ("animal".equals(name)) { throw new RuntimeException("variable "+name+" is read only"); } super.setVariable(name, value); } void changeAnimal(def animal) { a = animal; } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 30
  • 31. Binding class MyBinding extends Binding { def a = new Dog(); def getVariable(String name) { if (name == "animal") { return a } return super.getVariable(name); } void setVariable(String name, Object value) { if ("animal".equals(name)) { throw new RuntimeException("variable "+name+" is read only"); } super.setVariable(name, value); } void changeAnimal(def animal) { a = animal; } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 31
  • 32. Binding class MyBinding extends Binding { def a = new Dog(); def getVariable(String name) { if (name == "animal") { return a } return super.getVariable(name); } void setVariable(String name, Object value) { if ("animal".equals(name)) { throw new RuntimeException("variable "+name+" is read only"); } super.setVariable(name, value); } void changeAnimal(def animal) { a = animal; } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 32
  • 33. BaseScript.groovy abstract class BaseScript extends Script { def invokeMethod(String name, args) { animal."$name"(*args) } def change(String animal) { if (animal == "dog") { binding.changeAnimal(new Dog()) } else if (animal == "cat") { binding.changeAnimal(new Cat()) } } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 33
  • 34. BaseScript.groovy abstract class BaseScript extends Script { def invokeMethod(String name, args) { animal."$name"(*args) } def change(String animal) { if (animal == "dog") { binding.changeAnimal(new Dog()) } else if (animal == "cat") { binding.changeAnimal(new Cat()) } } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 34
  • 35. BaseScript.groovy abstract class BaseScript extends Script { def invokeMethod(String name, args) { animal."$name"(*args) } def change(String animal) { if (animal == "dog") { binding.changeAnimal(new Dog()) } else if (animal == "cat") { binding.changeAnimal(new Cat()) } } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 35
  • 36. BaseScript.groovy abstract class BaseScript extends Script { def invokeMethod(String name, args) { animal."$name"(*args) } def change(String animal) { if (animal == "dog") { binding.changeAnimal(new Dog()) } else if (animal == "cat") { binding.changeAnimal(new Cat()) } } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 36
  • 37. BaseScript.groovy abstract class BaseScript extends Script { def invokeMethod(String name, args) { animal."$name"(*args) } def change(String animal) { if (animal == "dog") { binding.changeAnimal(new Dog()) } else if (animal == "cat") { binding.changeAnimal(new Cat()) } } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 37
  • 38. Results The Script: speak() change(‘cat’) speak() change(‘dog’) animal.speak() Produces the Output: woof meow woof Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 38
  • 39. get/setProperty • Properties are accessed via “object.propertyname” • Properties can be either fields or accessors/mutators – foo.bar can either be get/setBar, or the variable bar in foo • Like invokeMethod, this behavior can be overridden • Beware the “Java Field” operator - .@, which bypasses the get/setProperty methods Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 39
  • 40. Talk Agenda • Introduction • Executing Programs • The Nouns - Objects • The Verbs - Functions • The AST Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 40
  • 41. Compilation Customizers • New in 1.8 • Replace and/or augment ASTTransformations • Modify code as part of the compilation process • For example: – adding imports (via ImportCompilationCustomizer) – disabling language features (via SecureASTCustomizer) – automatically applying ASTTransforms (either local or global) (via ASTTranformationCustomizer) Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 41
  • 42. Adding Imports import groovy.lang.GroovyShell; import groovy.lang.Script; import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.customizers.ImportCustomizer; public class JavaOne05Customizer { public static void main(String[] args) { CompilerConfiguration config = new CompilerConfiguration(); ImportCustomizer customizer = new ImportCustomizer(); customizer.addStaticStars("java.lang.Math"); config.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(config); Output: Script script = shell.parse("println PI; println cos(1)"); 3.141592653589793 script.run(); 0.5403023058681398 } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 42
  • 43. Adding Imports import groovy.lang.GroovyShell; import groovy.lang.Script; import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.customizers.ImportCustomizer; public class JavaOne05Customizer { public static void main(String[] args) { CompilerConfiguration config = new CompilerConfiguration(); ImportCustomizer customizer = new ImportCustomizer(); customizer.addStaticStars("java.lang.Math"); config.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(config); Output: Script script = shell.parse("println PI; println cos(1)"); 3.141592653589793 script.run(); 0.5403023058681398 } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 43
  • 44. Adding Imports import groovy.lang.GroovyShell; import groovy.lang.Script; import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.customizers.ImportCustomizer; public class JavaOne05Customizer { public static void main(String[] args) { CompilerConfiguration config = new CompilerConfiguration(); ImportCustomizer customizer = new ImportCustomizer(); customizer.addStaticStars("java.lang.Math"); config.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(config); Output: Script script = shell.parse("println PI; println cos(1)"); 3.141592653589793 script.run(); 0.5403023058681398 } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 44
  • 45. Disallowing ++ Operations Script script; CompilerConfiguration config = new CompilerConfiguration(); SecureASTCustomizer customizer = new SecureASTCustomizer(); List<Integer> tokens = new ArrayList<Integer>(); tokens.add(Types.PLUS_PLUS); customizer.setTokensBlacklist(tokens); config.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(config); try { script = shell.parse("def i = 1 ; println i++"); } catch (MultipleCompilationErrorsException mcee) { Throwable t = mcee.getErrorCollector().getException(0); System.out.println(t.getClass().getName()); System.out.println(t.getMessage()); return; Output: } java.lang.SecurityException script.run(); Token ("++" at 1:22: "++" ) is not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 45
  • 46. Disallowing ++ Operations Script script; CompilerConfiguration config = new CompilerConfiguration(); SecureASTCustomizer customizer = new SecureASTCustomizer(); List<Integer> tokens = new ArrayList<Integer>(); tokens.add(Types.PLUS_PLUS); customizer.setTokensBlacklist(tokens); config.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(config); try { script = shell.parse("def i = 1 ; println i++"); } catch (MultipleCompilationErrorsException mcee) { Throwable t = mcee.getErrorCollector().getException(0); System.out.println(t.getClass().getName()); System.out.println(t.getMessage()); return; Output: } java.lang.SecurityException script.run(); Token ("++" at 1:22: "++" ) is not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 46
  • 47. Disallowing ++ Operations Script script; CompilerConfiguration config = new CompilerConfiguration(); SecureASTCustomizer customizer = new SecureASTCustomizer(); List<Integer> tokens = new ArrayList<Integer>(); tokens.add(Types.PLUS_PLUS); customizer.setTokensBlacklist(tokens); config.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(config); try { script = shell.parse("def i = 1 ; println i++"); } catch (MultipleCompilationErrorsException mcee) { Throwable t = mcee.getErrorCollector().getException(0); System.out.println(t.getClass().getName()); System.out.println(t.getMessage()); return; Output: } java.lang.SecurityException script.run(); Token ("++" at 1:22: "++" ) is not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 47
  • 48. Disallowing ++ Operations Script script; CompilerConfiguration config = new CompilerConfiguration(); SecureASTCustomizer customizer = new SecureASTCustomizer(); List<Integer> tokens = new ArrayList<Integer>(); tokens.add(Types.PLUS_PLUS); customizer.setTokensBlacklist(tokens); config.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(config); try { script = shell.parse("def i = 1 ; println i++"); } catch (MultipleCompilationErrorsException mcee) { Throwable t = mcee.getErrorCollector().getException(0); System.out.println(t.getClass().getName()); System.out.println(t.getMessage()); return; Output: } java.lang.SecurityException script.run(); Token ("++" at 1:22: "++" ) is not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 48
  • 49. Forbidding for loops Script script; CompilerConfiguration config = new CompilerConfiguration(); SecureASTCustomizer customizer = new SecureASTCustomizer(); List<Class<? extends Statement>> statements = new ArrayList<Class<? extends Statement>>(); statements.add(ForStatement.class); customizer.setStatementsBlacklist(statements); config.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(config); try { script = shell.parse("for (i in [1,2,3]) {}"); } catch (MultipleCompilationErrorsException mcee) { Throwable t = mcee.getErrorCollector().getException(0); System.out.println(t.getClass().getName()); System.out.println(t.getMessage()); return; Output: } java.lang.SecurityException script.run(); ForStatements are not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 49
  • 50. Forbidding for loops Script script; CompilerConfiguration config = new CompilerConfiguration(); SecureASTCustomizer customizer = new SecureASTCustomizer(); List<Class<? extends Statement>> statements = new ArrayList<Class<? extends Statement>>(); statements.add(ForStatement.class); customizer.setStatementsBlacklist(statements); config.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(config); try { script = shell.parse("for (i in [1,2,3]) {}"); } catch (MultipleCompilationErrorsException mcee) { Throwable t = mcee.getErrorCollector().getException(0); System.out.println(t.getClass().getName()); System.out.println(t.getMessage()); return; Output: } java.lang.SecurityException script.run(); ForStatements are not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 50
  • 51. Forbidding for loops Script script; CompilerConfiguration config = new CompilerConfiguration(); SecureASTCustomizer customizer = new SecureASTCustomizer(); List<Class<? extends Statement>> statements = new ArrayList<Class<? extends Statement>>(); statements.add(ForStatement.class); customizer.setStatementsBlacklist(statements); config.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(config); try { script = shell.parse("for (i in [1,2,3]) {}"); } catch (MultipleCompilationErrorsException mcee) { Throwable t = mcee.getErrorCollector().getException(0); System.out.println(t.getClass().getName()); System.out.println(t.getMessage()); return; Output: } java.lang.SecurityException script.run(); ForStatements are not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 51
  • 52. Forbidding for loops Script script; CompilerConfiguration config = new CompilerConfiguration(); SecureASTCustomizer customizer = new SecureASTCustomizer(); List<Class<? extends Statement>> statements = new ArrayList<Class<? extends Statement>>(); statements.add(ForStatement.class); customizer.setStatementsBlacklist(statements); config.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(config); try { script = shell.parse("for (i in [1,2,3]) {}"); } catch (MultipleCompilationErrorsException mcee) { Throwable t = mcee.getErrorCollector().getException(0); System.out.println(t.getClass().getName()); System.out.println(t.getMessage()); return; Output: } java.lang.SecurityException script.run(); ForStatements are not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 52
  • 53. Forbidding for loops Script script; CompilerConfiguration config = new CompilerConfiguration(); SecureASTCustomizer customizer = new SecureASTCustomizer(); List<Class<? extends Statement>> statements = new ArrayList<Class<? extends Statement>>(); statements.add(ForStatement.class); customizer.setStatementsBlacklist(statements); config.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(config); try { script = shell.parse("for (i in [1,2,3]) {}"); } catch (MultipleCompilationErrorsException mcee) { Throwable t = mcee.getErrorCollector().getException(0); System.out.println(t.getClass().getName()); System.out.println(t.getMessage()); return; Output: } java.lang.SecurityException script.run(); ForStatements are not allowed Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 53
  • 54. ASTTransformations • Used to modify generated code from the compiler • Either global or local – Globals are triggered by compile time configuration – Locals are triggered by Annotations • Groovy compiler generates an Abstract Syntax Tree – breaks code into tree of Statements • which in turn are combinations of Statements and/or Expressions • You can examine code with groovyConsole to see the tree via Script -> Inspect AST Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 54
  • 55. Sample AST Sample code: def i = 1 println i Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 55
  • 56. A Simple Use of AST - Checking Bind Variables • An early problem we ran into • Need to determine if binding variables are valid – Most useful for read-only bindings – Check for things like misspelling (so, “usrename” vs. “username”) • Create a new CompilationCustomizer • Visitor pattern means we visit every VariableExpression • Test if it’s going against the supplied Binding Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 56
  • 57. Scanning for Binding variables, part 1 class ScriptVisitingCustomizer extends CompilationCustomizer { def visitor ScriptVisitingCustomizer() {super(CompilePhase.SEMANTIC_ANALYSIS)} void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException { for (method in classNode.methods) { if (method.name == 'run') method.code.visit(visitor) } } } class MyVisitor extends ClassCodeVisitorSupport { void visitVariableExpression(VariableExpression expression) { if (expression.accessedVariable instanceof DynamicVariable) println expression.name } protected SourceUnit getSourceUnit() {return source} } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 57
  • 58. Scanning for Binding variables, part 1 class ScriptVisitingCustomizer extends CompilationCustomizer { def visitor ScriptVisitingCustomizer() {super(CompilePhase.SEMANTIC_ANALYSIS)} void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException { for (method in classNode.methods) { if (method.name == 'run') method.code.visit(visitor) } } } class MyVisitor extends ClassCodeVisitorSupport { void visitVariableExpression(VariableExpression expression) { if (expression.accessedVariable instanceof DynamicVariable) println expression.name } protected SourceUnit getSourceUnit() {return source} } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 58
  • 59. Scanning for Binding variables, part 1 class ScriptVisitingCustomizer extends CompilationCustomizer { def visitor ScriptVisitingCustomizer() {super(CompilePhase.SEMANTIC_ANALYSIS)} void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException { for (method in classNode.methods) { if (method.name == 'run') method.code.visit(visitor) } } } class MyVisitor extends ClassCodeVisitorSupport { void visitVariableExpression(VariableExpression expression) { if (expression.accessedVariable instanceof DynamicVariable) println expression.name } protected SourceUnit getSourceUnit() {return source} } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 59
  • 60. Scanning for Binding variables, part 2 def scriptText = """ println baz println foo def bar = 2 println bar """ CompilerConfiguration config = new CompilerConfiguration(); def customizer = new ScriptVisitingCustomizer(visitor: new MyVisitor()); Output: config.addCompilationCustomizers(customizer); baz foo GroovyShell shell = new GroovyShell(config); Script script = shell.parse(scriptText); Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 60
  • 61. Scanning for Binding variables, part 2 def scriptText = """ println baz println foo def bar = 2 println bar """ CompilerConfiguration config = new CompilerConfiguration(); def customizer = new ScriptVisitingCustomizer(visitor: new MyVisitor()); Output: config.addCompilationCustomizers(customizer); baz foo GroovyShell shell = new GroovyShell(config); Script script = shell.parse(scriptText); Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 61
  • 62. ASTTransformations • Numerous standard ASTTransforms are available – ThreadInterrupt - add Thread.currentThread().isInterrupted checks on loops, closures, first Statement in a Block – TimedInterrupt - as above, with a timeout check – ConditionalInterrupt - as above, with any closure – EqualsAndHashCode - create equals() and hashCode() methods – AutoClone - create clone() method – Immutable - create constructors, accessors, make final Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 62
  • 63. Creating an ASTTransformation • Create an Annotation • Create an ASTTransformation class • (Optional, but likely) Create a ClassCodeVisitor • To use: – Decorate relevant object (method, class, etc) with annotation or – Use ASTTransformationCustomizer in GroovyShell Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 63
  • 64. NameChange.groovy @Retention(RetentionPolicy.SOURCE) @Target(ElementType.TYPE) @GroovyASTTransformationClass("NameChangeASTTransformation") public @interface NameChange { } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 64
  • 65. NameChangeASTTransformation @GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS) public class NameChangeASTTransformation implements ASTTransformation { public void visit(ASTNode[] nodes, SourceUnit source) { def visitor = new CustomClassVisitor(); if (nodes.length != 2) throw new RuntimeException("exception"); nodes[1].visitContents(visitor); } } class CustomClassVisitor extends ClassCodeVisitorSupport { protected SourceUnit getSourceUnit() { throw new UnsupportedOperationException("Not supported yet."); } public void visitConstantExpression(ConstantExpression expression) { if (expression.value == "Bob") { expression.value = "Alice" } super.visitConstantExpression(expression); } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 65
  • 66. NameChangeASTTransformation @GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS) public class NameChangeASTTransformation implements ASTTransformation { public void visit(ASTNode[] nodes, SourceUnit source) { def visitor = new CustomClassVisitor(); if (nodes.length != 2) throw new RuntimeException("exception"); nodes[1].visitContents(visitor); } } class CustomClassVisitor extends ClassCodeVisitorSupport { protected SourceUnit getSourceUnit() { throw new UnsupportedOperationException("Not supported yet."); } public void visitConstantExpression(ConstantExpression expression) { if (expression.value == "Bob") { expression.value = "Alice" } super.visitConstantExpression(expression); } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 66
  • 67. NameChangeASTTransformation @GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS) public class NameChangeASTTransformation implements ASTTransformation { public void visit(ASTNode[] nodes, SourceUnit source) { def visitor = new CustomClassVisitor(); if (nodes.length != 2) throw new RuntimeException("exception"); nodes[1].visitContents(visitor); } } class CustomClassVisitor extends ClassCodeVisitorSupport { protected SourceUnit getSourceUnit() { throw new UnsupportedOperationException("Not supported yet."); } public void visitConstantExpression(ConstantExpression expression) { if (expression.value == "Bob") { expression.value = "Alice" } super.visitConstantExpression(expression); } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 67
  • 68. NameChangeASTTransformation @GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS) public class NameChangeASTTransformation implements ASTTransformation { public void visit(ASTNode[] nodes, SourceUnit source) { def visitor = new CustomClassVisitor(); if (nodes.length != 2) throw new RuntimeException("exception"); nodes[1].visitContents(visitor); } } class CustomClassVisitor extends ClassCodeVisitorSupport { protected SourceUnit getSourceUnit() { throw new UnsupportedOperationException("Not supported yet."); } public void visitConstantExpression(ConstantExpression expression) { if (expression.value == "Bob") { expression.value = "Alice" } super.visitConstantExpression(expression); } } Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 68
  • 69. Run with Annotation String runMe = "@NameChange n"+ "class Name {n"+ " static def name = 'Bob'n"+ "} n" + "println Name.name"; GroovyShell shell = new GroovyShell(); Script script = shell.parse(runMe); script.run(); Output: Alice Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 69
  • 70. Run with Annotation String runMe = "class Name {n"+ "static def name = 'Bob'n"+ "} n" + "println Name.name"; CompilerConfiguration config = new CompilerConfiguration(); ASTTransformationCustomizer customizer = new ASTTransformationCustomizer(NameChange.class); config.addCompilationCustomizers(customizer); GroovyShell shell = new GroovyShell(config); Script script = shell.parse(runMe); Output: script.run(); Alice Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 70
  • 71. Securing Your Code • GroovyShell(ClassLoader,Binding,CompilerConfiguration) • GroovyShell.parse(GroovyCodeSource) – GroovyCodeSource(String script, String name, String codeBase) • SecureASTCustomizer – ArithmeticShell – static analysis only! • method/property wrapping Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 71
  • 72. Last Note: Naming Matters! This Code: Can take this as input: class Book { void name(String name) { Book.create { println name name "DSLs in Action" } loanedTo "Jim","Sint Si" void quantity(int i) { quantity 1 println i } } void loanedTo(String[] names) { println names } static create(closure) { def book = new Book() book.with closure return book } } *example inspired by “DSLs in Action”* Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 72
  • 73. Resources • General Groovy Books – Groovy Programming: An Introduction for Java Programmers • (K&R style simple book) – Programming Groovy – Groovy in Action • (2nd Ed coming soon, with DSL chapters) • Domain Specific Languages – Groovy for Domain-Specific Languages • (great discussion of MOP, Builders) – DSLs in Action • (contains multiple language examples) Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 73
  • 74. Q&A Copyright © 2011, Oracle and/or its affiliates. All rights reserved. 74
  • 75. Copyright © 2011, Oracle and/or its affiliates. All rights reserved. Insert Information Protection Policy Classification from Slide 8 75

Editor's Notes

  1. \n
  2. \n
  3. \n
  4. \n
  5. Ask what they&amp;#x2019;e worked on\n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \n
  52. \n
  53. \n
  54. \n
  55. \n
  56. \n
  57. \n
  58. Note: you can also use source.AST.code\n
  59. \n
  60. \n
  61. \n
  62. \n
  63. \n
  64. \n
  65. \n
  66. \n
  67. \n
  68. \n
  69. \n
  70. \n
  71. \n
  72. \n
  73. \n
  74. \n
  75. \n