以一个简单的例子来解释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的源码修改了。有空下次再来写。