Rhino - 有没有办法在不重新解析代码的情况下将修改后的 AST 恢复为原始 AST?
Rhino - Is there a way to restore the modified AST to the original one without parsing the code again?
我正在为 JavaScript 实施突变测试工具。
修改 AST 并针对修改后的代码执行测试用例。
在 运行 个测试用例之后,我想将修改后的 AST 恢复为原始的,以便我可以重复变异过程。
但是,我不知道如何恢复它。有帮助吗?
我不是特别了解 Rhino,所以我不知道它是否为此提供了特定的帮助。但一般来说,您可以直接自己做,通过跟踪您所做的更改,如 "anti-changes"。该方案适用于任何 AST 系统,而不仅仅是 Rhino。
树由节点以及节点与其子节点之间的关系组成。
要构造一棵树,您可以执行命令来创建一个节点,创建它的子节点,然后 link 将它们放在一起。当然,Rhino API 对此提供了原始支持。
以后要重新构建树,我们只需根据节点构建列出要完成的操作列表,子节点 connection/disconnection.
想象一下下面的树:
1:*
/ \
2:+ 3:[]
/ \ / \
4:x 5:17 6:a 7:i
我已将节点标记为 n:t,其中 n 是节点编号,t 是节点类型或文字值。我们将节点的子节点从左到右编号为 1、2、...
现在我们修改 ("mutate") 树,将 2:+ 替换为新节点 8:mod,将 5:17 替换为 9:j。我们这样做的动作是抽象的,顺序是:
disconnect(2,1); // disconnect node N from its Mth child
disconnect(2,2);
disconnect(1,1);
delete(2);
delete(5);
n1=create(mod); // node 8
n2=create(j); // node 9
connect(1,1,n1); // connect node 1 child 1 to n1
connect(n1,1,4);
connect(n1,2,n2);
我们将反向操作记录在一个(交易)"undo"列表中以供稍后处理:
[ [disconnect,n1,2],
[disconnect,n1,1],
[disconnect,1,1],
[delete,n2],
[delete,n1],
[create,n5,17],
[create,n2,+],
[connect,1,1,n2],
[connect,2,2,n5],
[connect,2,1,4] ]
这个列表可以 "executed" 通过按顺序遍历元素并简单地用简单的解释器做元素所说的。
而且构造简单;对于每个树变异操作,我们将一个新的逆操作推到撤消列表的前面。我们可以通过用树变异和记忆逆操作替换我们的树变异操作来使这变得容易,例如,
fn disconnect_and_remember_inverse(node,child) {
push(undo_list,[connect,node,child,nth_child(node,child)]);
disconnect(node,child);
}
在使用 "delete" 操作的地方,逆运算将重新创建相应的节点类型,或者,如果您 always 恢复树,
只是不要删除节点并将其记住在 "undo" 列表中:
[ [disconnect,n1,2],
[disconnect,n1,1],
[disconnect,1,1],
[delete,n2],
[delete,n1],
// [create,n5,17],
// [create,n2,+],
[connect,1,1,2],
[connect,2,2,5],
[connect,2,1,4] ]
这可能会给您带来麻烦的地方是 Rhino 实现的复合树粉碎操作;您显然希望这些操作调用这些 remember_inverse 过程。那可能不方便;你会
必须用您自己的等效程序复制这些程序。
留给 reader 的细节和微妙之处。 (我也不是 JavaScript 程序员,所以请原谅我犯下的任何语法错误)。
我正在为 JavaScript 实施突变测试工具。 修改 AST 并针对修改后的代码执行测试用例。 在 运行 个测试用例之后,我想将修改后的 AST 恢复为原始的,以便我可以重复变异过程。 但是,我不知道如何恢复它。有帮助吗?
我不是特别了解 Rhino,所以我不知道它是否为此提供了特定的帮助。但一般来说,您可以直接自己做,通过跟踪您所做的更改,如 "anti-changes"。该方案适用于任何 AST 系统,而不仅仅是 Rhino。
树由节点以及节点与其子节点之间的关系组成。
要构造一棵树,您可以执行命令来创建一个节点,创建它的子节点,然后 link 将它们放在一起。当然,Rhino API 对此提供了原始支持。
以后要重新构建树,我们只需根据节点构建列出要完成的操作列表,子节点 connection/disconnection.
想象一下下面的树:
1:*
/ \
2:+ 3:[]
/ \ / \
4:x 5:17 6:a 7:i
我已将节点标记为 n:t,其中 n 是节点编号,t 是节点类型或文字值。我们将节点的子节点从左到右编号为 1、2、...
现在我们修改 ("mutate") 树,将 2:+ 替换为新节点 8:mod,将 5:17 替换为 9:j。我们这样做的动作是抽象的,顺序是:
disconnect(2,1); // disconnect node N from its Mth child
disconnect(2,2);
disconnect(1,1);
delete(2);
delete(5);
n1=create(mod); // node 8
n2=create(j); // node 9
connect(1,1,n1); // connect node 1 child 1 to n1
connect(n1,1,4);
connect(n1,2,n2);
我们将反向操作记录在一个(交易)"undo"列表中以供稍后处理:
[ [disconnect,n1,2],
[disconnect,n1,1],
[disconnect,1,1],
[delete,n2],
[delete,n1],
[create,n5,17],
[create,n2,+],
[connect,1,1,n2],
[connect,2,2,n5],
[connect,2,1,4] ]
这个列表可以 "executed" 通过按顺序遍历元素并简单地用简单的解释器做元素所说的。
而且构造简单;对于每个树变异操作,我们将一个新的逆操作推到撤消列表的前面。我们可以通过用树变异和记忆逆操作替换我们的树变异操作来使这变得容易,例如,
fn disconnect_and_remember_inverse(node,child) {
push(undo_list,[connect,node,child,nth_child(node,child)]);
disconnect(node,child);
}
在使用 "delete" 操作的地方,逆运算将重新创建相应的节点类型,或者,如果您 always 恢复树, 只是不要删除节点并将其记住在 "undo" 列表中:
[ [disconnect,n1,2],
[disconnect,n1,1],
[disconnect,1,1],
[delete,n2],
[delete,n1],
// [create,n5,17],
// [create,n2,+],
[connect,1,1,2],
[connect,2,2,5],
[connect,2,1,4] ]
这可能会给您带来麻烦的地方是 Rhino 实现的复合树粉碎操作;您显然希望这些操作调用这些 remember_inverse 过程。那可能不方便;你会 必须用您自己的等效程序复制这些程序。
留给 reader 的细节和微妙之处。 (我也不是 JavaScript 程序员,所以请原谅我犯下的任何语法错误)。