a technique for constructing aspect weavers using a program transformation engine aosd 2004...
TRANSCRIPT
A Technique for Constructing
Aspect Weavers Using aProgram Transformation
Engine
AOSD 2004Lancaster, UKMarch 24, 2004
Jeff GraySuman Roychoudhury
http://www.gray-area.org
This paper: An idea from 1997
I first heard of AOP while working ona commercial Delphi project.
Cobol
Legacy
Legacy System - Challenges
Software
Fortran
Object Pascal
Corporate Backbone 200 Billion Lines of Cobol Source
Migration is not an easy task !
Infrastructure, money effort already spent
Maintenance & Tool Support
Maintaining such systems is still an onerous task
Need modern tools and methodologies to reduce maintenance cost and effort
Can we extend AOSD to support legacy software?
Language- and Platform-Independent Weavers Weaver Explosion Problem
AOSD Approach (AspectJ, HyperJ, CF, etc.) Base programming language Execution environment/platform
Main Barriers
The grammar recovery and parser construction problem…
The weaving engine problem…
Incomplete/toy parsers will not scale and will leave a negative first impression of aspects to skeptical industry adopters
Grammarware:
Paul Klint, Ralf Lämmel, and Chris Verhoef, “Towards an Engineering Discipline for Grammarware” http://www.cs.vu.nl/grammarware/agenda/
Program Transformation Systems
Program Transformation
System
Aspect WeaversRefactoring Tools
Low Level xForm constructs
High level aspect constructs
Legacy Software
Reuse of core set of transformations for each new weaver
We use a commercial PTE from Semantic Designs (the Design Maintenance System - DMS)
Outline
Background Case Study Weaver Transformation Rules Aspect Domain Related/Future work Conclusion
Background Case Study Experiment conducted on commercial software
written in Delphi Utility applications to support
database schema migration database-independent error handling language internationalization
Several aspects identified Processing dialog meter
Updating of progress Exception handling of meter
(not shown, but in paper) Logging of SQL query statements Synchronization in database error handler Dirty Bits in language internationalization utility
~45k Object Pascal
Processing Dialog Meter…Inc(TotalInserts);if not ProcDlg1.Process(TotalInserts/TotalCalc)
then begin ProcDlg1.Canceled := True; Result := True; exit; end; // if not Process…
The conditional statement is executed after each call to increment function.
Listing 1 contains a redundant code fragment that appears in 62 different places of the schema migrator.
Listing 1
Logging of SQL Query Statements…with dmSERVERS.qryCreateTriggers dobegin <statements that build a SQL Create Trigger> LogSQL.AddSQL(dmSERVERS.qryCreateTriggers,True); ExecSQL;end;…
Listing 2
The methods of the logging object are invoked in over 50 different places in the schema migrator.
Note: Object parameter passed to logging object is scoped in Object Pascal “with” statement.
Database Error Handler Synchronization
function TExNullField.Handle(ServerType: TServerType; E : EDBEngineError) : Integer;
begin TExHandleCollection(Collection).LockHandle; try <database error handling code omitted here> finally TExHandleCollection(Collection).UnLockHandle; end; end;
The addition of this concurrency concern resulted in a manual invasive change to over 20 classes.
The same code is replicated in all of the entry and exit points of each type of error handler.
Listing 3
Language Internationalization Utility // The user wants to perform another searchprocedure TLangMan.SearchAgainClick(Sender:TObject);begin // Perform an update if an edit occurred that
// might change the focus of the listview if EditMadeDirtyBit then SaveDBControls;…end;
There are 29 unique places in the language internationalization source code where access to the Boolean variable EditMadeDirtyBit is made.
Listing 4
Outline
Background Case Study Weaver Transformation Rules Aspect Domain Related/Future work Conclusion
DMS Reengineering Toolkit
© Semantic Designs, Inc.Provided with permission from:
Progress Meter Transformation Ruledefault base domain ObjectPascal.pattern advice(): if_statement = "if not ProcDlg1.Process (TotalInserts/TotalCalc) then begin ProcDlg1.Canceled := True; Result := True; exit; end;".
rule probe_progress_meter(): statement_list -> statement_list = "Inc(TotalInserts);" -> "Inc(TotalInserts); \advice\(\);".
public ruleset applyrules = { probe_progress_meter }.
The LHS / RHS of rule operates on the AST as defined by the
Delphi grammar fragments
default base domain ObjectPascal.external pattern add_log_stmt (slist1:statement_list, slist2:statement_list, id1:IDENTIFIER, id2:IDENTIFIER): statement_list = 'add_log_statement' in domain ObjectPascal.
pattern advice(id1:IDENTIFIER, id2:IDENTIFIER): statement_list = "LogSQL.AddSQL (\id1.\id2 , True);".pattern func_call_sig(): "ExecSQL".
rule probe_logging(id1:IDENTIFIER,id2:IDENTIFIER,slist:statement_list): with_statement -> with_statement = "with \id1 . \id2 do begin \slist end" -> "with \id1 . \id2 do begin \add_log_stmt\(\slist \, \advice\(\id1 \,\id2\) \, \id2 \,\func_call_sig\(\)\) end".public ruleset applyrules = { probe_logging }.
Log Transformation Rule(lambda (function boolean AST:Node )function (value (local (;; );; (;; (ifthen(== ~t (AST:ContainsString ?)) (;; (= search_string (AST:GetString ?)) (ifthen (== (@ search_string) arguments:4) (return ~t)) ifthen );; )ifthen (return ~f) );; )local ~f )value )lambda …
default base domain ObjectPascal.external pattern add_log_stmt(slist1:statement_list, slist2:statement_list, id1:IDENTIFIER, id2:IDENTIFIER): statement_list = 'add_log_statement' in domain ObjectPascal.
pattern advice(id1:IDENTIFIER, id2:IDENTIFIER): statement_list = "LogSQL.AddSQL (\id1.\id2 , True);".pattern func_call_sig(): "ExecSQL".
rule probe_logging (id1:IDENTIFIER, id2:IDENTIFIER, slist:statement_list) :
with_statement -> with_statement = "with \id1 . \id2 do begin \slist end" -> "with \id1 . \id2 do begin \add_log_stmt\(\slist \, \advice\(\id1 \,\id2\) \, \id2 \,\func_call_sig\(\)\) end".
default base domain ObjectPascal.pattern probe_handle(id:IDENTIFIER): qualified_identifier = "\id.Handle".pattern unlock():statement = "TExHandleCollection(Collection).UnLockHandle".pattern lock(): statement = "TExHandleCollection(Collection).LockHandle".pattern advice(slist:statement_list): statement_list = "\lock\(\); try \slist finally \unlock\(\); end;".rule probe_synchronize(slist:statement_list, id:IDENTIFIER, fps:formal_parameters, frt:function_result_type): implementation_decl -> implementation_decl = "function \probe_handle\(\id\) \fps : \frt ; begin \slist end;" -> "function \probe_handle\(\id\) \fps : \frt ; begin \advice\(\slist\) end;".if ~[modsList:statement_list .slist matches "\:statement_list \advice\(\modsList\)"].public ruleset applyrules = { probe_synchronize }.
Synchronization Rules
rule probe_synchronize(slist:statement_list, id:IDENTIFIER, fps:formal_parameters, frt:function_result_type):
implementation_decl -> implementation_decl = "function \probe_handle\(\id\) \fps : \frt ; begin \slist end;" -> "function \probe_handle\(\id\) \fps : \frt ; begin \advice\(\slist\) end;".
default base domain ObjectPascal.pattern probe_handle(id:IDENTIFIER): qualified_identifier = "\id.Handle".pattern unlock(): statement = "TExHandleCollection(Collection).UnLockHandle".pattern lock(): statement = "TExHandleCollection(Collection).LockHandle".pattern advice(slist:statement_list): statement_list = "\lock\(\); try \slist finally \unlock\(\); end;".
Dirty Bit Transformation Rule
default base domain ObjectPascal.external condition func_sig_has_click(id1:IDENTIFIER,id2:IDENTIFIER) = 'func_sig_has_click'.pattern advice(slist:statement_list) : statement_list = "if EditMadeDirtyBit then SaveDBControls; \slist".pattern isClick(id:IDENTIFIER): IDENTIFIER = id if func_sig_has_click(click(), id).pattern click (): IDENTIFIER = "Click".rule probe_dirty_bit (id1:IDENTIFIER, id2:IDENTIFIER,fps:formal_parameters, slist:statement_list): implementation_decl -> implementation_decl = "procedure \id1 . \isClick\(\id2\) \fps ; begin
\slist end;" -> "procedure \id1 . \id2 \fps ;
begin\advice\(\slist\)
end;".
if ~[modslist:statement_list .slist matches "\:statement_list \advice\(\modslist\)"].
public ruleset applyrules = { probe_dirty_bit }.
rule probe_dirty_bit (id1:IDENTIFIER, id2:IDENTIFIER, fps:formal_parameters, slist:statement_list): implementation_decl -> implementation_decl = "procedure \id1 . \isClick\(\id2\) \fps ; begin
\slist end;" -> "procedure \id1 . \id2 \fps ;
begin \advice\(\slist\)
end;".
external condition func_sig_has_click(id1:IDENTIFIER, id2:IDENTIFIER) = 'func_sig_has_click'.pattern advice(slist:statement_list) : statement_list = "if EditMadeDirtyBit then SaveDBControls; \slist".pattern isClick(id:IDENTIFIER): IDENTIFIER = id if func_sig_has_click(click(), id).
if ~[modslist:statement_list .slist matches "\:statement_list \advice\(\modslist\)"].
public ruleset applyrules={probe_dirty_bit}.
Video Demonstration
Dirty Bit Weaving with DMS More detailed example provided at:
http://www.gray-area.org/Research/GenAWeave
Outline
Background Case Study Weaver Transformation Rules Aspect Domain Related/Future work Conclusion
Aspect Weaver
Aspect Source Attribute
Evaluator
RSLRules
DMS Transformation
Engine
Lexer
Parser
PARLANSE f(x)
Delphi Source
Transformed Delphi Source
Pattern Instantiation
FRONT END
BACK END
// The user wants to perform another search
procedure TLangMan.SearchAgainClick(Sender: TObject);
begin
// Perform an update if an edit occurred that //might change the focus of the listview
if EditMadeDirtyBit then
SaveDBControls;
…
end;
default base domain ObjectPascal.external condition func_sig_has_click(id1:IDENTIFIER,id2:IDENTIFIER)= 'func_sig_has_click'.pattern advice(slist:statement_list) : statement_list = "if EditMadeDirtyBit then SaveDBControls; \slist".pattern isClick(id:IDENTIFIER): IDENTIFIER = id if func_sig_has_click(click(), id).pattern click (): IDENTIFIER = "Click".rule probe_dirty_bit (id1:IDENTIFIER, id2:IDENTIFIER,fps:formal_parameters, slist:statement_list): implementation_decl -> implementation_decl = "procedure \id1 . \isClick\(\id2\) \fps ; begin
\slist end;" -> "procedure \id1 . \id2 \fps ;
begin\advice\(\slist\)
end;".
if ~[modslist:statement_list .slist matches"\:statement_list \advice\(\modslist\)"].
public ruleset applyrules = { probe_dirty_bit }.
(define func_sig_has_click (lambda Registry:MatchingCondition (let (;;(= [const_string (reference string)] (AST:GetString arguments:1)) (= [search_string (reference string)] (AST:GetString arguments:2)) (= [scanner StringScan:Scan ] (StringScan:MakeScan search_string)) );; (value (while (== (StringScan:End? (. scanner)) ~f)
(ifthenelse (StringScan:MatchString? (. scanner) const_string) (return ~t)
(StringScan:Advance (. scanner)) )ifthenelse )while~f
)value )let )lambda)define
aspect ProbeDirtybit {
pointcut method_click() : execution(procedure *.*Click(..)); before() : method_click() { if EditMadeDirtyBit then SaveDBControls; } }
`Aspect Pascal Weaver - Takes Aspect source and weaves the changes to the Delphi source':
(;;
`include domains': (;; (include `ObjectPascalDomain.par') (include `AspectPascalDomain.par') );;
`include creating pattern and its registration code': (include `RegisterCreatingPattern.par')
`include ast/cst snippets needed for weaving: (;; `include appropriate SyntaxTreeSnippets module for selected configuration of infix arithmetic calculator':
(;; (define ObjectPascalASTRepresentationEliminatedTerminalNodes ObjectPascal:ASTRepresentationEliminatedTerminalNodes) ; work around compiler deficiency
(compileifthenelse ObjectPascalASTRepresentationEliminatedTerminalNodes (;; (include `%ObjectPascalASTSnippets.par')) (;; (include `%ObjectPascalCSTSnippets.par'))
)compileifthenelse );;
);;
`auxiliary function for driver': (define get_file_name `gets a file name from standard input' (lambda (function string void) (let [str string]
(value (;; (Console:Put (. `Enter file name : '))
(= str (Console:GetString)) (Console:PutNewline)
);; str )value
)let )lambda )define
`define driver for the weaver': (define main (action (procedure void) (local
(;; [file_name string] [domain_representation AST:Representation] (= [graph AST:Forest] (void AST:Forest)) [syntax_tree AST:Node] [op_syntax_tree AST:Node] );; (try (;; `get file name':
(= file_name (get_file_name)) `create working graph': (= graph (AST:CreateForest)) `parse file containing aspect pascal syntax': (try (= syntax_tree (Registry:ParseFile (. `AspectPascal') Registry:DefaultSyntaxTreeDomainRepresentation
graph (. file_name))) (case (exception)
SyntaxErrorsDetected (acknowledge (;; (Console:Put (. `Error: Syntax errors occurred during parsing
of file "')) (Console:Put (. file_name)) (Console:Put (. `".~l')) (return) ; program end takes care of memory being
freed for the created working graph );; )acknowledgeInputStream:FileNotFound (acknowledge (;; (Console:Put (. `Error: Could not find file "'))
(Console:Put (. file_name))…..
%%AspectPascal
#include "DMSLexical/Library/DMS.lex"
#macro A "[aA]"#macro B "[bB]"#macro C "[cC]"#macro D "[dD]"#macro E "[eE]"#macro F "[fF]"#macro G "[gG]"#macro H "[hH]"#macro I "[iI]"#macro J "[jJ]"#macro K "[kK]"#macro L "[lL]"#macro M "[mM]"#macro N "[nN]"#macro O "[oO]"#macro P "[pP]"#macro Q "[qQ]"#macro R "[rR]"#macro S "[sS]"#macro T "[tT]"#macro U "[uU]"#macro V "[vV]"#macro W "[wW]"#macro X "[xX]"#macro Y "[yY]"#macro Z "[zZ]"
-------------------------------------------------------------- Lexical definition for AspectPascal------------------------------------------------------------
-- comments
#macro newline " (\u000d\u000a) | [\u000a\u000c\u000d\u2028\u2029] " -- newline characters
#macro tab "[\t]"#macro blank "[\ ]"#macro whitespace "<blank>|<tab>"
#skip "(<whitespace>|<newline>)+"
#preskip "(<whitespace>|<newline>)*"
#postskip "<whitespace>*"
#macro digit "[0-9]"
-- symbols
#token '*' "\*"#token '.' "\."#token ';' "\;"#token ':' "\:"#token '(' "\("#token ')' "\)"#token '{' "\{"#token '}' "\}"
-- reserved keywords
#token 'aspect' "<A><S><P><E><C><T>"#token 'before' "<B><E><F><O><R><E>"#token 'pointcut' "<P><O><I><N><T><C><U><T>“
aspect_declaration = 'aspect' identifier '{' pointcut advice '}' ;
<<PrettyPrinter>>:{ V(H('aspect', identifier, '{'), I(pointcut),I(advice),'}') ; }
advice = before_advice ;
before_advice = 'before' '(' ')' ':' pointcut '{' statement_list '}';<<PrettyPrinter>>:{ V(H('before', '(' , ')', ':', pointcut, '{'),statement_list ,'}') ; }
statement_list = statement ';' statement_list ;<<PrettyPrinter>>:{I(H(statement , ';', statement_list[1]));}
statement_list = statement;<<PrettyPrinter>>:{H*;}
statement = string statement;<<PrettyPrinter>>:{H*;}
statement = ;
pointcut = 'call' '(' signature ')' ;<<PrettyPrinter>>:{I(H('call', '(', signature ,')'));}
pointcut = 'execution' '(' signature ')' ;<<PrettyPrinter>>:{I(H('execution', '(', signature ,')'));}
pointcut = 'pointcut' identifier '(' ')' ':' pointcut ';' ;<<PrettyPrinter>>:{V(H('pointcut', identifier, '(', ')',':') , H(pointcut[1],';'));}
pointcut = identifier '(' ')' ;<<PrettyPrinter>>:{H*;}
signature = type_pattern '(' type_pattern ')' ;<<PrettyPrinter>>:{H*;}
type_pattern = type_name_pattern type_pattern;<<PrettyPrinter>>:{H*;}
type_name_pattern = '*';<<PrettyPrinter>>:{H*;}
type_name_pattern = '.';<<PrettyPrinter>>:{H*;}
type_name_pattern = identifier ;type_pattern = ;
identifier = IDENTIFIER ;
string = IDENTIFIER;
Outline
Background Case Study Weaver Transformation Rules Aspect Domain Related/Future work Conclusion
Related Work Weaving from general transformation framework
Pascal Fradet and Mario Südholt (1998) Ralf Lämmel (1999)
Language Extension Environments JTS, JastAdd, GENOA
Program Transformation Systems ASF+SDF, TXL, Stratego
Language-Specific Aspect Weavers AspectC++, AspectS, AspectR, Apostle
Language-Independent and Domain-Specific Weavers Lafferty and Cahill (2003), Constantinides et al. (2002), Shonle et
al. (2003) Our own past work
Two-level weaving to support model-driven program transformation (http://www.gray-area.org/Research/C-SAW)
Future long term goal – Towards language independence Grammar Wrappers
Abstract Grammar
Rules
Abstract Grammar
Extract Commonalities
C#
Java
OP
Cobol
C
Fortran
PTE
Weave Changes Refactor source
Common classof languages
Conclusion It is inevitable that AOSD will find its way into legacy languages;
similar transition for OO into Ada95, OO-COBOL, FORTRAN 2000
This paper describes a very modest first effort to construct weavers for legacy languages using a program transformation system
The goal is to avoid: duplication of effort for each new weaver by building on
generalized/parameterized weaving primitives offer an example in support of the goals of Grammarware
Drawbacks of current effort Strictly syntax-based approach Deep initial investment to understand DMS Based on a commercial tool that is not open-source
Questions?
Summary of URLs referenced in talk
GenAWeave http://www.gray-area.org/Research/GenAWeave
C-SAW (two-level model/program weaving) http://www.gray-area.org/Research/C-SAW
Grammarware http://ww.cs.vu.nl/grammarware/agenda
DMS http://www.semdesigns.com
Shameless Plug: HICSS AESS Aspect-friendly mini-track in Hawaii http://www.cis.uab.edu/HICSS-AESS/