1.autojs之lua
2.systemtap从入门到放弃(一)
3.带tap是什么意思?
4.systemtap安装SystemTap
5.SystemTapå·¥å
·ç使ç¨åºç¡
6.systemtap使用指南
autojs之lua
在autojs中使用lua能提升自动化脚本的灵活性和功能。为了实现这一目标,依赖于一个名为luaJ的java实现的lua脚本解释器。下面将逐步展示如何在autojs中集成lua,以及实现的步骤和效果展示。
首先,破解源码免费分享导入luaJ类,这是实现lua脚本运行的基础。确保在项目中正确导入此类,以利用luaJ的解释功能。
接下来,创建一个Globals对象,用于管理全局状态。通过这个对象,可以轻松地在脚本中访问和设置全局变量,使脚本的使用更加灵活。
之后,执行lua文件成为关键步骤。通过加载lua文件并调用其中的函数或执行指令,可以实现自动化的任务,如模拟用户操作、自动化数据处理等。
获取lua变量的值,是进一步操作的基础。这允许根据脚本中的逻辑,动态地访问和使用变量,从而实现复杂的功能。
还有一种运行lua脚本的方式,即通过直接执行lua代码,而非加载文件。这种方式适合编写和执行简短的脚本,或在脚本执行过程中动态生成代码。
在实际应用中,一个典型的lua代码示例可以是自动化点击操作。通过编写简单的脚本,可以模拟用户点击屏幕上的特定位置,实现自动化任务。图片源码怎么
完整源码示例如下:
lua
Globals.set('clickPosition', { x: , y: })
function doClick()
local position = Globals.get('clickPosition')
TouchAction(device).tap({ x: position.x, y: position.y}).perform()
end
以上源码展示了如何在autojs中集成lua,通过导入luaJ类、创建全局变量、执行lua代码来实现自动化功能。使用这种方法,可以极大地提升自动化脚本的效率和可扩展性。
systemtap从入门到放弃(一)
内核调试利器:systemtap从入门到放弃(一)
systemtap,一个用于简化Linux系统运行形态信息收集的开源工具,立足于性能诊断与bug调试。相较于繁琐的工具、耗时的重新编译与引导过程,systemtap以动态hook内核代码的特性,提供了便捷的解决方案。其工作原理与底层kprobe接口紧密相连,通过在kprobe基础上引入脚本解析与内核模块编译运行单元,使开发人员得以在应用层实现对内核的hook,简化了开发流程。
相比传统的kernel API与debugfs接口,systemtap提供了更加简洁的命令行接口与内核指令脚本语言,对于开发者而言,它成为了一款极其实用的工具,尤其在bug调试、性能分析与源码学习方面。
入门systemtap,首先需了解其独特的脚本语法。脚本的编写,就是找到所需事件并设计事件处理流程的过程。系统中常用的语法元素包括脚本命名、注释、变量、数组、条件语句与循环等。其中,变量作用域默认为函数或括号内,全局变量则需在函数外显式声明。数组与关联数组则提供了数据存储与访问的图片怎么转源码灵活性。
在systemtap中,通过探针(probe)来触发事件处理,支持对特定内核函数、模块函数进行探测。此外,systemtap还提供了一系列内置探针(tapset),开发者可直接调用,加速调试与分析工作。
系统中常见的可探测事件包括函数调用、异常处理、系统调用等,这些事件为开发者提供了丰富的调试切入点。通过精心设计的脚本,开发者能够精准捕捉并分析系统行为,优化性能,定位并解决bug。
systemtap的脚本语言简洁易懂,对于底层开发人员而言,学习曲线相对平缓。然而,掌握其脚本语法与内置探针的使用,对于深入应用systemtap,实现高效、精确的调试至关重要。
综上所述,systemtap凭借其独特的功能与简洁的语法,为Linux内核调试提供了一种高效、便捷的工具。从入门到掌握,开发者需深入理解其工作原理与脚本语言,灵活应用内置探针,以实现精准的系统调试与优化。
带tap是什么意思?
在技术领域,tap代表着一种调试协议的名称。tap调试协议可以使调试者连接到目标设备上的调试接口,进而进行源码级别的报表设计控件源码调试和管理。它是一种简单、高效的调试协议,被广泛应用于软件调试和开发中。
在软件调试中,tap的主要意义是提供了一种可视化的、实时的调试手段。通过连接调试器和目标设备,调试者可以随时随地对目标设备中的程序进行调试、查看、修改,从而更好地发现问题并解决它们。同时,tap还能简化调试工作流程,提升开发效率。
tap调试协议可适用于不同的操作系统和开发环境。例如,在嵌入式系统开发中,tap常被应用于ARM架构的芯片和微控制器上,为嵌入式程序的调试和开发提供支持。而在PC端应用开发中,tap则主要应用于C/C++编程语言,可以与各种不同的集成开发环境(IDE)相兼容。总而言之,tap在软件开发、调试领域的应用非常广泛,对提升程序质量和开发效率都起到了积极的作用。
systemtap安装SystemTap
在开始安装SystemTap之前,确保你的系统已经安装了两个关键软件包:kernel-debuginfo RPM和elfutils RPM。kernel-debuginfo是SystemTap依赖于内核调试信息的工具,大多数发行版并未预装,需要从相应的下载站点获取。 elfutils RPM则为SystemTap提供了分析调试信息所需的库函数。推荐安装elfutils-0.或更高版本,目前最新版本为0.-0.1。如果系统中没有,可以访问SystemTap的公众号cms源码官方网站下载RPM包或源代码进行升级。 安装SystemTap有RPM包和源码编译两种途径。对于Fedora Core 6用户,系统自带的systemtap可能已经足够,无需额外安装。而对于其他需要源码编译的情况,步骤如下:首先,从SystemTap FTP站点下载最新的源码包:/root > tar -jxf SystemTap
然后,切换到源码目录:/root > cd src
执行配置步骤:/root/src> ./configure
开始编译:/root/src> make
最后,安装SystemTap:/root/src> make install
请根据你的系统需求和环境,选择合适的安装方法。确保所有依赖已准备就绪,以顺利完成SystemTap的安装过程。SystemTapå·¥å ·ç使ç¨åºç¡
æ¥çå½åå æ ¸çæ¬æ¯åªä¸ä¸ªï¼ç¶å使ç¨æ¾å°å æ ¸æ建ç详ç»ä¿¡æ¯ï¼ç¶åå»å¯¹åºåå¸ç½ç«ä¸æ¾kernel-debuginfoåkernel-debuginfo-commonå ã
å®æå®è£ åå¯ä»¥éè¿ä¸é¢å½ä»¤æµè¯systemtap
è¿è¡æµè¯ï¼ççsystemtapææ å®è£ æåã
ä¸é¢å½ä»¤æ¼ç¤ºæ¥ç__lookup_hash()å½æ°è¿åæ¶å»å¯ä»¥æ¥çå°çåé
å¨ä¸è¡¨ä¸æ¾ç¤ºäºlookup_hashå¨æ件ä¸çè¡å·ï¼æ¾ç¤ºäºå为$return çåéï¼å ¶å®è¿ä¸ªreturnåéå°±æ¯systemtap表示å½æ°è¿åå¼çãè$name,$base,$flagæ们对çlinuxæºç çåç°è¿æ¯__lookup_hashçä¸ä¸ªå ¥åã
ä¸é¢å½ä»¤å¯ä»¥æ¥ç__lookup_hashå½æ°å ¥å£å¯ä»¥æ¥ççåé
ä¹å¯ä»¥éè¿statementæ¹å¼æ¥çå æ ¸ç¬¦å·è¡¨éæç__lookup_hashç¸å ³çè¡
å¦ææ¥æ¾çå æ ¸å½æ°ä½äºæ个模åéå¯ä»¥ä½¿ç¨ä¸é¢å½ä»¤ï¼
éè¿ä¸é¢å½ä»¤å¯ä»¥æ¥çå°æ个æ£å¨è¿è¡çè¿ç¨çå½æ°
ä¸ä¾ä¸çå°æ¾å°äºsyscall.Mountå½æ°ï¼å¹¶ä¸æå®çææåæ°ååæ°ç±»åé½æå°äºåºæ¥ã
åé¢å¯ä»¥å¨stapèæ¬ä¸ï¼è¿ä¸ªå½æ°çä¸ä¸æéç´æ¥ä½¿ç¨è¿äºåæ°ï¼ä¾å¦éè¿$sourceå¯ä»¥è®¿é®å°åæ°source
systemtapæ¯æprint()åprintf()å½æ°ï¼å ¶ä¸printf使ç¨è¯æ³åcè¯è¨ä¸è´ãæ¯æ%sï¼%dï¼%xæ ¼å¼
å¨systemtapéå¡æ¯ä»¥$å¼å¤´çåéé½æ¯ç®æ åéï¼å¦æç®æ åéç»æä½æéæè ç»æä½å¯¹è±¡ï¼é£ä¹å¯ä»¥ä½¿ç¨->ç´æ¥è®¿é®å ¶æåãä¾å¦ä¸ä¾ä¸ï¼
常è§æ åµä¸ï¼printfï¼ï¼æå°targetåéæ¶å»ï¼åªæå°å ¶å¼ãå¦æéè¦å°å ¶æåï¼æéç±»åçtargetéè¦å°å ¶æåç对象çæåå±å¼ï¼å¯ä»¥å¨targetåéåé¢å $çæ¹å¼ä¾å¦ï¼
ä¸è¬æ åµä¸å¯¹structçå±å¼åªä¼å°æåå¼ä¸çº§ï¼å¦æç¸å¯¹æåå é¨ç»§ç»å±å¼å¯ä»¥å¨ç®æ åéåé¢è·$$
å¨systemtapä¸æ¯æé»è¾ifè¯å¥æ ¼å¼ä¸ºï¼
é»è¾è¯å¥æ¯æ以ä¸æ¯è¾
==ï¼!=,>=,>,<,<=
ä¸è¿°ä¾å对ls -lä¸çxmallocè¿è¡å æ å溯ï¼
-d å¯æ§è¡æ件å
--ldd ææå ±äº«åº
-c âls -lâ æ§è¡çåè¿ç¨ä½
ä¸é¢ä¾åå°æå°__lookup_hashä¸returnè¿ådentry*éinodeæåçi_inoåæå
è¿ä¸ä¾åä¸-o zxy.txtçææå°±æ¯å°ç»æåå ¥æ件zxy.txtä¸ï¼é»è®¤è¾åºå°æ§å¶å°ï¼
ä¸é¢ä¾åå°å¨å æ ¸ä¸ä½¿ç¨å¼ºå¶ç±»å转æ¢
è¿é解éä¸ä¸ï¼å æ ¸ä¸æ¹æ³å¼ºå¶è½¬æ¢
å¨ç¨systemtapè·è¸ªå æ ¸æ¶ä½¿ç¨å æ æå°å½ä»¤ï¼å¸¸å¸¸æå°ä¸åºæ¥å¦å¤æ¨¡åçå½æ°ï¼è¿æ¯å 为è¿äºæ¨¡å没æ被å è½½ãå¯ä»¥å¨systemtapå¯å¨å½ä»¤ä½¿ç¨--all-modules æ¹æ³å¼ºå¶å°ææ模å符å·å 载起æ¥ã
ä¸é¢ä¾å对ç¨golangåçdockerdè¿ç¨syscall.Mountè°ç¨å ¥å£æ¶å»æå°syscall.Mountï¼ï¼å½æ°çåæ°
sourceçstringå段å 容
ä¸é¢ä¾åæå°golangåçdockerdè¿ç¨xxx.Getå½æ°è¿åæ¶å»çåæ°æ åµ
}
systemtap对golangæ¯æä¸å¤å®ç¾ï¼ç¨æ·éè¦èªå·±è§£æåºæ¬ç»æä¾å¦golangçstringï¼arrayåsliceè¿äºé½éè¦ç¨æ·èªå·±è§£æãstring被systemtapè¯å«ä¸ºstruct string,æ¤ç»æsystemtapå¯ä»¥è¯å«çå®ä¹å¯ä»¥ç®å为ï¼
éè¦æ³¨æçæ¯éè¿systemtapæå°golang stringçstring->strä¼å¤æå¾å¤å符ï¼å 为stringæåstr并éæç §cè¯è¨å®ä¹çå符串以\0表示å符串ç»æï¼æ们åªè½ç»åstringçå段lenæ¥è·å精确çå符串å 容
sliceå®å ¨ä¸è¢«systemtapè¯å«ï¼æ们å¯ä»¥å°systemtapå¯ä»¥è¯å«çsliceç®å为æ¤ç§å®ä¹ï¼
å ¶ä¸arrayå°±æ¯æåsliceåå¨åå çé¦å°åã
è¦æ¯æ们æ³è·åhelo=[]string{ âhelloâ,âworldâ}è¿æ ·çå符串sliceçå 容å¯ä»¥éè¿systemtapæä¾ç@cast(addrï¼âtypeâ,âfileâ)å½æ°å°æ个å°å强转为fileä¸å®ä¹çtypeç»æãå ·ä½æ¥è¯´å¯ä»¥å¦ä¸åè·åhelloçå 容
systemtap使用指南
Systemtap 是一种工具,用于开发人员和管理员编写和复用简单脚本以深入检查 Linux 系统活动。它允许快速提取、过滤和汇总数据,安全地诊断复杂性能或功能问题。
使用 Systemtap,最简单的探测类型是跟踪一个事件,例如在 open 系统调用执行时打印特定进程信息及参数。
Systemtap 支持多种内置事件,并可自定义额外事件,这些事件通过统一命名的点分隔参数化标识符语法定义。编写脚本时,需指定探测位置及打印内容。
Systemtap 的脚本通过翻译成 C 代码并运行系统 C 编译器来创建内核模块,模块在内核中激活所有探测事件。模块加载时,探测到的事件触发编译的处理程序运行。
跟踪事件的处理程序可以使用丰富控制结构,类似于 awk 语法,用于描述算法。脚本支持条件语句,允许限制跟踪或逻辑特定于进程或感兴趣的地方。
Systemtap 脚本可以使用变量、表达式和函数,支持 C 和 awk 风格的语法,以及字符串和数字运算。全局变量和特殊“目标变量”用于访问探测点上下文。
目标变量允许访问内核源代码中的值,并提供如地址、结构打印、变量范围和类型转换功能。全局变量用于在探测程序之间共享数据。
脚本还可以包含自定义函数,定义在脚本中的任何位置,并接受字符串或数字参数,返回单个字符串或数字。
Systemtap 提供关联数组,用于在不同探测程序之间共享数据,数组实现为哈希表,并且必须声明为全局变量。数组支持设置、查找、迭代和测试元素。
探测程序执行受到时间限制,避免了内存动态分配,且具有安全机制,如限制函数调用嵌套深度和检测潜在危险操作。
Systemtap 通过 tapset 脚本库支持共享,允许建立相互构建的脚本库,这些库可以自动解析未定义的全局符号以在脚本中进行搜索。
嵌入式 C 代码功能允许使用安全且正确的 C 代码,以补充探测功能,如遍历全局链表。此代码在内核模块中转录,并需遵守安全约束。
为了防止名称冲突,建议遵循 Systemtap tapset 开发人员的命名约定,以避免翻译或运行时错误。
Systemtap 的安装方式包括默认目录和额外目录选项,以及使用 /usr/share/systemtap/tapset/ 和 sourceware.org 等资源。
总结,Systemtap 提供了一种实用的工具,让开发者和管理员能够深入分析 Linux 系统活动,诊断性能和功能问题。尽管存在安全风险,对于内核程序员而言,它提供了访问内核信息和增强理解的有效途径。
手写webpacktapable源码,官方tapable的性能真的就一定是好的吗?
完整的手写源码仓库tapable是Webpack?插件机制核心。?mini-tapable?不仅解读官方?tapable?的源码,还用自己的思路去实现一遍,并且和官方的运行时间做了个比较,我和webpack作者相关的讨论可以点击查看。webpacktapable源码内部根据newFunction动态生成函数执行体这种优化方式不一定是好的。当我们熟悉了tapable后,就基本搞懂了webpackplugin的底层逻辑,再回头看webpack源码就轻松很多
目录src目录。这个目录下是手写所有的tapablehook的源码,每个hook都用自己的思路实现一遍,并且和官方的hook执行时间做个对比。
tapable的设计理念:单态、多态及内联缓存由于在webpack打包构建的过程中,会有上千(数量其实是取决于自身业务复杂度)个插件钩子执行,同时同类型的钩子在执行时,函数参数固定,函数体相同,因此tapable针对这些业务场景进行了相应的优化。这其中最重要的是运用了单态性及多态性概念,内联缓存的原理,也可以看这个issue。为了达到这个目标,tapable采用newFunction动态生成函数执行体的方式,主要逻辑在源码的HookCodeFactory.js文件中。
如何理解tapable的设计理念思考下面两种实现方法,哪一种执行效率高,哪一种实现方式简洁?
//方法一:constcallFn=(...tasks)=>(...args)=>{ for(constfnoftasks){ fn(...args)}}//方法二:constcallFn2=(a,b,c)=>(x,y)=>{ a(x,y);b(x,y);c(x,y);}callFn及callFn2的目的都是为了实现将一组方法以相同的参数调用,依次执行。很显然,方法一效率明显更高,并且容易扩展,能支持传入数量不固定的一组方法。但是,如果根据单态性以及内联缓存的说法,很明显方法二的执行效率更高,同时也存在一个问题,即只支持传入a,b,c三个方法,参数形态也固定,这种方式显然没有方法一灵活,那能不能同时兼顾效率以及灵活性呢?答案是可以的。我们可以借助newFunction动态生成函数体的方式。
classHookCodeFactory{ constructor(args){ this._argNames=args;this.tasks=[];}tap(task){ this.tasks.push(task);}createCall(){ letcode="";//注意思考这里是如何拼接参数已经函数执行体的constparams=this._argNames.join(",");for(leti=0;i<this.tasks.length;i++){ code+=`varcallback${ i}=this.tasks[${ i}];callback${ i}(${ params})`;}returnnewFunction(params,code);}call(...args){ constfinalCall=this.createCall();//将函数打印出来,方便观察最终拼接后的结果console.log(finalCall);returnfinalCall.apply(this,args);}}//构造函数接收的arg数组里面的参数,就是taska、b、c三个函数的参数constcallFn=newHookCodeFactory(["x","y","z"]);consta=(x,y,z)=>{ console.log("taska:",x,y,z);};constb=(x,y,z)=>{ console.log("taskb:",x,y,z);};constc=(x,y,z)=>{ console.log("taskc:",x,y,z);};callFn.tap(a);callFn.tap(b);callFn.tap(c);callFn.call(4,5,6);当我们在浏览器控制台执行上述代码时:
拼接后的完整函数执行体:
可以看到,通过这种动态生成函数执行体的方式,我们能够同时兼顾性能及灵活性。我们可以通过tap方法添加任意数量的任务,同时通过在初始化构造函数时newHookCodeFactory(['x','y',...,'n'])传入任意参数。
实际上,这正是官方tapable的HookCodeFactory.js的简化版本。这是tapable的精华所在。
tapable源码解读tapable最主要的源码在Hook.js以及HookCodeFactory.js中。Hook.js主要是提供了tap、tapAsync、tapPromise等方法,每个Hook都在构造函数内部调用consthook=newHook()初始化hook实例。HookCodeFactory.js主要是根据newFunction动态生成函数执行体。
demo以SyncHook.js为例,SyncHook钩子使用如下:
const{ SyncHook}=require("tapable");debugger;consttesthook=newSyncHook(["compilation","name"]);//注册plugin1testhook.tap("plugin1",(compilation,name)=>{ console.log("plugin1",name);compilation.sum=compilation.sum+1;});//注册plugin2testhook.tap("plugin2",(compilation,name)=>{ console.log("plugin2..",name);compilation.sum=compilation.sum+2;});//注册plugin3testhook.tap("plugin3",(compilation,name)=>{ console.log("plugin3",compilation,name);compilation.sum=compilation.sum+3;});constcompilation={ sum:0};//第一次调用testhook.call(compilation,"mytest1");//第二次调用testhook.call(compilation,"mytest2");//第三次调用testhook.call(compilation,"mytest3");...//第n次调用testhook.call(compilation,"mytestn");我们用这个demo做为用例,一步步debug。
SyncHook.js源码主要逻辑如下:
constHook=require("./Hook");constHookCodeFactory=require("./HookCodeFactory");//继承HookCodeFactoryclassSyncHookCodeFactoryextendsHookCodeFactory{ }constfactory=newSyncHookCodeFactory();constCOMPILE=function(options){ factory.setup(this,options);returnfactory.create(options);};functionSyncHook(args=[],name=undefined){ //初始化Hookconsthook=newHook(args,name);//注意这里修改了hook的constructorhook.constructor=SyncHook;...//每个钩子都必须自行实现自己的compile方法!!!hook.compile=COMPILE;returnhook;}Hook.js源码主要逻辑如下:
//问题一:思考一下为什么需要CALL_DELEGATEconstCALL_DELEGATE=function(...args){ //当第一次调用时,实际上执行的是CALL_DELEGATE方法this.call=this._createCall("sync");//当第二次或者第n次调用时,此时this.call方法已经被设置成this._createCall的返回值returnthis.call(...args);};...classHook{ constructor(args=[],name=undefined){ this._args=args;this.name=name;this.taps=[];//存储我们通过hook.tap注册的插件this.interceptors=[];this._call=CALL_DELEGATE;//初始化时,this.call被设置成CALL_DELEGATEthis.call=CALL_DELEGATE;...//问题三:this._x=undefined是什么this._x=undefined;//this._x实际上就是this.taps中每个插件的回调//问题四:为什么需要在构造函数中绑定这些函数this.compile=this.compile;this.tap=this.tap;this.tapAsync=this.tapAsync;this.tapPromise=this.tapPromise;}//每个钩子必须自行实现自己的compile方法。compile方法根据this.taps以及this._args动态生成函数执行体compile(options){ thrownewError("Abstract:shouldbeoverridden");}//生成函数执行体_createCall(type){ returnthis.compile({ taps:this.taps,interceptors:this.interceptors,args:this._args,type:type});}..._tap(type,options,fn){ ...this._insert(options);}tap(options,fn){ this._tap("sync",options,fn);}_resetCompilation(){ this.call=this._call;this.callAsync=this._callAsync;this.promise=this._promise;}_insert(item){ //问题二:为什么每次调用testhook.tap()注册插件时,都需要重置this.call等方法?this._resetCompilation();...}}思考Hook.js源码中的几个问题问题一:为什么需要CALL_DELEGATE
问题二:为什么每次调用testhook.tap()注册插件时,都需要重置this.call等方法?
问题三:this._x=undefined是什么
问题四:为什么需要在构造函数中绑定this.compile、this.tap、this.tapAsync以及this.tapPromise等方法
当我们每次调用testhook.tap方法注册插件时,流程如下:
方法往this.taps数组中添加一个插件。this.__insert方法逻辑比较简单,但这里有一个细节需要注意一下,为什么每次注册插件时,都需要调用this._resetCompilation()重置this.call等方法?我们稍后再看下这个问题。先继续debug。
当我们第一次(注意是第一次)调用testhook.call时,实际上调用的是CALL_DELEGATE方法
constCALL_DELEGATE=function(...args){ //当第一次调用时,实际上执行的是CALL_DELEGATE方法this.call=this._createCall("sync");//当第二次或者第n次调用时,此时this.call方法已经被缓存成this._createCall的返回值returnthis.call(...args);};CALL_DELEGATE调用this._createCall函数根据注册的this.taps动态生成函数执行体。并且this.call被设置成this._createCall的返回值缓存起来,如果this.taps改变了,则需要重新生成。
此时如果我们第二次调用testhook.call时,就不需要再重新动态生成一遍函数执行体。这也是tapable的优化技巧之一。这也回答了问题一:为什么需要CALL_DELEGATE。
如果我们调用了n次testhook.call,然后又调用testhook.tap注册插件,此时this.call已经不能重用了,需要再根据CALL_DELEGATE重新生成一次函数执行体,这也回答了问题二:为什么每次调用testhook.tap()注册插件时,都需要重置this.call等方法。可想而知重新生成的过程是很耗时的。因此我们在使用tapable时,最好一次性注册完所有插件,再调用call
testhook.tap("plugin1");testhook.tap("plugin2");testhook.tap("plugin3");testhook.call(compilation,"mytest1");//第一次调用call时,会调用CALL_DELEGATE动态生成函数执行体并缓存起来testhook.call(compilation,"mytest2");//不会重新生成函数执行体,使用第一次的testhook.call(compilation,"mytest3");//不会重新生成函数执行体,使用第一次的避免下面的调用方式:
testhook.tap("plugin1");testhook.call(compilation,"mytest1");//第一次调用call时,会调用CALL_DELEGATE动态生成函数执行体并缓存起来testhook.tap("plugin2");testhook.call(compilation,"mytest2");//重新调用CALL_DELEGATE生成函数执行体testhook.tap("plugin3");testhook.call(compilation,"mytest3");//重新调用CALL_DELEGATE生成函数执行体现在让我们看看第三个问题,调用this.compile方法时,实际上会调用HookCodeFacotry.js中的setup方法:
setup(instance,options){ instance._x=options.taps.map(t=>t.fn);}对于问题四,实际上这和V8引擎的HiddenClass有关,通过在构造函数中绑定这些方法,类中的属性形态固定,这样在查找这些方法时就能利用V8引擎中HiddenClass属性查找机制,提高性能。
HookCodeFactory.js主要逻辑:
classHookCodeFactory{ constructor(config){ this.config=config;this.options=undefined;this._args=undefined;}create(options){ this.init(options);letfn;switch(this.options.type){ case'sync':fn=newFunction(...)breakcase'async':fn=newFunction(...)breakcase'promise':fn=newFunction(...)break}this.deinit();returnfn;}setup(instance,options){ instance._x=options.taps.map(t=>t.fn);}...}手写tapable每个Hook手写tapable中所有的hook,并比较我们自己实现的hook和官方的执行时间
这里面每个文件都会实现一遍官方的hook,并比较执行时间,以SyncHook为例,批量注册个插件时,我们自己手写的MySyncHook执行时间0.ms,而官方的需要6ms,这中间整整倍的差距!!!
具体可以看我的仓库
原文:/post/