使用 AST语法树分析与修改Javascript 代码

2024-06-19 02:36

本文主要是介绍使用 AST语法树分析与修改Javascript 代码,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1、AST语法树简介

当编写代码分析工具、代码美化工具、网站逆向分析等场景时,通常需要使用AST语法树技术。
比如项目开发过程中常遇到的场景:某个公共函数名需要更改,但被很多文件多处代码调用,手工修改非常容易漏改、改错等,而且还花时间。又如在网站逆向分析时,由于使用了混淆技术,代码难以阅读。 这时使用AST技术可以轻松完成这些任务。
对于重要项目,使用AST技术来开发自动化代码质量检查工具,也是提高项目质量的重要手段。

1.1 什么是AST

AST(Abstract Syntax Tree)抽象语法树,是源代码语法结构的一种抽象表示。常用于编译器、代码分析工具等。

如何将代码转为AST呢?

构建 AST 树的步骤
在这里插入图片描述

生成AST树之前,通常需要先将源代码转为 Parser Tree。
如1个表达式,2*7 + 3 , 先转为 Parser Tree,如下,其根节点,由 1个操作符 + 与2个 term 组成。左边的term 是 factor * factor 构成。 右边的term 为1个factor.。这个Parser Tree表示了完整的语法规则,但如果语句复杂,会造成树的层次复杂。

AST tree是Parse Tree的简化,其特点:
用操作符做为根节点。整个树由根节点,内部节点,叶节点组成
叶节点是由操作数组成,内部节点的子节点都是操作数(或称叶节点)。
内部节点不展示语法规则,因此是抽象的。

在这里插入图片描述

1.2 流行的AST工具库

当前有许多工具支持源代码转AST 树,以及逆向转回。

在线工具
https://astexplorer.net/

Babel/parser插件方案
babel/parser是JS社区最常用的解析器。但Babel方案节点多、API也很多,因此学习成本高, 另外编程代码工作量较大,可读性差。

jscodeshift
相比于Babel而言,jscodeshift的优势是匹配节点更简便一些,链式操作用起来更加顺手。还提供了命令行工具。

本文主要介绍,用jscodeshift 来进行AST解析。

2、jscodeshift库介绍

jscodeshift 是一个源代码转换工具,它基于 AST(抽象语法树)进行操作,使得开发者能够自定义转换规则来处理 JavaScript 代码。

当你使用 jscodeshift 解析 JavaScript 源文件后,会得到一个表示源文件结构的 AST。理解jscodeshift的AST树结构是最基础的知识点。

AST 树的每个节点都代表了源代码中的一部分。节点是object类型,由许多“属性–值”对组成。 比如 type, value, id, 子节点等。

在 AST 中,主要的概念和名词包括:

节点(Node):AST 的基本单元,节点通常包含类型、值、子节点等属性。
{
type: ‘Literal’,
value: ‘foo’,
raw: ‘“foo”’
}

类型(Type):节点的类型,比如 VariableDeclarator(变量声明符)、CallExpression(函数调用表达式)等。

值(Value):对于某些类型的节点,比如字面量节点(字符串、数字等),会有一个值属性,表示该节点的具体值。

子节点(Children or Subtree):节点通常会有子节点,这些子节点代表了该节点在语法树下的更具体的结构。例如,一个 CallExpression 节点可能有一个子节点是函数名,另一个子节点是参数列表。

jscodeshift 主要数据结构
1)NodePath对象: 是对Node的封装,
2) Collection对象 是1个Array,元素为NodePath,

3、使用jscodeshift修改源代码步骤

3.1 安装 jscodeshift

全局安装

npm i -g jscodeshift 

本地安装

npm install --save-dev  jscodeshift 

为测试目的,安装 lodash库

npm install --save-dev lodash 

3.2 准备1个源文件

如我们准备1个源文件 example.js , 包含1个add()函数, 及调用。 后面我们通过AST技术来修改函数名以及调用名。

const array = require('lodash/array')
var x = 1; 
var a1 = array.initial([1,2,3,4,5])
console.log(a1)
// define a function
function add(a,b){return a + b; 
}
x = add( 10,20)

3.3 使用AST 查找与修改函数名与调用名

对于初学者来说,可以先用在线工具可视化方式查看AST节点,确定要查找及替换节点名称。
访问地址:https://astexplorer.net/

3.3.1 先用在线工具查看 AST 结果

打开 https://astexplorer.net/ 网页,将example.js 代码拷贝过来后,会自动生成 AST数。
在这里插入图片描述
可以看到function 的节点 type 为FunctionDeclaration,

x = add( 10,20) 对应节点为 ExpressionStatement, 我们要查找的调用表达式为为右侧子节点,打开会看到, 子节点的type 为 CallExpression

3.3.2 编写代码

在example.js 同1目录下,新建1个文件ast.js,添加如下内容:

