AST解web控制流平坦化

  • 此代码可以解决大部分 while if else 控制流平坦化
  • 原理:
    • 先将 if 语句转为 switch 语句,再将 switch 分支合并,最后删除已合并的分支(具体看代码)
  • 实现效果图
    在这里插入图片描述
  • 首先安装依赖:
npm install @babel/parser
npm install @babel/generator
npm install @babel/traverse        
npm install @babel/types

代码:

/*
* 控制流平坦化 if语句转 switch* */
function del_code(name, consequent) {
    // 删除合并分支后多余的 赋值和break代码
    let assignment_bool, break_bool;
    for (let i = consequent.length - 1; i >= 0; i--) {
        if (consequent[i].type === "BreakStatement") {
            if (break_bool) {
                consequent.splice(i, 1);
            } else {
                break_bool = true;
            }
        } else if (consequent[i].type === "ExpressionStatement" && consequent[i].expression.type === "AssignmentExpression" && consequent[i].expression.left.name === name) {
            if (assignment_bool) {
                consequent.splice(i, 1);
            } else {
                assignment_bool = true;
            }
        }
    }
}

function merge_branch(name, key, cases_dict) {
    // 用于递归合并 switch 分支
    let {consequent} = cases_dict[key];
    let value = -1;
    for (let i in consequent) {
        if (consequent[i].type === "ExpressionStatement" && consequent[i].expression.type === "AssignmentExpression" && consequent[i].expression.left.name === name && consequent[i].expression.right.type === "NumericLiteral") {
            value = consequent[i].expression.right.value;
            break;
        }
    }

    if (value !== -1 && cases_dict.hasOwnProperty(value)) {
        del_cases_dict[value] = 1;
        return consequent.concat(merge_branch(name, value, cases_dict));    // 继续下一分支的合并
    }
    return consequent;
}

const fs = require('fs');
const {parse} = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const generator = require("@babel/generator").default;
const types = require("@babel/types");

const js_code = fs.readFileSync("./test.js", 'utf8');
const ast_code = parse(js_code);

switch_cases_dict = {};
break_node = types.breakStatement();

traverse(ast_code, {
    'IfStatement': {
        enter(path) {
            var name = path.node.test.left.name;
            if (path.node.test.operator === "===") {  // 如果判断符号是 ===
                if (switch_cases_dict[name] === undefined) {
                    switch_cases_dict[name] = [];
                }
                path.node.consequent.body.push(break_node);
                switch_cases_dict[name].push(types.switchCase(path.node.test.right, path.node.consequent.body));

                if (path.node.alternate.type === 'BlockStatement') {
                    path.node.alternate.body.push(break_node);
                    let num = path.node.test.right.value + 1;
                    switch_cases_dict[name].push(types.switchCase(
                        types.numericLiteral(num),
                        path.node.alternate.body,
                    ));
                }
            }
        },
        exit(path) {
            var name = path.node.test.left.name;
            if (path.parentPath.parentPath.type === "WhileStatement" && switch_cases_dict[name].length !== 0) {
                console.log(name, "if 已替换 switch");
                path.replaceWith(types.switchStatement(
                    discriminant = types.identifier(name),
                    cases = switch_cases_dict[name]
                ));
            }
        }
    },
    'SwitchStatement': {
        enter(path) {
            del_cases_dict = {}; // 待删除的 分支语句
            let cases_dict = {};
            let cases_list = path.node.cases;
            let {name} = path.node.discriminant;
            if (switch_cases_dict.hasOwnProperty(name)) {
                console.log(name, "switch 分支合并");
                for (let i in cases_list) {
                    cases_dict[cases_list[i].test.value] = cases_list[i]
                }

                for (let key in cases_dict) {   // 合并分支并删除多余代码
                    cases_dict[key].consequent = merge_branch(name, key, cases_dict);
                    del_code(name, cases_dict[key].consequent);
                }
                for (let key in del_cases_dict) {
                    delete cases_dict[key]; // 删除多余分支
                }
                path.node.cases = Object.values(cases_dict);
            }
        },
    }
})

// console.log(generator(ast_code).code)
fs.writeFileSync("./demo.js", generator(ast_code).code, 'utf8')

注:以上js解出来的代码在某些分支会出现多次 return 语句,当然,并不影响运行。我暂时没找到出现这种问题的原因在哪里,如果您找到请务必和我说下,另您也可以在 del_code 函数中删除多的 return 语句

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/461942.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

分布式文件存储与数据缓存(一)| FastDFS

目录 分布式文件系统FastDFS概述_简介FastDFS特性:分布式文件服务提供商 FastDFS概述_核心概念trackerstorageclientgroup FastDFS概述_上传机制内部机制如下 FastDFS概述_下载机制内部机制如下 FastDFS环境搭建_Linux下载安装gcc下载安装FastDFS下载安装FastDFS依赖…

c语言的字符串函数详解

文章目录 前言一、strlen求字符串长度的函数二、字符串拷贝函数strcpy三、链接或追加字符串函数strcat四、字符串比较函数strcmp五、长度受限制字符函数六、找字符串2在字符串1中第一次出现的位置函数strstr七、字符串切割函数strtok(可以切割分隔符)八、…

THM学习笔记—RootMe

nmap扫描,发现22端口和80端口打开 dirsearch扫描,注意到/panel和/uploads,在浏览器中打开 可以上传文件,尝试反弹shell 在尝试过程中发现网站不能上传.php文件,只需要将后缀更改为.php5之类即可 成功 查找文件&#x…

页面事件

