2013-04-03 2 views
8

私は、v8コードで直接jsコードのカバレッジを実装するつもりです。 私の最初の目標は、抽象構文ツリーのすべてのステートメントに簡単なプリントを追加することです。 AstVisitorクラスがあり、ASTをトラバースすることができます。 私の質問は、どのように訪問者が現在訪問しているステートメントの後にASTにステートメントを追加することができますか?V8を操作する

+0

基本ブロックは、ASTではなく、制御フローグラフの構成要素です。あなたはASTからCFGを作成しようと思いますか? – delnan

+0

私は2つを混合しているかもしれませんが、私はastのノードも基本ブロックだと思いましたか? – user2240085

+0

*どちらのノード?いずれにしても、基本ブロックと一致する共通のASTノードは認識していません(ただし、CFG-ish情報を保持し、それを "AST"と呼ぶデータ構造を持つことは可能です)。たとえば、ループは通常ASTノードですが、多くのループは複数のBBで構成されています。ループノードは、ステートメントノードのリストを含むことができるが、それらのステートメントのいくつかは、BBの* part *(例えば、単純割り当て)に対応し、ステートメントノードのいくつかは*複数の* BB(例えば、インライン条件またはネストループ)おそらく、あなたは "基本ブロック"という用語を誤って使用しているのでしょうか? – delnan

答えて

5

私は実験を要約します。まず、Chromiumバージョンr157275で使用されていたように、V8に私が書いたものが適用されるため、それ以上は機能しないかもしれませんが、現在のバージョンの場所にリンクします。

AstVisitorから継承し、そこからVisitXYZメソッドの束を実装する必要があるように、あなた自身のASTビジター、たとえばMyAstVisiorが必要です。実行されたコードを計測/検査するために必要な唯一のものはVisitFunctionLiteralです。実行されるコードは、関数内でV8がラップして実行されるソース(ファイル)内の関数またはloose文のセットです。

次に、解析されたASTをコードhere(ルーズ文からのコンパイル)とthere(実行時のコンパイル、初めて定義された関数が実行されるとき)の直前に変換すると、訪問者にVisitFunctionLiteralを呼び出す関数リテラルへの訪問者、:

MyAstVisitor myAV(info); 
info->function()->Accept(&myAV); 
// next line is the V8 compile call 
if (!MakeCode(info)) { 

1はASTを変更することを必要とするので、私は、カスタムユーザーへCompilationInfoポインタinfoを可決しました。コンストラクタは次のようになります。

MyAstVisitor(CompilationInfo* compInfo) : 
    _ci(compInfo), _nf(compInfo->isolate(), compInfo->zone()), _z(compInfo->zone()){}; 

_ci、_nfと_ZはCompilationInfoAstNodeFactory<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ポインタは、最後に元の関数本体に置き換わるステートメントのリストを保持します。ですから、最初に元の文を追加して、あなた自身のprint文を追加し、それぞれの文の後にprint文を追加します。

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); 
} 

NewCallNewUnresolvedの最後のパラメータは、スクリプト内の位置を特定する番号です。私はこれがデバッグ/エラーメッセージのどこでエラーが発生したかを示すために使用されると仮定します。私は少なくとも0に設定することで問題に遭遇したことはありません(kNoPositionのどこかに定数もあります)。

最後のいくつかの単語:Blocks(例:ループ本体)はステートメントのリストを表すステートメントであり、ループは条件式とボディブロックを持つステートメントであるため、実際には各ステートメントの後にprintステートメントを追加しません。したがって、現在どのような文が処理されているのかを調べ、再帰的に調べる必要があります。ブロックの書き換えは、関数本体の書き換えとほぼ同じです。

しかし、ASTには分岐に関する情報も含まれているため、既存のステートメントの置き換えや変更を開始するときに問題が発生します。だから、もしあなたがあなたのコードを壊すいくつかの条件のためのジャンプターゲットを置き換えます。私は、これを置き換えるために新しいものを作成するのではなく、単一の式と文の型にリライト機能を直接追加すると、これをカバーすることができると思います。

これまでのところ、私はそれが役に立ちそうです。

+0

Gad。代わりに、「任意の言語のブランチカバレッジを簡単に作成する」で概説したアプローチを検討してください。http://www.semdesigns.com/Company/Publications/TestCoverage.pdf –