For whoever is interested: A replace method aspect template

With many thanks to @ck who helped met with it and killed the bugs I encountered :+1:

@mh: Maybe somethings for the documentation or a new item template in Oxygene?

Edit: You need Release 2423 (tonight’s build) to run this aspect

The template
You can replace the implementation of any method (including methods defined on objects from the runtime or other third party DLL files) with your own with the following aspect class template.

The example replaces all calls to String.Substring with a call to buildITClasses.buildITString.SafeSubstring.

namespace builditAspects;

// 1. Type where the method has been defined
// 2. True for a static method, false for an instance method
// 3. Name of the method to replace
// 4. Count of generic parameters
// 5. Array of the parameter types
[MethodAspectAttribute(typeOf(String), false, 'Substring', 0, [typeOf(Int32), typeOf(Int32)])]
[MethodAspectAttribute(typeOf(String), false, 'Substring', 0, [typeOf(Int32)])]
type SubstringAspect = public class(IMethodCallDecorator)
public
    method ProcessMethodCall(aContext: IContext; aMethod: IMethod; aValue: ParameterizedValue): Value;
    begin
      var TheTypeName := "buildITClasses.buildITString"; // Replace with your full type name
      var TheReplaceMentMethodName := "SafeSubstring";   // Replace with your static function name

      // This is the replacement method, so don't change anything
      if aContext.CurrentMethod.Name = TheReplaceMentMethodName then
          if aContext.CurrentType.Name = TheTypeName then
              exit;

      // Get the type definition
      var TheType := aContext.Services.FindType(TheTypeName);

      // If The type was not found, what means that there is no reference to the reimplementation class
      // so in this case just exit and do not change anything 
      // if you want you can show a warning with aContext.Services.EmitWarning
      if TheType = nil then
        exit;
      // Also, if the replacement method was not found, also just exit and do not change anything 
      // You might want to do a better check to see if there is a replacement with the correct parameters
      if TheType.GetMethods(TheReplaceMentMethodName).Length = 0 then
        exit;

      // Get the current arguments
      var lArgs := aValue.Parameters.ToList();

      // Get the instance where the method was called on (not needed for a static method)
      var lSelf := ProcValue(aValue).Self;
      lSelf := IdentifierValue(lSelf).Start; 
      // Insert this instance as first parameter (not needed for a static method)
      lArgs.Insert(0, lSelf); 

      // Create and exit the new method call that will replace the original
      exit new ProcValue(new TypeValue(TheType), TheReplaceMentMethodName, lArgs.ToArray);
   end;
end;
end.

You have to implement the new method yourself (for all overloads that you specified with a MethodAspectAttribute) with one extra parameter; the first parameter should be of the type that the original method was part of.
This extra parameter is not needed to replace a static method.

So for this example, you have to implement this somewhere in your solution (not in the aspect dll!):

> static method SafeSubstring(aString: String; start: Integer; length: Integer): String;
> static method SafeSubstring(aString: String; start: Integer): String;

Note: Just to be clear: The method calls will only be replaced in your own code that can access the new implementation, and only in projects that reference the DLL where this aspect is defined.

2 Likes