227还原实战(六)控制流专题

合并三元节点

  • ==在经过了前面的合并第一节点后,之后的所有节点都是处于一个最简的形式,至于什么算最简的形式,后续会体现出来==

  • ==什么是最简形式?==

    • ==最简形式就是将所有被调用控制流只有 1 的全部合并,但是并不包括在三元里的,因为它无法合并,这边合并了另一边就合并不了,所以要经过处理,也就是现在这里,合并三元节点==

  • ==当我们合并了三元节点后,又会重新出现可以合并的最简节点,不过我们在 while true 这个循环中进行了处理(next_num != -1 这里)==

  • 一样的套路,但这里没有用 pop 的原因是,后面会在 num_key 中重新添加 key

末尾节点处理

  • "un" "re" 就代表当前控制流已经合并到最后了,往下合并不了,所以直接 添加-删除-结束

单一节点处理

  • 此时 next_num 一定是数字,所以我们直接从两个对象中取出对应的对象

  • 然后判断 num 是不是小于2(即只调用一次),并且还要判断当前 while 循环是不是第一次(即没有走 handle_conditional 方法)

三元节点处理
  • ==处理三元的时候我们要把所有的节点都当作最简的节点来看待,只有这样我们才能完美的还原所有的控制流==

  • 首先取出代码块最后一句,然后取出这个三元的 判断语句(test),正确的数字(co),错误的数字(al)

  • 传入参数

    • red 当前控制流的代码块

    • value 当前控制流的数字

    • test 当前控制流末尾三元的判断语句

    • con 判断正确的控制流数字

    • alt 判断错误的控制流数字

    • num_cont 用于同步修改对象

    • new_num_cont、num_key 同上

    • end_code 为末尾的三元表达式

  • 返回参数

    • red 为处理完毕的代码块

    • next_num 下一个指向的数字

    • num_cont、new_num_cont、num_key 同步修改

主要还原方法 handle_conditional

  • co_code 为从对象中取出正确控制流的对象

  • al_code 为从对象中取出错误控制流的对象

  • co_next 为正确分支执行完后指向的下一个控制流数字

  • al_next 为错误分支执行完后指向的下一个控制流数字

  • co_red 为正确分支的代码块

  • al_red 为错误分支的代码块

  • co_num 为正确分支所被指向的次数

  • al_num 为错误分支所被指向的次数

  • sta 用于接收最终将代码处理完毕的语句,方便判断前面的判断有没有执行成功的

  • next_num 为处理完毕后指向的下一个控制流数字

  • co_judge 用来判断是否在 num_key, num_cont, new_num_cont 对象中删除 co 的 key

  • al_judge 用来判断是否在 num_key, num_cont, new_num_cont 对象中删除 al 的 key

while / while true 还原
  • 首先我们先来看 while 和 while true 在控制流中的结构==(注意这里并不是最简的形式,则没有通过合并单一节点)==

  • while 的形成

    • 可以看到,在上面的控制流中,while 和 while true 都是有一个入口的

    • 当进行一些判断后==重复调用这个入口==,就会形成一个循环

    • 判断==不符合==后就会==调用出口==跳出该循环

  • 两种 while 的不同

    • 入口不同,可以看到 while 的入口 0 只有一个==三元表达式==

    • 而 while true 的入口 3 是先赋值,然后再走的三元表达式

  • 根据上面 while 的形成我们就可以判断出 while 和 while true 两种语句

  • 在判断前我们先看一下通过==合并了单一节点==处理后的控制流,以免后面看不懂

while ==(这个就是最简形式)==

  • 2 初始化参数

  • 0 while 循环的判断语句(while 循环入口/出口)

  • 1 while 循环里面的代码块

  • 3 while 循环结束后执行的代码块

  • 可以看到想要判断 while 循环的话,while 循环的判断有一段所走的数字会指回它本身

    • 什么意思呢?0 number < index ? 1 : 3, 1 走完后是不是必定指回 0,而 3 就是结束循环

    • 然后根据我们之前==将代码块与控制流数字进行对应==中,把每个控制流对应的下一个控制流都存储在了 next_num 里面

    • 所以我们需要判断是不是 while 循环就只需要判断三元的其中一个分支是否指向它本身就可以了

      如:0 =\=> next_num = -1; 1 =\=> next_num = 0; 3 =\=> next_num = undefined;

      我们进入 0 然后判断 1 和 3 的 next_num 是否等于 0 就可以判断出他是不是while 循环

