当前位置: 首页 > 工具软件 > AspectD > 使用案例 >

Aspectd 原理浅析

严柏
2023-12-01

以一个简单的例子来解释aspectd改造后的代码的调用过程。

声明一个简单的类A

class A {
  int m;
}

void main() {
  A a = A();
  print('a value :${a.m}');
  ...
}

在main()方法中打印一下。

写一个注解,hook 一个名为A的类的构造函数。

向该类调用的前后点加注释。

import 'package:aspectd/aspectd.dart';

@Aspect()
@pragma("vm:entry-point")
class RegularCallDemo {
  @pragma("vm:entry-point")
  RegularCallDemo();

  @Call("package:example/main.dart", "A", "+A")
  @pragma("vm:entry-point")
  static dynamic hookMethod(PointCut pointcut) {
    print('[KWLM1]: Before A! named widget');
    dynamic object = pointcut.proceed();
    print('[KWLM1]: After A! named');
    return object;
  }
}

可以dump_kernel一下。

static method main() → void {
    main::A* a = aop2::RegularCallDemo::hookMethod(new poi::PointCut::•(<dynamic, dynamic>{"library": "package:example", "file": "file:///Users/local-admin/Documents/code2/aspectd/example/lib/main.dart", "lineNum": "103", "lineOffset": "8"}, "A", "A", "aop_stub_0", <dynamic>[], <dynamic, dynamic>{}));
    core::print("a value :${a.{main::A::m}}");
    ...
    bin::runApp(new main::MyApp::•());
  }

发现代码前面加上了RegularCallDemo,也就是转而指向了aop_impl.dart中的静态方法。

这里要

1.构造一个PointCut类

pointcut中记录了ast location等一些相关的信息。

2.调用pointcut.proceed()方法

点进去可以看到pointcut中的proceed是空的方法,那么这块调用的啥呢?

/// Object carrying callsite information and methods which can enable you to
/// call the original implementation.
@pragma('vm:entry-point')
class PointCut {
  /// PointCut default constructor.
  @pragma('vm:entry-point')
  PointCut(this.sourceInfos, this.target, this.function, this.stubKey,
      this.positionalParams, this.namedParams);

  /// Source infomation like file, linenum, etc for a call.
  final Map<dynamic, dynamic> sourceInfos;

  /// Target where a call is operating on, like x for x.foo().
  final Object target;

  /// Function name for a call, like foo for x.foo().
  final String function;

  /// Unique key which can help the proceed function to distinguish a
  /// mocked call.
  final String stubKey;

  /// Positional parameters for a call.
  final List<dynamic> positionalParams;

  /// Named parameters for a call.
  final Map<dynamic, dynamic> namedParams;

  /// Unified entrypoint to call a original method,
  /// the method body is generated dynamically when being transformed in
  /// compile time.
  @pragma('vm:entry-point')
  Object proceed() {
    return null;
  }
}

想想我们最终返回的是一个当前类的实例。所以里面要处理类的构造过程。我们继续翻一下kernel_dump后的内容。

library from "package:aspectd/src/plugins/aop/annotation/pointcut.dart" as poi {

  import "package:example/main.dart";

  @#C544
  class PointCut extends core::Object {
    final field core::Map<dynamic, dynamic>* sourceInfos;
    final field core::Object* target;
    final field core::String* function;
    final field core::String* stubKey;
    final field core::List<dynamic>* positionalParams;
    final field core::Map<dynamic, dynamic>* namedParams;
    @#C544
    constructor •(core::Map<dynamic, dynamic>* sourceInfos, core::Object* target, core::String* function, core::String* stubKey, core::List<dynamic>* positionalParams, core::Map<dynamic, dynamic>* namedParams) → poi::PointCut*
      : poi::PointCut::sourceInfos = sourceInfos, poi::PointCut::target = target, poi::PointCut::function = function, poi::PointCut::stubKey = stubKey, poi::PointCut::positionalParams = positionalParams, poi::PointCut::namedParams = namedParams, super core::Object::•()
      ;
    @#C544
    method proceed() → core::Object* {
      if(this.stubKey.==("aop_stub_0")) {
        return this.aop_stub_0();
      }
      return null;
    }
    abstract member-signature get _identityHashCode() → core::int*; -> core::Object::_identityHashCode
    abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*; -> core::Object::_instanceOf
    abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOf
    abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfTrue
    abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOfFalse
    abstract member-signature operator ==(dynamic other) → core::bool*; -> core::Object::==
    abstract member-signature get hashCode() → core::int*; -> core::Object::hashCode
    abstract member-signature method toString() → core::String*; -> core::Object::toString
    abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic; -> core::Object::noSuchMethod
    abstract member-signature get runtimeType() → core::Type*; -> core::Object::runtimeType
    method aop_stub_0() → core::Object* {
      return new main::A::•();
    }
  }
}

可以看到,dump后的PointCut里面proceed方法有了实现,里面判断如果方法名是aop_stub_0,就指向aop_stub_0()方法。

在这个方法里面处理了A类的构造过程。

这里选的了一个简单的类。

如果类的构造函数参数比较多。

可能你看到的是下面的情况。

method aop_stub_0() → core::Object* {
      return new main::D::•(a: this.namedParams.[]("a") as core::int*, b: this.namedParams.[]("b") as core::int*, c: this.namedParams.[]("c") as core::String*, back: this.namedParams.[]("back") as core::Function*, w: this.namedParams.[]("w") as fra::Widget*, color: this.namedParams.[]("color") as ui::Color*);
    }

实际开发中,很多构造函数的hook,往往都是这儿从map把参数还原到调用原始构造函数的过程出错。这里面就涉及到一些aspectd的源码修改了。有空下次再来写。

 类似资料: