使用 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 panda库从基础到高级操作分析

《pythonpanda库从基础到高级操作分析》本文介绍了Pandas库的核心功能,包括处理结构化数据的Series和DataFrame数据结构,数据读取、清洗、分组聚合、合并、时间序列分析及大数据... 目录1. Pandas 概述2. 基本操作:数据读取与查看3. 索引操作:精准定位数据4. Group

Spring Boot集成/输出/日志级别控制/持久化开发实践

《SpringBoot集成/输出/日志级别控制/持久化开发实践》SpringBoot默认集成Logback,支持灵活日志级别配置(INFO/DEBUG等),输出包含时间戳、级别、类名等信息,并可通过... 目录一、日志概述1.1、Spring Boot日志简介1.2、日志框架与默认配置1.3、日志的核心作用

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

破茧 JDBC:MyBatis 在 Spring Boot 中的轻量实践指南

《破茧JDBC:MyBatis在SpringBoot中的轻量实践指南》MyBatis是持久层框架,简化JDBC开发,通过接口+XML/注解实现数据访问,动态代理生成实现类,支持增删改查及参数... 目录一、什么是 MyBATis二、 MyBatis 入门2.1、创建项目2.2、配置数据库连接字符串2.3、入

Springboot项目启动失败提示找不到dao类的解决

《Springboot项目启动失败提示找不到dao类的解决》SpringBoot启动失败,因ProductServiceImpl未正确注入ProductDao,原因:Dao未注册为Bean,解决:在启... 目录错误描述原因解决方法总结***************************APPLICA编

深度解析Spring Security 中的 SecurityFilterChain核心功能

《深度解析SpringSecurity中的SecurityFilterChain核心功能》SecurityFilterChain通过组件化配置、类型安全路径匹配、多链协同三大特性,重构了Spri... 目录Spring Security 中的SecurityFilterChain深度解析一、Security

MySQL中EXISTS与IN用法使用与对比分析

《MySQL中EXISTS与IN用法使用与对比分析》在MySQL中,EXISTS和IN都用于子查询中根据另一个查询的结果来过滤主查询的记录,本文将基于工作原理、效率和应用场景进行全面对比... 目录一、基本用法详解1. IN 运算符2. EXISTS 运算符二、EXISTS 与 IN 的选择策略三、性能对比

SpringBoot多环境配置数据读取方式

《SpringBoot多环境配置数据读取方式》SpringBoot通过环境隔离机制,支持properties/yaml/yml多格式配置,结合@Value、Environment和@Configura... 目录一、多环境配置的核心思路二、3种配置文件格式详解2.1 properties格式(传统格式)1.

Apache Ignite 与 Spring Boot 集成详细指南

《ApacheIgnite与SpringBoot集成详细指南》ApacheIgnite官方指南详解如何通过SpringBootStarter扩展实现自动配置,支持厚/轻客户端模式,简化Ign... 目录 一、背景:为什么需要这个集成? 二、两种集成方式(对应两种客户端模型) 三、方式一:自动配置 Thick

使用Python构建智能BAT文件生成器的完美解决方案

《使用Python构建智能BAT文件生成器的完美解决方案》这篇文章主要为大家详细介绍了如何使用wxPython构建一个智能的BAT文件生成器,它不仅能够为Python脚本生成启动脚本,还提供了完整的文... 目录引言运行效果图项目背景与需求分析核心需求技术选型核心功能实现1. 数据库设计2. 界面布局设计3