The following code will be used to illustrate this feature:
class Shape {
public function foo() {
print("Base foo");
}
public function paint():void {
foo();
}
}
class Rectangle extends Shape{
public override function paint():void {
super.paint();
print( "Rectangle");
}
}
class Circle extends Shape{
public override function paint():void {
super.paint();
print( "Circle");
}
}
var shapes = [new Rectangle(),new Circle()];
for each(var s:Shape in shapes) {
s.paint();
}
Compiling this program using the Flex SDK and running it using the Tamarin binaries shows the following output:
$ java -jar asc.jar -import builtin.abc Shapes.as
Shapes.abc, 678 bytes written
$ avmshell Shapes.abc
Base foo
Rectangle
Base foo
Circle
Say that we want to create a definition of the
foo
method in the Rectangle
class that overrides the definition from Shape
.1. First we load the class file:
let abcFile = using (new FileStream(sourceFile,FileMode.Open)) (
fun stream -> AvmAbcFile.Create(stream))
2. Then we need a definition for the new
foo
implementation. The following function creates a foo
override that prints a message to the screen:
let newFooMethod(message:string) =
AvmMemberMethod(
CQualifiedName(Ns("",NamespaceKind.PackageNamespace),"foo"),
AvmMethod(
"",
SQualifiedName("*"),
[||],
Some <|
AvmMethodBody(
2,1,4,5,
[|
GetLocal0;
PushScope;
FindPropertyStrict(
MQualifiedName(
[|Ns("",
NamespaceKind.PackageNamespace)|],
"print"));
PushString message;
CallProperty(
MQualifiedName(
[|Ns("",
NamespaceKind.PackageNamespace)|],
"print"),
1);
Pop;
ReturnVoid
|],[||],[||]
)
),
AbcTraitAttribute.Override
)
3. We need a function to add a method to a existing class. Notice that the modification consists in only creating a new instance of the AvmClass with the same values as the original but adding the new method.
let addClassMethod(aClass,newMethod) =
match aClass with
| AvmClass(name,
superclassname,
init,
cinit,
slots,
methods,
pns) ->
AvmClass(name,
superclassname,
init,cinit,
slots,
newMethod::methods,
pns)
4. The following method is used to locate the rectangle class and apply the modification:
let modifyFileToAddMethod(f:AvmAbcFile) =
AvmAbcFile(f.Scripts,
f.Classes |>
List.map (fun (c:AvmClass) ->
match c.Name with
| CQualifiedName(_,"Rectangle") ->
addClassMethod(
c,
newFooMethod("New foo for Rectange!!!"))
| _ -> c))
5. Finally we write the input file back to disk:
let modifiedAbcFile = modifyFileToAddMethod(abcFile)
let abcFileCreator = AbcFileCreator()
let file = modifiedAbcFile.ToLowerIr(abcFileCreator)
using (new BinaryWriter(new FileStream(targetFileName,FileMode.Create)))
(fun f -> file.WriteTo(f))
After running this program we can execute the bytecode again to get the new results:
$ mono modify.exe Shapes.abc
...
$ avmshell Shapes_t.abc
New foo for Rectange!!!
Rectangle
Base foo
Circle
One area that really needs works is name handling. A particular challenge is to find a good way to represent multinames ( name references in a set of name spaces ).
In general the way of defining a method from scratch(for example in
newFooMethod
) needs some work since it is requires lots of details that might not be interesting for the developer.Finally, another area that really needs improvement is the output file generation. Right now it requires the user to write three instructions to write the file to disc. This will be changed to be similar to the load process.
Code for this program can be found as part of the AbcExplorationLib samples.