Tutorial — Further Interception
Part 3 of a Guide to Function Interception in JavaScript
Contents
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