我打算直接在v8代码中实现一个js代码覆盖。
我最初的目标是为抽象语法树中的每个语句添加一个简单的打印。
我看到有一个 AstVisitor 类,它允许您遍历 AST。
所以我的问题是如何在访问者当前访问的语句之后向 AST 添加语句?
最佳答案
好的,我将总结我的实验。首先,我写的内容适用于 V8,因为它在 Chromium 版本 r157275 中使用,因此可能不再有效 - 但我仍然会链接到当前版本中的位置。
如前所述,您需要自己的 AST 访问者,例如 MyAstVisior , 它继承自 AstVisitor 并且必须实现一堆 VisitXYZ那里的方法。唯一需要检测/检查执行代码的是 VisitFunctionLiteral .执行的代码是一个函数或源(文件)中的一组松散语句,V8 将其包装在一个函数中,然后执行。
然后,就在解析的 AST 转换为代码之前,here (从松散的语句中编译函数)和there (在运行时编译,当第一次执行预定义的函数时),您将访问者传递给函数文字,它将调用 VisitFunctionLiteral在访问者上:
MyAstVisitor myAV(info);
info->function()->Accept(&myAV);
// next line is the V8 compile call
if (!MakeCode(info)) {
我通过了 CompilationInfo指针 info给自定义访问者,因为需要它来修改 AST。构造函数如下所示:
MyAstVisitor(CompilationInfo* compInfo) :
_ci(compInfo), _nf(compInfo->isolate(), compInfo->zone()), _z(compInfo->zone()){};
_ci、_nf 和_z 是指向CompilationInfo 的指针, AstNodeFactory<AstNullVisitor>和 Zone .
现在 VisitFunctionLiteral您可以遍历函数体,也可以根据需要插入语句。
void MyAstVisitor::VisitFunctionLiteral(FunctionLiteral* funLit){
// fetch the function body
ZoneList<Statement*>* body = funLit->body();
// create a statement list used to collect the instrumented statements
ZoneList<Statement*>* _stmts = new (_z) ZoneList<Statement*>(body->length(), _z);
// iterate over the function body and rewrite each statement
for (int i = 0; i < body->length(); i++) {
// the rewritten statements are put into the collector
rewriteStatement(body->at(i), _stmts);
}
// replace the original function body with the instrumented one
body->Clear();
body->AddAll(_stmts->ToVector(), _z);
}
在rewriteStatement您现在可以检查语句的方法。 _stmts指针包含一个语句列表,这些语句最终将替换原始函数体。因此,要在每个语句之后添加打印语句,您首先要添加原始语句,然后添加您自己的打印语句:
void MyAstVisitor::rewriteStatement(Statement* stmt, ZoneList<Statement*>* collector){
// add original statement
collector->Add(stmt, _z);
// create and add print statement, assuming you define print somewhere in JS:
// 1) create handle (VariableProxy) for print function
Vector<const char> fName("print", 5);
Handle<String> fNameStr = Isolate::Current()->factory()->NewStringFromAscii(fName, TENURED);
fNameStr = Isolate::Current()->factory()->SymbolFromString(fNameStr);
// create the proxy - (it is vital to use _ci->function()->scope(), _ci->scope() crashes)
VariableProxy* _printVP = _ci->function()->scope()->NewUnresolved(&_nf, fNameStr, Interface::NewUnknown(_z), 0);
// 2) create message
Vector<const char> tmp("Hello World!", 12);
Handle<String> v8String = Isolate::Current()->factory()->NewStringFromAscii(tmp, TENURED);
Literal* msg = _nf.NewLiteral(v8String);
// 3) create argument list, call expression, expression statement and add the latter to the collector
ZoneList<Expression*>* args = new (_z) ZoneList<Expression*>(1, _z);
args->Add(msg);
Call* printCall = _nf.NewCall(_printVP, args, 0);
ExpressionStatement* printStmt = _nf.NewExpressionStatement(printCall);
collector->Add(printStmt, _z);
}
NewCall的最后一个参数和 NewUnresolved是一个数字,指定脚本中的位置。我假设这用于调试/错误消息以告知错误发生的位置。至少我从未遇到过将其设置为 0 的问题(在某处也有一个常量 kNoPosition)。
一些最后的话:这实际上不会在每个语句之后添加打印语句,因为Blocks (例如循环体)是表示语句列表的语句,循环是具有条件表达式和主体 block 的语句。所以你需要检查当前处理的语句类型并递归地查看它。重写 block 与重写函数体几乎相同。
但是当您开始替换或修改现有语句时,您会遇到问题,因为 AST 还带有有关分支的信息。因此,如果您在某些情况下更换跳转目标,就会破坏您的代码。我想如果直接向单个表达式和语句类型添加重写功能而不是创建新的来替换它们,这可能会被覆盖。
到目前为止,我希望它能有所帮助。
关于javascript - 操纵 V8 ast,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15785548/
我正在尝试在我的centos服务器上安装therubyracer,但遇到了麻烦。$geminstalltherubyracerBuildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingtherubyracer:ERROR:Failedtobuildgemnativeextension./usr/local/rvm/rubies/ruby-1.9.3-p125/bin/rubyextconf.rbcheckingformain()in-lpthread...yescheckingforv8.h...no***e
我正在尝试学习Ruby词法分析器和解析器(whitequarkparser)以了解更多有关从Ruby脚本进一步生成机器代码的过程。在解析以下Ruby代码字符串时。defadd(a,b)returna+bendputsadd1,2它导致以下S表达式符号。s(:begin,s(:def,:add,s(:args,s(:arg,:a),s(:arg,:b)),s(:return,s(:send,s(:lvar,:a),:+,s(:lvar,:b)))),s(:send,nil,:puts,s(:send,nil,:add,s(:int,1),s(:int,3))))任何人都可以向我解释生成的
我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的
我有这个:AccountSummary我想单击该链接,但在使用link_to时出现错误。我试过:bot.click(page.link_with(:href=>/menu_home/))bot.click(page.link_with(:class=>'top_level_active'))bot.click(page.link_with(:href=>/AccountSummary/))我得到的错误是:NoMethodError:nil:NilClass的未定义方法“[]” 最佳答案 那是一个javascript链接。Mechan
动画/*INITIALIZEANANIMATION 初始化一个动画*-----------------------*/lv_anim_ta;lv_anim_init(&a);/*MANDATORYSETTINGS 必选设置*------------------*//*Setthe"animator"function 设置“动画”功能*/lv_anim_set_exec_cb(&a,(lv_anim_exec_xcb_t)lv_obj_set_x);/*Setthe"animator"function*/lv_anim_set_var(&a,obj);/*Lengthoftheanim
我看到有关未找到文件min.map的错误消息:GETjQuery'sjquery-1.10.2.min.mapistriggeringa404(NotFound)截图这是从哪里来的? 最佳答案 如果ChromeDevTools报告.map文件的404(可能是jquery-1.10.2.min.map、jquery.min.map或jquery-2.0.3.min.map,但任何事情都可能发生)首先要知道的是,这仅在使用DevTools时才会请求。您的用户不会遇到此404。现在您可以修复此问题或禁用sourcemap功能。修复:获取文
我有一个用Rails3编写的站点。我的帖子模型有一个名为“内容”的文本列。在帖子面板中,html表单使用tinymce将“content”列设置为textarea字段。在首页,因为使用了tinymce,post.html.erb的代码需要用这样的原始方法来实现。.好的,现在如果我关闭浏览器javascript,这个文本区域可以在没有tinymce的情况下输入,也许用户会输入任何xss,比如alert('xss');.我的前台会显示那个警告框。我尝试sanitize(@post.content)在posts_controller中,但sanitize方法将相互过滤tinymce样式。例如
Ripper是Ruby1.9附带的解析库。它将Ruby代码转换为AST,如下所示:ppRipper.sexp("deffoo;yield:a;return1end")#=>[:program,[[:def,[:@ident,"foo",[1,4]],[:params,nil,nil,nil,nil,nil],[:bodystmt,[[:yield,[:args_add_block,[[:symbol_literal,[:symbol,[:@ident,"a",[1,16]]]]],false]],[:return,[:args_add_block,[[:@int,"1",[1,26]]
出于某种原因,我必须为Firefox禁用javascript(手动,我们按照提到的步骤执行http://support.mozilla.org/en-US/kb/javascript-settings-for-interactive-web-pages#w_enabling-and-disabling-javascript)。使用Ruby的SeleniumWebDriver如何实现这一点? 最佳答案 是的,这是可能的。而是另一种方式。您首先需要查看链接Selenium::WebDriver::Firefox::Profile#[]=
我是Ruby和Watir-Webdriver的新手。我有一套用VBScript编写的站点自动化程序,我想将其转换为Ruby/Watir,因为我现在必须支持Firefox。我发现我真的很喜欢Ruby,而且我正在研究Watir,但我已经花了一周时间试图让Webdriver显示我的登录屏幕。该站点以带有“我同意”区域的“警告屏幕”开头。用户点击我同意并显示登录屏幕。我需要单击该区域以显示登录屏幕(这是同一页面,实际上是一个表单,只是隐藏了)。我整天都在用VBScript这样做:objExplorer.Document.GetElementsByTagName("area")(0).click