while true ==(这个就是最简形式)==

  • 可以看到没什么变化

  • 1 初始化参数

  • 2 while true 循环结束后执行的代码块

  • 3 while true 循环的判断语句(while true循环入口/出口)

  • 4 while true 循环里面的代码块

  • 可以看到和 while true 基本上是一模一样的,就是判断语句那块不同而已

  • 所以 while true 的判断方式和 while 的判断方式是一模一样的,但是要进行细分,也就是判断三元这里的代码有多少句,一句就是 while 两句就是 while true

  • while true 还有一种就 break 在屁股后面的,但实际上都是一样的判断方式,这里就不一一例举了

代码写法

  • 首先判断其中一个的分支的下一个指向是不是指向自己 即==value =\= co_next or value =\= al_nex==

  • 然后将当前控制流的调用次数减一

    • 因为我们在还原它,而还原的代码里面,会有一次指向它本身,所以要进行减一

  • co_judge = true 是后续为了统一删除这个控制流在 num_cont 里的数据

  • red.length != 0 就是为了区分 while 和 while true

    • 我们前面调用 handle_conditional 方法前,传入的 red 是进行了 pop 的,所以 while 和 while true 的长度就会变成 1 - 1=0 和 2 - 1 = 1

    • 所以这里就是用的 != 0 而不是 != 1

  • while true(可以结合上面的控制流来看)

    • 构建 if break 语句,这里用的 test 就是当前三元的 test,然后要进行一个取反,因为 true 是继续运行 false 是跳出,我们为了让代码看的简洁一些就直接将他取反,然后放入 break 语句,最终就会构成这样的一个 if 语句 =\==> if (!test) break;

    • 将构建好的语句添加到 red 中,之后将 co 里的代码也添加到 red 中

    • 然后使用 red 来构建 while true 循环==注意,只要是要将代码块构建成一个语句都必须走 map_statement 这个方法来套 expressionStatement 因为我们前面为了方便判断是将代码从 expressionStatement 里取出来的(在 if 转 swicth + 存储 if 里)后续会有很多调用这个方法的地方,后面就不会说明了==

    • 最后用 [] 包裹构建好的语句,重新赋值给 red,方便后续进一步的处理

  • while

    • 这里就比较简单,直接使用当前三元的 test 和 co 里的代码构建一个 while 语句

    • 然后添加到 red 当中即可

  • 后续处理

    • al_num < 2 这里不直接添加,加了个判断是为了不会添加错误

    • 因为有些地方是一种嵌套的形式 =\==> while (test) { while (test2) { }}

    • 判断成功后,我们就可以将 al_judge = true,将 al 的代码添加到 red 中,之后修改 next_num 为 al 的 next_num 就完成了

    • 判断失败我们就将 next_num 修改成 al 就好

    • 修改是因为我已经将当前三元还原完了,它的下一个指向就肯定不会是原来的,要进行重新修改

  • value =\= al_next 这里和上面是一样的,只不过是相反的,不详细说==后面的所有还原基本都会有相反的,就不一一细说了,对着看就可以了==

  • 至此 while / while true 还原完毕

if 还原

控制流结构==最简(后续都是最简,就不注明了)==

  • 可以看到,在控制流中 if 语句的特殊点就是,其中一边走完必定会指向另一边

  • 例如: 0 =\==> next_num 2; num = judge ? 0 : 2; 0.next_num === 2

  • 所以我们判断 if 语句就非常的简单,只需要判断其中一条分支的下一个指向是指向另一条分支就好

代码实现

  • 判断成功后,直接使用当前三元的==判断语句==和对应的==代码块==构成一个 if 语句

  • 然后将构建好的 if 语句添加到 red 中

  • 然后判断另一个分支被指向次数是否 < 3

    • 在 if 语句中,最多只会有两个指向另一边

      • 一个是三元中的指向 num = judge ? 0 : 2; 2 =\==> 1

      • 另一个则是其中一个分支走完后的指向 num = 2; 2 =\==> 2

      • 所以总共两个

  • < 3

    • al_judge = true 删除,因为添加了,只要添加到 red 中就会删除掉==后续除特殊情况都不会再单独说明==

    • red 添加另一个分支的代码

    • 然后更新当前控制流的下一个指向

  • > 3

    • 这就代表了它是一个嵌套的类型,不能直接合并,要其他节点处理完毕后才能合并

    • 在 num_cont 或 new_num_cont 中将 num 减一

      • 因为这里已经合并了一次 if 语句,那么就会少一个指向==(其中一个分支走完就会指向另一个的指向,if 语句内)==

    • 将当前控制流的下一个指向改为 al

  • 至此 if 语句还原完毕

if else 还原

  • 可以看到 if else 在控制流中的特点就是,两个分支,最终会指向都同一个分支

  • 例如: 0 =\==> next_num = 2; 3 =\==> next_num = 2;

  • 所以要判断它也非常的简单,两个 next_num 相同就可以了

