Aztec® Programming Language
Version 1.1 Alpha 2

Copyright © 2010-2017, Aztec Development Group, All Rights Reserved

Download Aztec

Search        Contact Us

Don't surround yourself with yourself.

- Jon Anderson and Chris Squire

 

The Aztec Engine provides three separate ways to dynamically compile Aztec source code while the Virtual Machine is running a script. All of these methods are contained in the Script class.

♦ The Interpret family of methods compiles and immediately executes a single expression or statement within the context of the method (or module) where it is used.

♦ The dynamic source code for the Interpret methods can access all variables and methods that are valid where the Interpret call is made, as if the dynamic code were part of the original compilation.

♦ The Interpret() method dynamically compiles and executes one or more Aztec statements.

♦ The other Interpret methods (InterpretInt(), InterpretString(), InterpretRef(), etc.) dynamically compile code for a single expression, execute the code and return the value of the expression.

♦ The CompileMethod family of methods compiles new source code for an existing method and integrates the new code into the Virtual Machine run-time environment.

♦ The method signature/interface (arguments and return type) does not change, so existing code that calls the method does not change.

♦ Once the new code is integrated into the VM, all future calls to the method will automatically execute the new code.

♦ The LoadModule methods compile source code for a completely new module. The module can contain new data, methods and classes. The new code is integrated into the Virtual Machine run-time environment.

♦ Satellite classes can be used in the new module to add new instance methods to an existing class, as well as shared data and methods.

♦ A new instance method can dynamically change the virtual method hierarchy table, which will change the behavior of existing code.

The Interpret methods are useful, and some other scripting languages offer a similar capability to dynamically compile and execute a statement or expression. The Aztec Interpret methods are flexible in that they allow the new source code to reference all variables, methods, classes, etc. that are valid within the context of where the Interpret method call is made.

The CompileMethod and LoadModule methods are unique in the potential that they provide for self modifying code. The integration of the new code into the virtual machine and how it can dynamically affect class hierarchies and virtual method hierarchies is very powerful and offers a tremendous amount of flexibility when designing and writing code that supports run-time modification.

Example Script for Self Modifying Code

A simple script is shown below to demonstrate the ease of dynamically generating code at run-time. The script uses the LoadModule, CompileMethod and Interpret methods of the Script class to modify the class hierarchy for ClassB, create a new ClassC, and then execute dynamic code that accesses the new class.

The diagram below shows the class hierarchy for ClassB after the initial compilation is performed, and prior to running the script in the Virtual Machine. ClassB is shown in gray because it is empty at first creation. The Script.LoadModule() call at run-time creates the virtual PrintName() method for class B using the Aztec satellite class feature and then creates a new class named ClassC, which is also derived from B. The class hierarchy on the right reflects the metadata state after the Script.LoadModule() call.

Class Hierarchies for Self Modifying Code Script

 

The source code that is passed to the LoadModule call to create the ClassC class and the ClassB.PrintName() method is very simple. In this sample script, the dynamic code comes from a string constant, but obviously this code can be generated at run-time to satisfy the needs of the script. The code is entered more compactly in the string than typical original source that is also written to be human readable.

"public class ClassC from<ClassB> " + string.LF +

"{ public method virtual PrintName() { StdIO.Write('ClassC') } }" + string.LF +

"public class satellite ClassB " + string.LF +

"{ public method virtual PrintName() { StdIO.Write('ClassB') } }"

The next diagram shows the standard output generated from running the script. It has numbers near the output lines that correspond to the bullets below.

Self Modifying Code Output

 

1) Shows the result of calling ClassA.PrintName() and ClassB.PrintName() before modifying the code at run-time using Script.LoadModule(). The method is virtual, and ClassB did not define one in the initial compile session, so the call to ClassB.PrintName() ends up executing the ClassA implementation of the method.

2) Shows results that reflect the state of the class hierarchy after the Script.LoadModule() call, as shown on the right side of the diagram above. Both of the "ClassC" output lines were created from dynamically compiled methods with new code that accesses the new ClassC class. The two are fairly similar, though the second method also takes a Base variable. The Base variable (MyBase) is set by dynamically compiling and executing code using the "InterpretRef" method that returns a reference to a new ClassC object, based on the dynamic code (new<ClassC>). The dynamic code for the first re-compiled method is shown below.