const path = require('path')
const fs = require('fs')
const jc = require('jscodeshift')  //引入jscodeshift // 读取源代码文件
const fpath =  path.join(__dirname, 'example.js')
const root = jc(fs.readFileSync(fpath, 'utf8'))let oldFuncName = "add"   //设置原函数名称//查找及修改函数名称
let newFuncName = "newAdd"
root.find(jc.FunctionDeclaration).forEach( (path) => {//如果函数名为oldFuncName, 则修改为新名称if (path.value.id.name == oldFuncName) {path.value.id.name = newFuncNameconsole.log("change name: ", path.value.id.name)}})// 修改函数调用名称
root.find(jc.CallExpression).forEach((path)=>{if(path.value.callee.name == oldFuncName){path.value.callee.name = newFuncNameconsole.log("change name of CallExpress: ", path.value.callee.name)}
})// 输出修改后的代码
console.log(root.toSource())

代码解释:

  • const root = jc(fs.readFileSync(fpath, 'utf8')) 将源码转为AST 树结构对象
  • root.find(jc.FunctionDeclaration) 在AST树中查找 type = 'FunctionDeclaration’ 的节点,可能有多个。 用forEach()方法遍历。
  • find()方法的查找结果为NodePath 集合, NodePath.value中保存 Node对象。 不同类型节点有不同的属性。
    • FunctionDeclaration 节点名称为 path.value.id.name,
    • CallExpression 调用函数名为 path.value.callee.name
  • oot.toSource() 将 AST 树还原为源代码。

3.3.3 测试代码

在代码所在目录下

node ast.js 

输出结果如下,可以看到,函数名与调用名均已改为 newAdd

change name:  newAdd
change name:  newAdd
const array = require('lodash/array')
var x = 1;
var a1 = array.initial([1,2,3,4,5])
console.log(a1)
// define a function
function newAdd(a,b){return a + b;
}
x = newAdd( 10,20)

怎么样,使用 AST 来修改代码也不复杂 ,而且效率远高于手工。可以按上述思路,修改变量名,添加、删除语句等操作,对自己的代码进行修改或美化。

这篇关于使用 AST语法树分析与修改Javascript 代码的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1073765

相关文章

使用Python和OpenCV库实现实时颜色识别系统

《使用Python和OpenCV库实现实时颜色识别系统》:本文主要介绍使用Python和OpenCV库实现的实时颜色识别系统,这个系统能够通过摄像头捕捉视频流,并在视频中指定区域内识别主要颜色(红... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间详解

Windows下C++使用SQLitede的操作过程

《Windows下C++使用SQLitede的操作过程》本文介绍了Windows下C++使用SQLite的安装配置、CppSQLite库封装优势、核心功能(如数据库连接、事务管理)、跨平台支持及性能优... 目录Windows下C++使用SQLite1、安装2、代码示例CppSQLite:C++轻松操作SQ

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4

一文详解如何在idea中快速搭建一个Spring Boot项目

《一文详解如何在idea中快速搭建一个SpringBoot项目》IntelliJIDEA作为Java开发者的‌首选IDE‌,深度集成SpringBoot支持,可一键生成项目骨架、智能配置依赖,这篇文... 目录前言1、创建项目名称2、勾选需要的依赖3、在setting中检查maven4、编写数据源5、开启热

SQL Server修改数据库名及物理数据文件名操作步骤

《SQLServer修改数据库名及物理数据文件名操作步骤》在SQLServer中重命名数据库是一个常见的操作,但需要确保用户具有足够的权限来执行此操作,:本文主要介绍SQLServer修改数据... 目录一、背景介绍二、操作步骤2.1 设置为单用户模式(断开连接)2.2 修改数据库名称2.3 查找逻辑文件名

Python常用命令提示符使用方法详解

《Python常用命令提示符使用方法详解》在学习python的过程中,我们需要用到命令提示符(CMD)进行环境的配置,:本文主要介绍Python常用命令提示符使用方法的相关资料,文中通过代码介绍的... 目录一、python环境基础命令【Windows】1、检查Python是否安装2、 查看Python的安

Java对异常的认识与异常的处理小结

《Java对异常的认识与异常的处理小结》Java程序在运行时可能出现的错误或非正常情况称为异常,下面给大家介绍Java对异常的认识与异常的处理,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参... 目录一、认识异常与异常类型。二、异常的处理三、总结 一、认识异常与异常类型。(1)简单定义-什么是

Python并行处理实战之如何使用ProcessPoolExecutor加速计算

《Python并行处理实战之如何使用ProcessPoolExecutor加速计算》Python提供了多种并行处理的方式,其中concurrent.futures模块的ProcessPoolExecu... 目录简介完整代码示例代码解释1. 导入必要的模块2. 定义处理函数3. 主函数4. 生成数字列表5.

Python中help()和dir()函数的使用

《Python中help()和dir()函数的使用》我们经常需要查看某个对象(如模块、类、函数等)的属性和方法,Python提供了两个内置函数help()和dir(),它们可以帮助我们快速了解代... 目录1. 引言2. help() 函数2.1 作用2.2 使用方法2.3 示例(1) 查看内置函数的帮助(

Linux脚本(shell)的使用方式

《Linux脚本(shell)的使用方式》:本文主要介绍Linux脚本(shell)的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述语法详解数学运算表达式Shell变量变量分类环境变量Shell内部变量自定义变量:定义、赋值自定义变量:引用、修改、删