AspeCt-oriented C Examples
| Index |
Description |
| [1] |
HelloWorld program |
| [2] |
around() advice and proceed() |
| [3] |
Composite pointcut |
| [4] |
Context exposure by args() and result() |
| [5] |
Modify function argument value by around(), proceed(), and args() |
| [6] |
Named pointcut |
| [7] |
Wildcard character matching |
| [8] |
Dynamic crosscutting with cflow() |
| [9] |
Reflective information at join points with this variable |
| [10] |
Multiple around() advice execution with proceed() |
| [11] |
Static crosscutting with intype() and introduce() |
| [12] |
Capture function calls through function pointer deferencing with callp() pointcut |
| [13] |
Expose argument value in cflow() |
| [14] |
preturn() in advice, using acc and tacc |
| [15] |
Exception handling by try() pointcut, catch() advice, and throw() statement |
| [16] |
Variable set() pointcut |
| [17] |
Variable get() pointcut |
| [18] |
A generic tracing aspect which outputs parameter type and values, and return type |
| Tutorial |
ACC Tutorial with slight bias on systems development. |
| [Flash Demo 1] |
Build and test ACC |
| [Flash Demo 2] |
Debug a program compiled by ACC |
Example 1: HelloWorld program
1. Assuming the normal C file, "world.mc", is:
int main() {
printf("world");
}
2. Assuming the aspect file, "hello.acc", is:
before(): execution(int main()) {
printf("Hello ");
}
after(): execution(int main()) {
printf(" from ACC ! \n");
}
3. Compile both files with
acc, the AspeCt-oriented C compiler:
>acc hello.acc world.mc <--- compilation will generate the hello.c and world.c files
>
4. Compile the generated files with gcc and run the executable file:
>gcc hello.c world.c
>./a.out
Hello world from ACC !
[Top]
Example 2: around() advice + proceed()
1. Assuming the normal C file, "foo.mc", is:
void foo(char * a) {
printf("inside foo, a = %s\n", a);
}
int main() {
foo("abcde");
}
2. Assuming the aspect file, "fooac.acc", is:
void around(): call(void foo(char *)) {
printf("in around advice: start\n");
proceed();
printf("in around advice: end\n");
}
3. Compile both files with the ACC compiler:
>acc foo.mc fooac.acc <--- compilation will generate foo.c and fooac.c
>
4. Compile the generated files by gcc and run the executable file:
>gcc foo.c fooac.c
>./a.out
in around advice: start
inside foo, a = abcde
in around advice: end
[Top]
Example 3: Composite pointcut
1. Assuming the normal C file, "foo.mc", is:
void foo(char * a) {
printf("inside foo, a = %s\n", a);
}
void foo2() {
printf("in foo2, call foo\n");
foo("ABCDE");
}
int main() {
foo("abcde");
foo2();
return 0;
}
2. Assuming the aspect file, "fooac.acc", is:
before(): call(void foo(char *)) && infunc(main) {
printf("aspect 1: call foo in main \n");
}
before(): call(void foo(char *)) && infunc(foo2) {
printf("aspect 2: call foo in foo2\n");
}
3. Compile both files with the ACC compiler:
>acc foo.mc fooac.acc <--- compilation will generate foo.c and fooac.c
>
4. Compile the generated files with gcc and run the executable file:
>gcc foo.c fooac.c
>./a.out
aspect 1: call foo in main
inside foo, a = abcde
in foo2, call foo
aspect 2: call foo in foo2
inside foo, a = ABCDE
[Top]
Example 4: Context exposure with args() or result()
1. Assuming the normal C file, "foo.mc", is:
int foo(char * a) {
printf("inside foo, a = %s\n", a);
return 99;
}
int main() {
foo("abcde");
}
2. Assuming the aspect file, "fooac.acc", is:
after(int value, char * argu): call(int foo(char *)) && args(argu) && result(value) {
printf("aspect: after call foo, param = %s , return value = %d\n", argu, value);
}
3. Compile both files with the ACC compiler:
>acc foo.mc fooac.acc <--- compilation will generate foo.c and fooac.c
>
4. Compile the generated files with gcc and run the executable file:
>gcc foo.c fooac.c
>./a.out
inside foo, a = abcde
aspect: after call foo, param = abcde , return value = 99
[Top]
Example 5: Modify function argument value by around(), proceed(), and args()
1. Assuming the normal C file, "foo.mc", is:
void foo(int a, int b, int c) {
printf("inside foo, argu = %d, %d, %d\n", a, b, c);
}
int main() {
int n = 3;
printf("call foo in main, argu = %d,%d,%d\n", 1, n, 5);
foo(1,n,5);
}
2. Assuming the aspect file, "fooac.acc", is:
void around(int * i): call(void foo(int,int,int)) && args(int,*i, int) { //use *i to pick the address of an argument
printf("in around advice, 2nd argument = %d.\n", *i);
printf("double its value in advice\n");
*i = (*i) * 2; //change argument value
proceed();
}
3. Compile both files with the ACC compiler:
>acc foo.mc fooac.acc <--- compilation will generate foo.c and fooac.c
>
4. Compile the generated files with gcc and run the executable file:
>gcc foo.c fooac.c
>./a.out
call foo in main, argu = 1,3,5
in around advice, 2nd argument = 3
double its value in advice
inside foo, argu = 1, 6, 5
[Top]
Example 6: Named pointcut
1. Assuming the normal C file, "foo.mc", is:
int foo(char * a) {
printf("inside foo, a = %s\n", a);
return 99;
}
void foo2() {
foo("call foo in foo2\n");
}
int main() {
foo("call foo in main");
foo2();
}
2. Assuming the aspect file, "fooac.acc", is:
pointcut callfoo(): call(int foo(char *)) ; <-- declare a named pointcut
pointcut callfooinfoo2(): callfoo() && infunc(foo2); <-- declare a new named pointcut
pointcut callfooinmain(): callfoo() && infunc(main); <-- declare a new named pointcut
before() : callfooinfoo2() { <-- advice 1: match a "foo" call inside function foo2
printf("aspect: before call foo in foo2\n");
}
before() : callfooinmain() { <-- advice 2: match a "foo" call inside function main
printf("aspect: before call foo in main\n");
}
3. Compile both files with the ACC compiler:
>acc foo.mc fooac.acc <--- compilation will generate foo.c and fooac.c
>
4. Compile the generated files with gcc and run the executable file:
>gcc foo.c fooac.c
>./a.out
aspect: before call foo in main
inside foo, a = call foo in main
aspect: before call foo in foo2
inside foo, a = call foo in foo2
[Top]
Example 7: Wildcard character matching
1. Assuming the normal C file, "foo.mc", is:
int foo1(char * a, char * b, int c) {
printf("inside foo1, b = %s\n", b);
return 99;
}
int foo2(int a, int b, int c, char * d, char e) {
printf("inside foo2, d = %s\n", d);
}
int main() {
foo1("abc", "call foo1", 3);
foo2(1,2,3, "call foo2", 'c');
foo1("abc", 0, 4);
foo2(1,2,3, 0, 'd');
}
2. Assuming the aspect file, "fooac.acc", is:
int around(char * x) : call($ foo$(..., char *, $)) && args(...,x,$) { <-- advice : use "x" to expose the second last parameter
printf("aspect: call function\n");
if(x != 0) {
proceed();
}else {
printf("aspect, the second last parameter is 0, skip the call\n");
}
}
3. Compile both files with the ACC compiler:
>acc foo.mc fooac.acc <--- compilation will generate foo.c and fooac.c
>
4. Compile the generated files with gcc and run the executable file:
>gcc foo.c fooac.c
>./a.out
aspect: call function
inside foo1, b = call foo1
aspect: call function
inside foo2, d = call foo2
aspect: call function
aspect, the second last parameter is 0, skip the call
aspect: call function
aspect, the second last parameter is 0, skip the call
[Top]
Example 8: Dynamic crosscutting with cflow()
1. Assuming the normal C file, "foo.mc", is:
void foo1() {
printf("inside foo1\n");
}
void foo2() {
printf("inside foo2\n");
foo1();
}
void foo3() {
printf("inside foo3\n");
foo2();
}
int main() {
printf("call foo1 in main\n");
foo1();
printf("-----------------\n\n");
printf("call foo2 in main\n");
foo2();
printf("-----------------\n\n");
printf("call foo3 in main\n");
foo3();
return 0;
}
2. Assuming the aspect file, "fooac.acc", is:
before(): call(void foo1()) && cflow(execution(void foo3())) { <-- advice : capture call foo1() in the control flow of execution of foo3()
printf("aspect: call foo1 in cflow of foo3 \n");
}
3. Compile both files with the ACC compiler:
>acc foo.mc fooac.acc <--- compilation will generate foo.c and fooac.c
>
4. Compile the generated files with gcc and run the executable file:
>gcc foo.c fooac.c
>./a.out
call foo1 in main
inside foo1
call foo2 in main
inside foo2
inside foo1
call foo3 in main
inside foo3
inside foo2
aspect: call foo1 in cflow of foo3
inside foo1
[Top]
Example 9: Reflective information at join points with "this" variable
1. Assuming the normal C file, "foo.mc", is the same as the one in Example 8.
2. Assuming the aspect file, "fooac.acc", is:
before(): call(void $()) && cflow(execution(void foo3())) { <-- advice : capture all function calls in the control flow of the execution of foo3()
printf("aspect: call %s in cflow of foo3. \n", this->funcName);
}
3. Compile both files with the ACC compiler:
>acc foo.mc fooac.acc <--- compilation will generate foo.c and fooac.c
>
4. Compile the generated files with gcc and run the executable file:
>gcc foo.c fooac.c
>./a.out
call foo1 in main
inside foo1
call foo2 in main
inside foo2
inside foo1
call foo3 in main
inside foo3
aspect: call foo2 in cflow of foo3.
inside foo2
aspect: call foo1 in cflow of foo3.
inside foo1
[Top]
Example 10: Multiple around() advice execution with proceed()
1. Assuming the normal C file, "foo.mc", is:
void foo1() {
printf("inside foo1\n");
}
int main() {
printf("call foo1 in main\n");
foo1();
return 0;
}
2. Assuming the first aspect file, "fooac1.acc", is:
void around(): call(void foo1()) { <-- advice : print a message before call foo1()
printf("aspect 1 \n");
proceed();
}
Assuming the second aspect file, "fooac2.acc", is:
void around(): call(void foo1()) { <-- advice : print a message before call foo1()
printf("aspect 2 \n");
proceed();
}
Assuming the third aspect file, "fooac3.acc", is:
void around(): call(void foo1()) { <-- advice : print a message before call foo1()
printf("aspect 3 \n");
proceed();
}
3. Compile 4 files with the ACC compiler:
>acc foo.mc fooac1.acc fooac2.acc fooac3.acc <--- compilation will generate foo.c, fooac1.c fooac2.c and fooac3.c
>
4. Compile the generated files with gcc and run the executable file:
>gcc foo.c fooac1.c fooac2.c fooac3.c
>./a.out
call foo1 in main
aspect 1
aspect 2
aspect 3
inside foo1
[Top]
Example 11: Static crosscutting with intype() and introduce()
1. Assuming the normal C file, "foo.mc", is:
struct X {
char a;
int b;
};
typedef struct X MYX1;
typedef MYX1 MYX2;
int main() {
printf("size of struct X = %d\n", sizeof(struct X));
return 0;
}
2. Assuming the aspect file, "fooac.acc", is:
introduce(): intype(struct X) { <-- advice : add a member
char * c;
}
introduce(): intype(MYX1) { <-- advice : add another member
char * d;
}
introduce(): intype(MYX2) { <-- advice : add another member
char * e;
}
3. Compile 2 files with the ACC compiler:
>acc foo.mc fooac.acc
>
4. Compile the generated files with gcc and run the executable file:
>gcc foo.c fooac.c
>./a.out
size of struct X = 20 <-- struct X has 5 members.
[Top]
Example 12: callp() pointcut: capture function call through function pointer deferencing
1. Assuming the normal C file, "foo.mc", is:
void foo1(char * a) {
printf("inside foo1, a = %s\n", a);
}
void foo2(char * a) {
printf("inside foo2, a = %s\n", a);
}
int foo(char * a, void (*p)(char *)) {
printf("inside foo \n");
(*p)(a);
return 0;
};
int main() {
foo("call foo1", foo1);
foo("call foo2", foo2);
return 0;
}
2. Assuming the aspect file, "fooac.acc", is:
before(): callp(void foo1(char *)) { <-- advice : print a message before call foo1() through deferencing a pointer
printf("aspect: call foo1 through a function pointer\n");
}
3. Compile 2 files with the ACC compiler:
>acc foo.mc fooac.acc
>
4. Compile the generated files with gcc and run the executable file:
>gcc foo.c fooac.c
>./a.out
inside foo
aspect: call foo1 through a function pointer
inside foo1, a = call foo1
inside foo
inside foo2, a = call foo2
[Top]
Example 13: Expose argument value in cflow()
1. Assuming the normal C file, "foo.mc", is:
void foo1(char * a) {
printf("inside foo1, a = %s\n", a);
}
void foo2(char * a) {
printf("inside foo2, a = %s\n", a);
foo1("call foo1 in foo2");
}
int foo(char * a) {
printf("inside foo, a = %s\n", a);
foo2("call foo2 in foo");
return 0;
};
int main() {
foo1("call foo1 in main");
printf("\n");
foo("call foo in main");
return 0;
}
2. Assuming the aspect file, "fooac.acc", is:
before(char * a, char * b) : cflow(call(int foo(char*)) && args(a))
&& call(void foo1(char *)) && args(b) {
// "a" matches the argument of foo , "b" matches argument of foo1
printf("aspect: call foo1 in cflow of foo\n");
printf("foo argument = %s \n", a);
printf("foo1 argument = %s \n", b);
}
3. Compile 2 files with the ACC compiler:
>acc foo.mc fooac.acc
>
4. Compile the generated files with gcc and run the executable file:
>gcc foo.c fooac.c
>./a.out
inside foo1, a = call foo1 in main
inside foo, a = call foo in main
inside foo2, a = call foo2 in foo
aspect: call foo1 in cflow of foo
foo argument = call foo in main
foo1 argument = call foo1 in foo2
inside foo1, a = call foo1 in foo2
[Top]
Example 14: preturn() in advice
method 1: use "acc"
1. Assuming the normal C file, "foo.mc", is:
int foo2(void) {
printf("in foo2\n");
return 0;
}
int foo1(void) {
printf("in foo1\n");
foo2();
printf("end of foo1\n");
return 0;
}
int main(void) {
printf("return value of foo1 = %d \n", foo1());
return 0;
}
2. Assuming the aspect file, "fooac.acc", is:
before(): call(int foo2()) {
printf("before advice \n");
preturn(99);
}
3. Compile 2 files with the ACC compiler:
>acc foo.mc fooac.acc
>
4. Compile the generated files with gcc and run the executable file:
Note: The supports of preturn() and exception handling requires linking with a library file, libacc.a, provided by ACC. The libacc.a resides under the "lib" directory.
>gcc foo.c fooac.c -L../lib -lacc
>./a.out
in foo1
before advice
return value of foo1 = 99
method 2: use "tacc"
1. Assuming the above normal C file is "foo.c" and aspect file is "fooac.acc", compile 2 files with "tacc" :
>tacc foo.c fooac.acc
>
2. Run the executable file:
>./a.out
in foo1
before advice
return value of foo1 = 99
[Top]
Example 15: Exception handling by try() / catch() / throw()
1. Assuming the normal C file, "foo.c", is:
int foo2(int i) {
printf("\t\t\t in foo2\n");
return i;
}
int foo1(int i) {
printf("\t\t in foo1\n");
foo2(i);
printf("\t\t end of foo1\n");
return i;
}
int foo(int i) {
printf("\t in foo\n");
foo1(i);
printf("\t end of foo\n");
return i;
}
int main() {
printf("in main, call foo\n");
foo(1);
printf("end of main\n");
return 0;
}
2. Assuming the aspect file, "fooac.acc", is:
before() : call(int foo2(int)) {
printf("aspect, throw exception 3 before calling foo2\n");
throw(3);
printf("end of aspect\n");
}
catch(int e): try(call(int foo(int))) {
printf("catch exception = %d when calling foo\n", e);
}
3. Compile 2 files with the "tacc"
>tacc foo.c fooac.acc
>
4. Run the executable file:
>./a.out
in main, call foo
in foo
in foo1
aspect, throw exception 3 before calling foo2
catch exception = 3 when calling foo
end of main
[Top]
Example 16: Variable set() pointcut
1. Assuming the normal C file, "foo.c", is:
int a;
int main() {
printf("in main\n");
a = 99;
return 0;
}
2. Assuming the aspect file, "fooac.acc", is:
before() : set(int a) {
printf("aspect, before set variable a \n");
}
3. Compile 2 files with the "tacc"
>tacc foo.c fooac.acc
>
4. Run the executable file:
>./a.out
in main
aspect, before set variable a
[Top]
Example 17: Variable get() pointcut
1. Assuming the normal C file, "foo.c", is:
int a;
int main() {
printf("in main\n");
a = 99;
printf("a = %d\n", a);
return 0;
}
2. Assuming the aspect file, "fooac.acc", is:
before() : get(int a) {
printf("aspect, before get variable a \n");
}
3. Compile 2 files with the "tacc"
>tacc foo.c fooac.acc
>
4. Run the executable file:
>./a.out
in main
aspect, before get variable a
a=99
[Top]
Example 18A generic tracing aspect which outputs parameter type and values, and return type
1. Assuming the normal C file, "foo.c", is:
char * foo(int a) {
return "just a test ";
}
void foo2(int a, double b) {
foo(3);
}
void foo3() {
foo2(5, 2.2);
}
int main() {
foo3();
}
2. Assuming the aspect file, "fooac.acc", is:
before(): call($ $(...)) {
printf("%s \" %s \" in function %s \n", this->kind, this->targetName, this->funcName);
if ( this->argsCount == 0 ) printf("no parameter \n");
else {
for(int i = 1 ; i <= this->argsCount; i++) {
printf("arg[%d] = %s ", i, this->argType(i));
if(strcmp(this->argType(i), "int") == 0) {
printf(", value = %d ", *(int *)(this->arg(i)));
} else if(strcmp(this->argType(i), "double") == 0) {
printf(", value = %.2f ", *(double *)(this->arg(i)));
}
printf("\n");
}
}
printf("return type = %s \n \n", this->retType);
}
3. Compile 2 files with the "tacc"
>tacc foo.c fooac.acc
>
4. Run the executable file:
>./a.out
call "foo3" in function main
"foo3" parameter type:
no parameter
return type = void
call "foo2" in function foo3
"foo2" parameter type:
arg[1] = int , value = 5
arg[2] = double , value = 2.20
return type = void
call "foo" in function foo2
"foo" parameter type:
arg[1] = int , value = 3
return type = char*
[Top]
Note
As a proof of concept, the AspeCt-oriented C compiler was used successfully
in refactoring functions belonging to the logging concern of
ORBit2. ORBit2 is an
object request broker used extensively as middleware in Red Hat's
GNOME project.
The aspect weaving process was successfully integrated into ORBit2's
build system and the generated files were used to build ORBit2. A set
of test cases provided by ORBit2 were successfully exercised as well.
We are actively using AspeCt-oriented C in the refactoring and the development of other systems.
We are successfully using AspeCt-oriented C in a
3rd year introductory-level, undergraduate Operating Systems course.
[Top]