代码实现

  • ==这里做了个判断,只有第二个分支被指向次为 1 才删除,至于为什么会有后续看一个特殊情况就明白了==

  • 构建 if else 语句

  • 在 red 中添加

  • 当两个都删除,才进行下一步处理

    • 都删除

      • 获取下一个控制流的对象

      • 判断这个对象是否只被指向两次(即 if else 的两次)

          • 将这个对象的代码添加到 red 中,并更新 next_num

          • 之后将下一个控制流对象在 num_cont 、new_num_cont 和 num_key 中删除

          • 将这个对象的 num 减一

          • if else 合并后,将两个指向变为了一个指向

          • 将当前三元的下一个指向更新为合并后的指向(al_next, co_next 都行)

    • 只删除了 co

      • 将 al 的 num 减一

      • 更新指向为 al_next

        • if else 的两个指向必定是指向同一个的,要是更新的指向指回 al 就不是 if else 了,而是 if

    • 特殊情况的图

    • 可以看到这里很多 if else 的 else 都是 x.push(0); 所以就会出现 al 的 num > 3 的情况,而且这种情况还不能全部合为一个

  • 至此 if else 合并完毕

特殊情况(嵌套)
  • 到了现在这个位置,如果还是没有成功合并节点(sta 不为空),那么就只剩下两种情况了

    • 一是嵌套==(即其中一个分支指向的下一个控制流是 -1)==

    • 二是其中一个分支中的下一个是 un 或者 re 的

嵌套还原 coNestCo

  • 传入参数不细说,上面认真看了,这里都知道传了什么

  • 不过要注意,他是有两个分支,所以另一边也需要进行处理,传入的参数是相对相反的

  • 我们传入参数时,都是确保了第一个传入的 code 他的下一个指向是 -1,就代表了 code 的最后一个是 三元,所以这边就做和之前一模一样的操作

    • pop

    • 取出相应的 next_num, num, red

  • 这里定义的参数 (let sta...) 和上一个方法的也是差不多的,不多解释,唯一不同的就是 minus_num ,这个后面会讲

while 里面有 break

  • while break 的形成

    • 可以看到,要形成这种类型的语句,通常都会有==2 个三元(多个也没关系,再多都会有对应的类型合成,最终还是会变成两个)==

    • 而且这两个三元是有联系的

      • 比如 3 =\==> 0 : 2; 0 =\==> 2 : 3; 他们两个存在一个==相互调用的关系==,且==有一个分支是相同的==

      • 3 指向 0 代表继续循环, 3 指向 2 代表结束循环

      • 0 指向 3 代表继续循环, 0 指向 2 代表结束循环

  • 通过这一特征我们就可以分辨出 while break 语句

while break

  • 1 初始化参数

  • 3 while 循环入口出口

  • 0 while 循环的代码块 + 出口

  • 2 while 循环结束的语句

  • 可以看到,想要判断它的话,我们只需要确保 0 中的其中一个 指向 3 另一个的指向和 3 里的其中一个相同

代码实现

  • 首先将 value 的 num 减一

    • value 为上一个的控制流

  • 在 co_red 中添加 break

    • 因为是判断正确就跳出 while ,所以需要在 red 中添加 break 语句

  • 使用当前控制流的 判断语句 构建 if 语句

  • 在上一个控制流指向 -1 的分支中的代码块加入刚刚构建好的语句,然后再添加当前控制流另一个分支中的代码

  • 最后使用刚刚构建好的代码块,通过上一个控制流的 判断语句 来构成一个 while 语句

  • 之后再在 red 中添加构建好的 while 语句

  • 然后我们判断 num2 最后的这个 控制流 是否要合并

    • < 3 则就是 为 2 或者 1,2 是有个 break 的指向 + 入口的指向

    • 需要合并就删除掉 num2 然后添加就好

  • 不需要合并就将其调用的次数减一(break 位置的调用)

  • 最后更新 next_num

  • 至此 while break 还原完毕

if 嵌套, if else 嵌套(嵌套内容 if else)

if 中嵌套 if else