下拉刷新事件 1. 什么是下拉刷新 下拉刷新是移动端的专有名词,指的是通过手指在屏幕上的下拉滑动操作,从而重新加载页面数据的行为。 2. 启用下拉刷新 启用下拉刷新有两种方式: ① 全局开启下拉刷新  在 app.json 的 window 节点中&…

Docker常用命令的使用及镜像的构建

1.docker的好处 在开发中可能会遇到一个问题,一个程序在自己电脑上能跑,但是换到服务器上就不行了。如果我们重新搭建环境,需要重新部署mysql,es,redis等组件很麻烦。有了docker之后,我们可以快速完成项目的部署。同时docker的隔…

MyBatis3源码深度解析(十二)MyBatis的核心组件(一)Configuration

文章目录 第四章 MyBatis的核心组件4.1 使用MyBatis操作数据库4.2 MyBatis核心组件4.3 Configuration组件4.3.1 属性4.3.2 设置4.3.3 类型别名4.3.3 类型处理器4.3.5 对象工厂4.3.6 插件4.3.7 配置环境4.3.8 映射器 第四章 MyBatis的核心组件 4.1 使用MyBatis操作数据库 在研…

《操作系统实践-基于Linux应用与内核编程》第10章-Linux综合应用

前言: 内容参考《操作系统实践-基于Linux应用与内核编程》一书的示例代码和教材内容,所做的读书笔记。本文记录再这里按照书中示例做一遍代码编程实践加深对操作系统的理解。 引用: 《操作系统实践-基于Linux应用与内核编程》 作者:房胜、李旭健、黄…

网络通信与网络协议

网络编程是指利用计算机网络实现程序之间通信的一种编程方式。在网络编程中,程序需要通过网络协议(如 TCP/IP)来进行通信,以实现不同计算机之间的数据传输和共享。在网络编程中,通常有三个基本要素 IP 地址:定位网络中某台计算机端口号port:定…

Pyqt5中,QGroupBox组件标题字样(标题和内容样式分开设置)相对于解除继承

Python代码示例: import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QGroupBox, QLabelclass MyApp(QWidget):def __init__(self):super().__init__()# 创建一个 QVBoxLayout 实例layout QVBoxLayout()# 创建 QGroupBox 实例self.grou…

【中等】保研/考研408机试-二叉树相关

目录 一、基本二叉树 1.1结构 1.2前序遍历(注意三种遍历中Visit所在的位置) 1.2中序遍历 1.3后序遍历 二、真题实战 2.1KY11 二叉树遍历(清华大学复试上机题)【较难】 2.2KY212 二叉树遍历二叉树遍历(华中科技大…

王道机试C++第8章递归与分治 Day35和蓝桥杯两道真题程序

第 8 章 递归与分治 递归是指:函数直接或间接调用自身的一种方法,通常可把一个复杂的大型问题层层转化为与原问题相似但规模较小的问题来求解。 递归策略只需少量的程序就可描述解题过程所需的多次重复计算,因此大大减少了程序的代码量。 8.…

OLED 菜单操作

本次介绍一款中景园带字库的OLED显示屏,并基于该模块描述一种菜单操作方法,能够极大的减少显示界面开发工作量。 使用的2.08寸OLED显示屏,字库芯片为GT30L32S4W,支持多种字号中英文。 官方提供了很完善的参考资料,包括…

结构体联合体枚举和位段

文章目录 结构体结构体类型的声明特殊的声明 结构的自引用结构体变量的定义和初始化结构体内存对齐为什么要内存对齐结构体传参结构体实现位段(位段的填充&可移植性)位段位段的内存分配空间如何开辟位段的跨平台问题位段的应用 枚举枚举类型的定义枚…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:Column)

沿垂直方向布局的容器。 说明: 该组件从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 子组件 可以包含子组件。 接口 Column(value?: {space?: string | number}) 从API version 9开始,该接口…

vscode 生成树状图工具:project-tree

按下快捷键“CtrlShiftP”, 在弹框中输入 Project Tree,然后敲回车即会在根目录自动生成README.md(如果之前没有的话)。

pytorch 入门基础知识二(Pytorch 02)

一 微积分 1.1 导数和微分 微分就是求导: %matplotlib inline import numpy as np from matplotlib_inline import backend_inline from d2l import torch as d2l def f(x):return 3 * x ** 2 - 4 * x 定义: 然后求 f(x) 在 x 1 时的导数&#xff…

HarmonyOS NEXT应用开发—折叠屏音乐播放器方案

介绍 本示例介绍使用ArkUI中的容器组件FolderStack在折叠屏设备中实现音乐播放器场景。 效果图预览 使用说明 播放器预加载了歌曲,支持播放、暂停、重新播放,在折叠屏上,支持横屏悬停态下的组件自适应动态变更。 实现思路 采用MVVM模式进…

【Algorithms 4】算法(第4版)学习笔记 18 - 4.4 最短路径

文章目录 前言参考目录学习笔记0:引入介绍1:APIs1.1:API:加权有向边1.2:Java 实现:加权有向边1.3:API:加权有向图1.4:Java 实现:加权有向图1.5:AP…

Unity类银河恶魔城学习记录10-12 p100 Improve aliments - chill源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释,可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili CharacterStats.cs using System.Collections; using System.Collections…

docker容器镜像管理

目录 一、 Docker的基本组成 二、 容器和镜像的关系 2.1 面向对象角度 2.2 从镜像容器角度 三、镜像命令 3.1 查看当前已有镜像 3.2 查看已有的全部镜像 3.3 查看镜像ID 3.4 镜像删除 四、 容器命令 4.1 下载镜像 4.2 新建和启动容器 run 4.3 交互式 4.…