210 likes | 349 Vues
This paper introduces a side-effect free technique for program transformation using the Java Transformation Language (JTL). It discusses how logic paradigms can effectively query code and manages baggage—a string variable—to specify matches. Simple rules calculate baggage for predicates, allowing for straightforward transformations within Java code. The authors provide examples demonstrating the utility of JTL for extracting interfaces, generating implementations, and weaving aspect-oriented programming advice into code, promoting best practices in software development.
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 ∩ ∩