200 likes | 222 Views
The nitrO Reflective Platform. International Conference on Software Engineering Research and Practice (SERP) Session on Adaptable Software Architectures (ASA). Adaptability.
E N D
The nitrO Reflective Platform International Conference on Software Engineering Research and Practice (SERP) Session on Adaptable Software Architectures (ASA)
Adaptability • Adaptable software systems and architectures give the programmer the ability to create applications that might be customized to runtime-emerging requirements (unpredictable at design time) • An adaptable platform should give its applications the opportunity to customize their structure and behavior at runtime
Reflection • Reflection is a programming language technique that achieves dynamic application adaptability • Taking what system’s feature can be customized as a classification criterion, we can distinguish: • Introspection: Application’s structure might be inspected but not modified (e.g. Java reflect package used in the JavaBeans architecture) • Structural Reflection: Application’s structure might be inspected as well as modified (e.g. Smalltalk and Python ability to modify objects’ structure at runtime) • Computational Reflection: The whole system semantics can be adapted (e.g. modifying the message passing mechanism to create a system’s logger)
Attaching objects to the meta-object (attachObject) implies behavior adaptation Trace class that overrides the message- passing mechanism Trace Meta-Object Meta-Object Protocol Semantics Overriding MetaObject attachObject(Object) eventMethodEnter() System class used to modify the applications semantics Meta-Object Protocols • Most runtime computational-reflective systems are based on Meta-Object Protocols (MOP) • A MOP specifies the way an application (base level) may access its object-model implementation (meta-level) in order to adapt its behavior and structure at runtime User’s Application User Objects Base Level MetaXa Example Runs Meta-Level Interpreter
MOP Restrictions • The way a MOP is defined restricts the way amount of features that can be customized: if the is not a “create object” meta-object, we will not be able to adapt this system behavior • Enhancing the MOP implies different interpreter and language versions Previous code could result deprecated • System semantics customization is expressed by method overriding: a richer mechanism to express the adaptation would be needed • Meta-level and base-level programming language are the same: the do not offer runtime adaptability in a language-independent way
Theoretical Concept • The theoretical concept of reflection uses the notion of a reflective tower of interpreters: • An interpreter (hardware or software) runs an application • A reflective computation is a computation about its computation: a computation that accesses the interpreter • If the application would be able to access its interpreter at runtime, it would • Inspect its running objects Introspection • Modify its own structure Structural Reflection • Customize the language semantics Computational Reflection
System Architecture • We have developed a Generic Interpreter that: • Is capable of interpreting any programming language by previously reading its specification • Separates every language-specific representation from the interpreter implementation (grammar and semantics) • Separates every application structure from the interpreter implementation (dynamic application’s symbol-table) • Gives the programmer the ability to access every: • Application Structure • Programming Language Specification
SampleLang.ML 6) If the application reads its symbol-table, introspection is achieved ... 7) If the application modifies its symbol-table, structural reflection is achieved 2) The language specification file is found in the system 3) The language specification is translated into a objects structure 1) A user program is about to be run 8) If the application modifies the language semantics, computational reflection is achieved 4) The user’s application starts running 5) While the application is running, a symbol-table is used by the interpreter Application’s Symbol-Table Language-Specification Objects Structure System Architecture User’s Application Application = “Sample App” Language = “Sample Lang” Class MyClass { // * App code ... } Generic Interpreter
Computational Jump • How can an application access to its interpreter computational environment? • We have selected the Python programming language to implement the nitrO platform because of its: • Introspection capabilities: At runtime, the every application may inspect its variables, objects, attributes, methods and modules • Structural reflection capabilities: The previously-mentioned features an application has might be dynamically modified • Dynamic evaluation of code represented as strings: The execfunction can dynamically evaluate code created at runtime
The reify Statement • Not needing to specify it, every language has the reify statement • Inside a reify statement, Python code can be placed • The generic interpreter recognizes every reify statement regardless the programming language being analyzed • The generic interpreter will not process a reify statement as the rest of the code; it will: • The Python code inside the reifystatement is taken as a string • This code is passed as an argument to the exec function The Python code is evaluated at the interpreter computational environment!
code=“...” language["assignment"]. actions.append(code) 3) Computational Reflection 1) Introspection 2) Structural Reflection write(str(vars)) vars["a"]=2 Non-Restrictive Reflection • Using Python introspection and structural reflection, application’s symbol-tables and language’s specification can be inspected/adapted User’s Application a=10*2; b=a/-18; Reify <# write(str(vars)) vars["a"]=2 #> a; b; Reify <# code=“...” language["assignment"]. actions.append(code) #> a=10*2; Specific language Python becomes a 1.- runtime 2.- non-restrictive 3.- language-neutral4.- three-levels-reflectivemeta-language Python language Application Computational Environment Interpreter Computational Environment Application’s Symbol-Table Generic Interpreter Language-Specification
Language Specification • Language-Specification files describe: • Scanner and parser rules with context-free grammar rules • Semantic specifications by means of Python code MetaPython.ML describes a subset of the Python programming language: Language = MetaPython Scanner = { "Digit Token"digit -> "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9" ; "Integer Token" INTEGER -> digitmoreDigits ; "Float Token" FLOAT -> INTEGER "." INTEGER ; "Zero or more digits token" moreDigits -> digitmoreDigits | ; "Character Token" char -> "a"|"b"|"c"|"d"|"e"|"f"|"g"|"h"|"i"|"j"| "k"|"l"|"m"|"n"|"ñ"|"o"|"p"|"q"|"r"|"s"|"t"|"u"|"v"|"w"|"x"|"y"|"z"| "á"|"é"|"í"|"ó"|"ú"| "A"|"B"|"C"|"D"|"E"|"F"|"G"|"H"|"I"|"J"|"K"|"L"|"M"|"N"|"Ñ"|"O"|"P"| "Q"|"R"|"S"|"T"|"U"|"V"|"W"|"X"|"Y"|"Z"|"Á"|"É"|"Í"|"Ó"|"Ú"|"_” ; ...
Syntactic and Semantic Spec Parser = { "Initial Free-Context Rule" S -> statement moreStatements <# # Initial Code Needed in the Application Execution global classes,functions classes={} # Classes Symbol Table functions={} # Function Symbol Table nodes[1].execute() nodes[2].execute() #> ; "Zero or more statements" moreStatements -> statement moreStatements <# nodes[1].execute() nodes[2].execute() #> | ; "Python Statement" statement -> class <# nodes[1].execute() # Inserts the class into the ST #> | function <# nodes[1].execute() # Inserts the class into the ST #> | if | while | import | assignment | functionCall ; "Class Spec." class -> CLASS ID OPENCURLYBRACE classBody CLOSECURLYBRACE ... Skip = { "\t"; "\n"; " "; } NotSkip = { } Semantic actions Syntactic rules (semantic rules derivatively suppressed) Tokens automatically skipped Tokens automatically appended
Sample Application Application = "Sample App" Language = "MetaPython" import string; import random; class MyClass { def init(self) { self.calls=0; self.nestingLevel=1; } def call(self) { self.calls=self.calls+1; } def nestedCall(self) { self.calls=self.calls+1; temp=random.random()-(1.0-self.nestingLevel/10.0); if temp<0.5 { self.nestingLevel=self.nestingLevel+1; self.nestedCall(); self.nestingLevel=self.nestingLevel-1; } } } object=MyClass(); object.init(); while 1 { object.call(); object.nestedCall(); } Application and Language Description
Application Execution • The platform offers a nitrO object, representing the system applications and languages (Facade) • When the application execution takes place in the nitrO platform: • An application object is created in the nitrO.apps dictionary (with the key “Sample App”) • The MetaPython.ml language specification-file is located • The language specification is translated into a object structure; this is saved into the language application-object attribute • At runtime, the application symbol-table is located on its applicationGlobalContext attribute language Language Spec apps nitrO.apps[“Sample App”] nitrO applicationGlobalContext Symbol Table
Application Adaptation • Every application executed in the nitrO platform may access the nitrO “Facade” object and dynamically adapt: • An application symbol-table Introspection and Structural Reflection • A language specification Computational Reflection • without any MOP-restrictive protocol! • in a language-independent way!
Dynamic Application Weaving # Application time-monitoring weaver nitrO.executeFile(nitrO.dir+”time/timeAspect.na”) write(nitrO.describe()) nitrO.apps["Time Aspect"].weave("Sample App",nitrO) Application = "Time Aspect" Language = <# Language = JustAReifyStatement Scanner = {} Parser = { "Initial Free-Context Rule" S -> _REIFY_ <# nodes[1].execute() #> ; } Skip={ "\t"; "\n"; " "; } NotSkip = { } #> reify <# def weave(app,nitrO): # Function that weaves an application if nitrO.apps.has_key(app): # This is just a MetaPython aspect if nitrO.apps[app].language.name=="MetaPython": codeBefore="""... """ codeAfter="""... """ actions=nitrO.apps[app].language.syntacticSpec["functionCall"].options[1].actions # Had the aspect been weaved? hadBeenWeaved=... if not(hadBeenWeaved): # Inserts the monitoring code actions.insert(0,SemanticAction(codeBefore)) actions.append(SemanticAction(codeAfter)) else: # Removes the monitoring code length=len(actions) for i in range(length): action=actions[length-i-1] if action.action==codeBefore or action.action==codeAfter: actions.remove(action) nitrO.shell.write("Application unweaved.") ... nitrO.apps["Time Aspect"].weave=weave # Creates an application aspect-weaver #> Language Specification inside the application file (just a reify statement) Time-execution method-call monitoring Removes the monitoring routines
Platform Benefits • No MOP-restrictions: the real computational jump overcomes the MOP-based adaptation limitations • Language independence: • The generic interpreter can interpreter any previously-specified language • The reify statement is part of every language • What can be adapted: introspection, structural reflection and computational reflection • Application interoperability: any application may access and reflectively modify another program being executed –whatever its language would be • Expressiveness improvement: the way behavior is customized is not restricted to a framework that relies in method overriding; a complete meta-language (Python) might be used to adapt any other language’s feature
Runtime Performance • Runtime performance penalties are caused by the interpretation of every programming language • However, many interpreted languages (Java, C# or even Python) are commercially employed due to optimization techniques such as JIT compilation • As we always translate any language into Python code, a way of speeding up the execution of our platform’s applications in the following versions is using the interface of a Python-JIT compiler implementation Platform download at: www.di.uniovi.es/reflection/lab/prototypes.html#nrrs
The nitrO Reflective Platform International Conference on Software Engineering Research and Practice (SERP) Session on Adaptable Software Architectures (ASA)