AST解web控制流平坦化

news2024/11/18 14:40:50
  • 此代码可以解决大部分 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 语句

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

jenkins使用公共库问题

Jenkins解决上编译解决引用问题 本地运行 把公共库创建链接到指定项目目录下即可 mklink /d /j D:\codepath\xxxx\yyyyy\tool_base D:\codepath\tool_base

Learn OpenGL 14 混合

混合 OpenGL中,混合(Blending)通常是实现物体透明度(Transparency)的一种技术。透明就是说一个物体(或者其中的一部分)不是纯色(Solid Color)的,它的颜色是物体本身的颜色和它背后其它物体的颜色的不同强度结合。一个有色玻璃窗是…

python自动化之(django)(2)

1、创建应用 python manage.py startapp apitest 这里还是从上节开始也就是命令行在所谓的autotest目录下来输入 然后可以清楚的看到 多了一个文件夹 2、创建视图 在views中加入test函数(所建应用下) from django.http import HttpResponse def tes…

【STM32定时器 TIM小总结】

STM32 TIM详解 TIM介绍定时器类型基本定时器通用定时器高级定时器常用名词时序图预分频时序计数器时序图 定时器中断配置图定时器定时 TIM介绍 定时器(Timer)是微控制器中的一个重要模块,用于生成定时和延时信号,以及处理定时事件…

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

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

复现文件上传漏洞

一、搭建upload-labs环境 将下载好的upload-labs的压缩包,将此压缩包解压到WWW中,并将名称修改为upload,同时也要在upload文件中建立一个upload的文件。 然后在浏览器网址栏输入:127.0.0.1/upload进入靶场。 第一关 选择上传文件…

一命通关差分

本章节是前缀和的延申 一命通关前缀和-CSDN博客https://blog.csdn.net/qq_74260823/article/details/136530291?spm1001.2014.3001.5501 一命通关前缀和 公交车 引入 还是利用我们在前缀和中所采用的例子——公交车。 有一辆公交车,一共上下了N批乘客&#xff1a…

SMART PLC 卷径计算(圈数检测+膜厚叠加法)

1、卷径计算(膜厚叠加+数值积分器应用博途PLC SCL代码) https://rxxw-control.blog.csdn.net/article/details/136719982https://rxxw-control.blog.csdn.net/article/details/1367199822、膜厚叠加法 https://rxxw-control.blog.csdn.net/article/details/128600466

[c++] std::future, std::promise, std::packaged_task, std::async

std::promise 进程间通信,std::packaged_task 任务封装,std::async 任务异步执行;std::future 获取结果。 1 std::promise 1.1 线程间同步 std::promise 可以用于线程间通信。 如下代码是 std::promise 中的示例代码。 std::promise - cp…

我的NPI项目之设备系统启动(九) -- 高通平台启动阶段和功能详解

接触到一个新的平台最终要的一件事莫过于搞清楚如何boot起系统,那就要弄清楚系统开机各阶段的具体工作内容。这里最好的指导就是高通的启动guide。 对于系统的镜像和启动阶段我已经做了简单的介绍,详见: 镜像和启动阶段的说明 那今天我就以电源为例&a…

树的初步了解及堆,堆的topk问题,堆排序

目录 前言 一:树 1.树的概念 2.树的基础概念知识 3.在树中孩子节点和父节点知一求一 4.树的表示方法 二:二叉树 1.二叉树的概念 2.二叉树的特性 3.满二叉树 4.完全二叉树 三:堆 1.堆的定义 2.堆的实现:数组实现…

牛客网-SQL大厂面试题-2.平均播放进度大于60%的视频类别

题目:平均播放进度大于60%的视频类别 DROP TABLE IF EXISTS tb_user_video_log, tb_video_info; CREATE TABLE tb_user_video_log (id INT PRIMARY KEY AUTO_INCREMENT COMMENT 自增ID,uid INT NOT NULL COMMENT 用户ID,video_id INT NOT NULL COMMENT 视频ID,start…

【小白刷leetcode】第15题

【小白刷leetcode】第15题 动手刷leetcode,正在准备蓝桥,但是本人算法能力一直是硬伤。。。所以做得一直很痛苦。但是不熟练的事情像练吉他一样,就需要慢速,多练。 题目描述 看这个题目,说实在看的不是很懂。索性我们直…

YOLOv9算法原理——使用可编程梯度信息学习想要学习的内容

前言 2023年1月发布YOLOv8正式版后,经过一年多的等待,YOLOv9终于面世了!YOLO是一种利用图像全局信息进行目标检测的系统。自从2015年Joseph Redmon、Ali Farhadi等人提出了第一代模型以来,该领域的研究者们已经对YOLO进行了多次更…

OceanBase4.2版本 Docker 体验

📢📢📢📣📣📣 哈喽!大家好,我是【IT邦德】,江湖人称jeames007,10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】!😜&am…

文件包含例子

一、常见的文件包含函数 php中常见的文件包含函数有以下四种: include() require() include_once() require()_once() include与require基本是相同的,除了错误处理方面: include(),只生成警告(E_WARNING)&#x…

Unity Live Capture 中实现面部捕捉同步模型动画

Unity Face Capture 是一个强大的工具,可以帮助你快速轻松地将真实人脸表情捕捉到数字模型中。在本文中,我们将介绍如何在 Unity Face Capture 中实现面部捕捉同步模型动画。 安装 |实时捕获 |4.0.0 (unity3d.com) 安装软件插件 安装 Live Capture 软件…

数据资产管理解决方案:构建高效、安全的数据生态体系

在数字化时代,数据已成为企业最重要的资产之一。然而,如何有效管理和利用这些数据资产,却是许多企业面临的难题。本文将详细介绍数据资产管理解决方案,帮助企业构建高效、安全的数据生态体系。 一、引言 在信息化浪潮的推动下&a…

为什么单线程的 Redis 能那么快?

大家好我是苏麟 , 给大家找一些好的文章看看 . 原文文章 : 03 高性能IO模型:为什么单线程Redis能那么快? (lianglianglee.com) Redis 为什么用单线程? 要更好地理解 Redis 为什么用单线程,我们就要先了解多线程的开销。 多线程的…

力扣hot100:416.分割等和子集(组合/动态规划/STL问题)

组合数问题 我们思考一下,如果要把数组分割成两个子集,并且两个子集的元素和相等,是否等价于在数组中寻找若干个数使之和等于所有数的一半?是的! 因此我们可以想到,两种方式: ①回溯的方式找到t…