if else 中嵌套 if else (嵌套内容 if else)

  • 可以看出,两种表达基本上没什么区别

    • 一种是走完 B 后==会走== A2

    • 一种是走完 B 后==不走== A2

  • 如何判断嵌套

    • 一 最显著的就是,有两个三元

    • 二 一个三元的其中一个分支会指向另一个三元

    • 三 被指向的三元的分支,最终会指向同一个分支

    • 最终指向的同一个分支会等于 指向三元的另一个分支(即形成 B 走 A2)

    • 最终指向的同一个分支会等于 指向三元的另一个分支的下一个指向(即形成 B 不走 A2)

  • 控制流语句我就不过多讲了。

  • 如何判断?if 嵌套 or if else 嵌套

    • if 相同分支指向 if 的另一个分支

    • if else 相同分支指向 if 的另一个分支的下一个分支

  • 构建 if else 语句

  • 在 code1 中添加刚刚构建好的语句

  • 如果==当前控制流的下一个指向==等于==上一个控制流的另一个指向==,那么就是 if 嵌套 if else

    • 使用上一个控制流的判断语句,和 code1 构建 if 语句

  • 如果==当前控制流的下一个指向==等于==上一个控制流的另一个指向的下一个指向==,那么就是 if else 嵌套 if else

    • 删除上一个控制流的另一个指向

    • 使用 code1 coe2 code_test 构建 if else 语句

  • 在 red 中添加构建好的语句

  • 获取下一个控制流的对象

  • 然后判断这个对象的 num 是否 < 4

    • 里面的 if else 有 两个指向

    • 外面的 if or if else 也有两个

      • ==但是在外面的 if else 中的 if 里的指向是和里面的 if else 指向相同的==

    • 所以就一共有四个

  • < 4

    • 正常添加,更新 next_num

  • > 4

    • 下一个的指向中的 num -2

      • 把里面的两个给减掉,因为已经合并

    • 更新 next_num

  • 至此 if 嵌套,if else 嵌套还原完毕

if 嵌套 (嵌套内容 if, while, if 里有 continue)

  • 这一块其实和上面的 if 里嵌套 if else 是一样的,只不过是吧 if else 变成了 if 和 while ,这里就不详细讲流程了,直接上代码吧

代码实现

  • 判断里面是嵌套 if 还是 while 语句

  • 因为 co == num2 ,那么分支的另一边就是一个完整的语句,但是也是会有特殊的情况(这个特殊情况是另写的,通用性不高)

    • 目前就看到一种

    • continues, 因为上面我们已经吧 break 给全部排除了,剩下的就只有 continues 了

    • 这里我的判断是包含的语句为 0 ,也就是啥都没有走就跳到下一步,但实际情况是会有走了一些语句再跳,这里我没写(懒)

    • 这里给看一下这种特殊的情况

  • 然后后面的合并就不细讲了,前面的认真看了,这里就知道是在做什么

while 里 有 break 2

  • 这里其实也是一种特殊情况,特殊情况需要特殊处理,而且这个的通用性也不高,感兴趣的可以自己去看来理解

  • 处理好的 js 可以搜索下面这个来看,方便理清处理的思路

结尾处理

  • 没有下一个的控制流数字,就代表上面的处理都不成功,则还没到达最底层,直接返回

  • 删除 num,到达了这个位置,代表上面 100% 有处理成功的,所以就要删除 num,因为它是头,已经合并了用不上了

  • delete_num 就是吧需要删除的分支给删除,不细讲 so easy

  • 最后返回处理好的参数

至此,嵌套还原结束!!

re 和 un 的处理(美化)

  • 当语句没处理过时和 co 的下一个为 re 或者 un 时,进行处理

    • 只判断 co 是因为 al 为 re 或者 un 时,代表了是这一块控制流到这就基本上是结束了,而 co 不一定,它可能是嵌在一些语句中而已

    • 因此 al 不需要判断,只需要处理 co 这一块就行

  • 当另一个分支的下一个也是 re 时,就代表了两边都是结束了

    • 这时候就需要进行处理,也就是美化

    • 这里的话是看情况来弄的,可以不这样写,也可以这样写,这样写就好看一些

    • 看结果吧,看了就知道为什么这样写了,就不讲了,没啥难点

  • < 2 就很常规了,和其他的一些简单的处理是一样的,无非就是没有指向了就合并,看结果吧

结尾

这里的结尾就和上面的 coNestCo 是一样的

更新 num_cont 对象

结尾

最后会处理成只有一个属性的对象,直接返回就行

  • 顾名思义就是吧处理好的语句,和下一个指向的控制流进行更新,并将当前处理完毕的节点,添加回 num_key 当中

  • === -1

    • 代表了下一个还是三元,只需要修改语句就行

    • 下一个指向存在于 num_cont 并且 指向次数大于 1

      • 代表了还有其他的分支指向它

      • 修改当前的 语句 和 next_num

    • number++ 代表当前语句还可以进行下一步处理,下一步的处理前面有讲

然后将返回的语句,进行替换就可以了

至此,227 三元分支还原完毕

本期还原后代码已放在星球中,有需要自行取用

有想交流或者交个朋友的可以加我

下一期是一些收尾动作,基本上的还原就已经完成了,还望大家给个关注QAQ

let v = Died_in2021

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值