210 likes | 335 Views
Guarded Program Transformations Using JTL. Tal Cohen, Joseph (Yossi) Gil, Itay Maman (submitted to ECOOP’07) Presented by Oren Mishali. Introduction. A growing interest in using the logic paradigm to query code
E N D
Guarded Program Transformations Using JTL Tal Cohen, Joseph (Yossi) Gil, Itay Maman (submitted to ECOOP’07) Presented by Oren Mishali
Introduction • A growing interest in using the logic paradigm to query code • The paper describes a side-effect free technique of using the paradigm for the task of program transformation • The technique is presented as an extension to JTL • Every JTL predicate maintains a “baggage” string variable • Simple rules for computing the baggage of compound predicates • The baggage string is returned upon successful match
Simple Baggage Management • The principle: any predicate returns the text of a Java code fragment which can be used for specifying the match “final” “final abstract” “public static int oldValue” “class Complex extends Number” “” “”
Tautologies • Predicates which always hold • (Hence always produce output)
Multiple Baggage • Suppressing the output of predicates: %public %static %final _ '?*'; %[public static final] _ '?*'; • A name of a Java class should begin with a lower case: testClassName := %2[ class '[a-z]?*' "begins with a lowercase letter." ]; testClassName := %2[class '[a-z]?*‘ "begins with a lowercase letter." ] | [class '?*‘ "is properly named."];
String Literals • Multi-line strings: • Renaming a method/field: \[ private String oldName; private String newName; \] rename[Prefix] := modifiers _ prepend[Prefix] [ method (*) throwsList "{ #[torso] }"| field "= #[torso];“ ]; • Suppressing the padding space: class “New”+ ‘?*’ • The ‘#’ character: class "ChildOf"+ ['?*‘ is T] "extends #T“ prepend[Prefix] := "#Prefix“ +"#";
Baggage Management in Quantifiers • Output members that violate the Java coding convention: badlyNamedClassMembers := %class %{[field|method] ‘[A-Z]?*’“is badly named.";%} • Pseudo quantifiers: optional_interfaces := implements: %{ first "implements"; exists _; between ","; last nothing; %} | nothing;
Using JTL in an IDE and for Refactoring • Extracting the public protocol of a class: elicit_interface := %class -- Guard modifiers "interface“ prepend["P_"] { optional %public !static method header ";" ; }; • Generating a prototype implementation from an interface: defVal := %boolean "false" | %primitive "0" | %void nothing | "null";gen_class := %interface -- Guardmodifiers "class“ prepend["C_"] "implements #" {header \[ { return #[defVal]; } \]};
Using JTL in an IDE and for Refactoring addEquals := %class header %[# is T] declares: {![boolean equals (Object)] declaration; last \[ @Override boolean equals(Object obj) { if (obj == null) return false; if (obj == this) return true; if (!obj.getClass().equals(this.getClass())) return false; #T that = (#T)obj; // downcast the parameter to the current type #[T.compareFields] // invoke helper predicate for field comparison return true; // all field comparisons succeeded } \]; compareFields := { -- generate field-comparison code %[primitive field, '?*‘ is Name] -- guard for primitive fields "if (this.#Name != that.#Name) return false;"; %[!primitive field, '?*‘ is Name] -- guard for reference fields "if (!this.#Name.equals(that.#Name)) return false;"; } } • Adding an equals method to a class:
JTL as a Lightweight AOP language • Weaving a “before” advice to all public methods: loggingAspect := %class header declares: { targetMethod := public !abstract method; -- pointcut definition %targetMethod header \[ { System.out.println("Entering method #"); #[torso] } \] | declaration; } • What about other kinds of advices? • (around, after returning, after throwing etc.) • The answer is in the next slide…
loggingAspect2 := %class header declares: { targetMethod := public !abstract method; -- pointcut definition } actualsAsString := %(first \[ "(" + \]; last \[ + ")" \]; between \[ + ", " + \];argName; -- at least one; iterate as needed%)| "()"; -- no arguments -- rename matching methods: %targetMethod rename["original_"] | declaration; %targetMethod header "{" %[ !void, _ is Return ] -- Guard for non-void methods \[ System.out.println("Entering #" + #[actualsAsString]); try { #Return result = #[prepend["original_"]] #[argNames]; } catch (Throwable e) { System.out.println("Exception: " + e); throw e; } System.out.println("Returned " + result); return result; \] • JTL AOP is limited to ‘execution’ pointcuts • Generated code is specific per method to which the advice is applied • No usage of runtime reflection • No boxing/unboxing | -- deal with void methods \[ System.out.println("Entering #Name“ + #[actualsAsString]); try { // Invoke the renamed original: #[prepend["original_"]] #[argNames]; } catch (Throwable e) { System.out.println("Exception: " + e); throw e; } \] ] "}";
Templates, Mixins and Generics • Generating a SINGLETON class: singleton := "public" class "Singleton"+ '?*', %[# is T] { %[public constructor ()] | %2 "#T has no public zero-args constructor."; last \[ private Singleton#T() { } private static #T instance = null; public static #T getInstance() { if (instance == null) instance = new #T(); return instance; } \]; } • Cannot be implemented using Java generics • Is also superior to C++ template approach • Because requirements on the parameter are expressed explicitly
Templates, Mixins and Generics • A guarded mixin-like transformation: undoMixin := "public" class [# is T] "Undoable#T extends #T" { %[!private void setName(String)] | %2 "#T has no matching setName method."; %[!private String getName()] | %2 "#T has no matching getName method."; all ![!private undo()] | %2 "Conflict with existing undo method."; last \[ private String oldName; public void undo() { setName(oldName); } public void setName(String name) { oldName = getName(); super.setName(name); } \]; }
Non-Java Output • Persistence: mapping between a class and a relational database • In most modern systems mapping is defined using annotations: CREATE TABLE OWNER (id INTEGER PRIMARY KEY,firstName VARCHAR NOT NULL,lastName VARCHAR NOT NULL); … @Table class Account { @Id @Column long id; // Primary key @Column float balance; @ForeignKey @Column(name="OWNER_ID") Person owner; } @Table(name="OWNER") class Person { @Id @Column long id; @NotNull @Column String firstName; @NotNull @Column String lastName; }
@Table(name="OWNER") class Person { @Id @Column long id; @NotNull @Column String firstName; @NotNull @Column String lastName; } CREATE TABLE OWNER (id INTEGER PRIMARY KEY,firstName VARCHAR NOT NULL,lastName VARCHAR NOT NULL); … generateDDL := %class "CREATE TABLE" tableName %{ first "("; last ")"; between ","; %[ @Column field ] => %sqlType | %2 ["Unsupported field type, field" '?*']; columnName sqlType sqlConstraints; %} sqlType := %String "VARCHAR“ | %integral "INTEGER“ | %real "FLOAT" | %boolean "ENUM('Y','N')" | %BigDecimal "DECIMAL(32,2)" | %Date "DATE" | foreignKey; columnName := [ %@Column(value=CName:STRING) "#CName" ]| [ %@Column() '?*' ]; --Default column name = field name …
JTL AOP • JTL views aspects as transformations of a software base • This perspective was presented earlier by Fradet and Sudholt • AST-based transformations: a richer set of join-points • LogicAJ: “the feature set of AspectJ can be completely mapped to a set of conditional program transformations” • JTL uses logic-based program transformation for weaving • Aspect-oriented logic meta-programming (AOLMP) • A very orderly approach, no free-form strings, easier to define and reason about aspects • JTL AOP is limited, “quick-and-dirty” AOP language • However, JTL is not limited to AOP!
Output Validation • JTL is not “type-safe” • A JTL program p may generate a non-valid program • A general validation is undecidable, however: • proving that p always generates valid programs in a specific target language is possible • in order to prove that p always generates valid code: • Find the grammar Gp • Prove that L(Gp) L(G) • In JTL, Gp = p • It is possible and practical to decide if L(G) L(Gxml) • Hence, it is possible to validate JTL-XML program • In any case, JTL’s Java output is always processed by a Java compiler ∩ ∩