"data<ClassC> MyClassC = new<ClassC> " + string.LF + "MyClassC.PrintName()"

3) Shows the results of calling ClassA.PrintName() and ClassB.PrintName() again. The ClassB call now executes its own implementation of the PrintName method, which did not exist when it was called at the start of program. The virtual method hierarchy table in the Virtual Machine was dynamically updated to reflect the new method. The script's metadata is also updated accordingly.

The Aztec source code for this self modifying code script is shown below.

#===============================================================================================
# Example Script: SelfModifyingCode
# Demonstrates the use of dynamic code generation with Interpret, CompileMethod and LoadModule
# methods in the Script class.
#===============================================================================================

# Method metadata for the dynamic methods that will get set within the method.
data<Method> DynamicMethodMetadata
data<Method> DynamicMethodMetadataPost

# Main method which is the entry point for the script.
method Main()
{
data<Base> MyBase
data<ClassA> MyClassA = new<ClassA>
data<ClassB> MyClassB = new<ClassB>

# Must call the dynamic methods to set the global method metadata references.
DynamicMethod()
DynamicMethodPost()

# Call the virtual methods for ClassA and ClassB.
MyClassA.PrintName()
MyClassB.PrintName() # Will write out 'ClassA' since virtual method not overridden.

#----------------------------------------------------------------------
# Dynamically compile a new module (in a string) that contains ClassC
# and adds a new virtual method to ClassB.
#----------------------------------------------------------------------
GetScript().LoadModule(NewModuleCode)

# Compile new method code that references the new ClassC and then execute it.
GetScript().CompileMethod(DynamicMethodMetadata,NewMethodCode)
DynamicMethod()

# We can access the dynamically created ClassC directly using Interpret. Dynamically compiles and executes the code to create a ClassC object and return its reference.
MyBase = GetScript().InterpretRef("new<ClassC>")

# Compile new method code that references the new ClassC via a dynamically set "MyBase" and then execute it.
GetScript().CompileMethod(DynamicMethodMetadataPost,NewMethodCodePost)
DynamicMethodPost(MyBase)

# Finally, call the virtual methods for ClassA and ClassB again.
MyClassA.PrintName()
MyClassB.PrintName() # Will now write out 'ClassB' since virtual method has been added.
}

#--------------------------------------------------------------------------------------------
# Code to be compiled at run-time for demonstration purposes. NewModule adds a new ClassC
# and also adds a method to ClassB. NewMethod creates an object of type ClassC and uses it.
#--------------------------------------------------------------------------------------------
data<string> NewModuleCode = "public class ClassC from<ClassB> " + string.LF +
"{ public method virtual PrintName() { StdIO.Write('ClassC') } }" + string.LF +
"public class satellite ClassB " + string.LF +
"{ public method virtual PrintName() { StdIO.Write('ClassB') } }"

data<string> NewMethodCode = "data<ClassC> MyClassC = new<ClassC> " + string.LF +
"MyClassC.PrintName()"

data<string> NewMethodCodePost = "if ( (MyBase != null) & (MyBase is type<ClassC>) )" + string.LF +
"{ data<ClassC> MyClassC = MyBase as type<ClassC> " + string.LF +
"MyClassC.PrintName() }"

#------------------------------------------------------------------------
# Dynamic methods to be modified at run-time to access new ClassC. The
# initial code is also critical since it sets method metadata for later
# use with dynamic code generation.
#------------------------------------------------------------------------
method dynamic DynamicMethod()
{
DynamicMethodMetadata = new<Method>
}

method dynamic DynamicMethodPost(Base MyBase = null)
{
DynamicMethodMetadataPost = new<Method>
}

# ClassA - Contains virtual method to be overridden in ClassB at run-time.
class ClassA
{
method virtual PrintName()
{
StdIO.Write('ClassA')
}
}

# ClassB derived from ClassA - Defined as empty at initial compile session. Must be marked as dynamic.
public class dynamic ClassB from<ClassA>
{
}

 

Page UpPage DownCopyright © 2010-2017
Aztec Development Group
All Rights Reserved

Download Aztec