Passing Arguments to Affix Functions
AspectJS allows an argument to be passed to a given affix function when it is called.
This is passed as a single parameter to AddPrefix or
AddSuffix (or AddWrapper etc), whence
the interception mechanism passes it on to the affix upon invocation of the interceptee.
Example Nine illustrates this.
// Example 9
function Prefix (Value) { alert ("Prefix executed - Value = " + Value); }
function MyFunc () { alert ("MyFunc executed"); }
AJS.AddPrefix (this, "MyFunc", Prefix, "10");
MyFunc ();
--------------------------------------
Output:
Prefix executed - Value = 10
MyFunc executed
Obviously, it may be necessary at times to pass multiple arguments to a given affix.
To effect this, the relevant objects should be subsumed within a single container-object, which
should then be passed into AddPrefix or
AddSuffix (or
AddWrapper/AddSymmetricWrapper).
Example Ten illustrates this principle.
// Example 10
function Prefix (Container)
{
alert ("Prefix executed");
alert ("Container.Attribute_1 = " + Container.Attribute_1);
alert ("Container.Attribute_2 = " + Container.Attribute_2);
}
function MyFunc () { alert ("MyFunc executed"); }
var MyValues =
{
Attribute_1 : 10,
Attribute_2 : 20
}
AJS.AddPrefix (this, "MyFunc", Prefix, MyValues);
MyFunc ();
--------------------------------------
Output:
Prefix executed
Container.Attribute_1 = 10
Container.Attribute_2 = 20
MyFunc executed
Any arguments passed in the call to the interceptee are also passed on to affix functions. These
are passed as a single parameter that represents the 'arguments'
array that results from the call to the interceptee. This means that affixes can use array syntax to access
each element, and make use of them from there. Example Eleven illustrates this idea.
Note that the PrevPrefixResult argument is passed into Prefix,
even though it is not actually used. This
parameter comes into play when working with multiple prefixes. Note also that this parameter must be present
at this point in an affix's signature, otherwise it would not be possible for the same function to serve
as a prefix and suffix to an interceptee. This is because suffixes are also passed the value returned
from execution of the interceptee.
// Example 11
// Note PrevPrefixResult is not used here
// See tutorial text for an explanation of this
function Prefix (Value, PrevPrefixResult, IntercepteeArgs)
{
alert ("Prefix executed");
alert ("IntercepteeArgs[0] = " + IntercepteeArgs[0]);
alert ("IntercepteeArgs[1] = " + IntercepteeArgs[1]);
alert ("IntercepteeArgs[2] = " + IntercepteeArgs[2]);
}
function MyFunc () { alert ("MyFunc executed"); }
AJS.AddPrefix (this, "MyFunc", Prefix);
MyFunc ("Fred", "Barney", "Wilma");
--------------------------------------
Output:
Prefix executed
IntercepteeArgs[0] = Fred
IntercepteeArgs[1] = Barney
IntercepteeArgs[2] = Wilma
MyFunc executed
Access to the interceptee's arguments allows prefixes to change their value before the interceptee ever sees them. Example Twelve illustrates
this principle.
Note that this is also possible in suffixes but, by definition, this has meaning only for the suffix in question, and not the interceptee or
its caller.
Do remember also that JavaScript sees functions as first-class objects that can have attributes and methods (entities that are quite distinct
from the local objects and inner-functions that arise through its execution). Given that it is possible to augment objects with additional
attributes and methods, it follows that one can augment any function that is passed to an interceptee before the interceptee encounters it.
// Example 12
// Note PrevPrefixResult and Arg are also not used here
// but must be present for IntercepteeArgs to be passed
function Prefix (Arg, PrevPrefixResult, IntercepteeArgs)
{
alert ("Prefix executed");
IntercepteeArgs[0].Name_1 = "Fred"
IntercepteeArgs[0].Name_2 = "Barney"
}
function MyFunc (Names)
{
alert ("MyFunc executed");
alert ("Names.Name_1 = " + Names.Name_1);
alert ("Names.Name_2 = " + Names.Name_2);
}
var Names =
{
Name_1 : "Homer",
Name_2 : "Marge"
};
AJS.AddPrefix (this, "MyFunc", Prefix);
MyFunc (Names);
--------------------------------------
Output:
Prefix executed
MyFunc executed
Names.Name_1 = Fred
Names.Name_2 = Barney
As mentioned previously, suffix functions also enjoy an advantage over prefixes, in that they are passed the value that is
returned from the interceptee as well as its arguments. Example Thirteen demonstrates this.
This allows suffixes to compare what went in to the interceptee with what came out, and a clear application of this is in policing
conformity to pre-conditions and post-conditions within an application. The example shows this by testing the value returned from a function
that is supposed to calculate the area described by the Width and Length
parameters that it is passed (but which has a deliberate bug).
// Example 13
function Suffix (Arg, PrevSuffixResult, IntercepteeArgs, IntercepteeResult)
{
alert ("Suffix executed");
if (IntercepteeArgs[0] * IntercepteeArgs[1] !== IntercepteeResult)
{
alert ("CalcArea is flawed!");
}
}
function CalcArea (Width, Length)
{
return Width + Length;
}
AJS.AddSuffix (this, "CalcArea", Suffix);
CalcArea (10, 20);
--------------------------------------
Output:
Suffix executed
CalcArea is flawed!
Specifying Execution-Maxima
All the affixes in the examples shown above execute indefinitely. This means that
they will continue to execute before or after the interceptee (when it is called) until the execution environment is destroyed (e.g.
the user loads a new page into the browser), or until the relevant prefix/suffix is removed explicitly.
One way of setting the execution limit for an affix is to pass an integer parameter to the relevant setting function.
In Example Fourteen, Prefix is set to execute three times, and once that has
happened it is removed automatically.
Moreover, and in this example, the interceptee has only one prefix, therefore when this is removed the interception mechanism disappears, and the
relationship between the interceptee and its owner-object returns to normal.
Note that if a parameter is supplied for the ExecMax argument, but no actual value needs to be passed for the
prefix-argument parameter, then that should be null or undefined, or
should be an empty string.
Note also that, should a prefix or suffix throw an exception when called, the call still counts as one execution, thus
bringing it one step closer to its removal (assuming a finite execution-limit). In other words, prefixes and suffixes
cannot keep themselves attached to an interceptee indefinitely by throwing exceptions.
// Example 14
function Prefix () { alert ("Prefix executed"); }
function MyFunc () { alert ("MyFunc executed"); }
AJS.AddPrefix (this, "MyFunc", Prefix, null, 2);
MyFunc ();
MyFunc ();
MyFunc ();
--------------------------------------
Output:
Prefix executed
MyFunc executed
Prefix executed
MyFunc executed
MyFunc executed
Generating Diagnostics
Where a large number of intercepts are applied in an application, it is inevitable that
human error will, at some point, cause the wrong parameter or parameter-type to be passed to a given method. To
ameliorate this, AJS (but not AJS_HP) can generate
diagnostic information to aid in detecting the offending line of client-code.
This feature is used by passing a meaningful (to you) string into calls to the AddPrefix,
AddSuffix, AddWrapper and
AddSymmetricWrapper methods of AJS, and into calls to the
AddBefore, AddAfter and
SetExecMax methods of the Affix type.
As a demonstration, Example Fifteen illustrates a call to AddPrefix, and the message
contents of the Error object
thrown by the library, where the ExecMax argument passed is wrapped (incorrectly and deliberately)
in quotes, making it a string rather than a numeric argument.
In this case the exception is caught by the interpreter itself, but wrapping
the call to AddPrefix in a
try block allows client code to catch the exception
(which should be de-rigueur in any live application of AspectJS).
// Example 15
// No exception will be thrown if using AJS_HP
// Exception thrown by AJS will not be reported by IE 6
function Prefix () { alert ("Prefix executed"); }
function MyFunc () { alert ("MyFunc executed"); }
AJS.AddPrefix (this, "MyFunc", Prefix, "", "2", "MyFuncIntercept");
MyFunc ();
--------------------------------------
Output (using AJS, on Firefox):
Error: AJS.AddPrefix - ExecMax argument is non-numeric.
Client-code call point: MyFuncIntercept
Note that, while passing a CallPoint string is optional, doing so requires passing some
value for the parameter that is to be passed to the affix, along with a value for ExecMax.
If no meaningful value can be passed to the affix then this argument should be null or an empty string.
In the case of the
ExecMax parameter, and where the intercept is to persist for a limited number of calls, this can be a simple integer-argument
as shown above. However, when the affix must remain attached indefinitely then callers should pass Infinity,
which is a member of the Global object. Example Sixteen illustrates this.
If you are using
AJS_HP, and you suspect that a bad parameter is causing a problem, switch to using
AJS, and thereby make use of
that object's diagnostic support. Once the problem has been resolved switch back again to using
AJS_HP. The
Tips and Tricks part of this tutorial shows a simple technique for achieving this
with a minimum of fuss.
// Example 16
function Prefix () { alert ("Prefix executed"); }
function MyFunc () { alert ("MyFunc executed"); }
AJS.AddPrefix (this, "MyFunc", Prefix, "", Infinity, "MyFuncIntercept");
MyFunc ();
--------------------------------------
Output:
Prefix executed
MyFunc executed
Go forward to
Part 4 of this tutorial.
Go back to
Part 2 of this tutorial.
Copyright © Dodeca Technologies Ltd. 2007