2013-04-03 45 views
8

Tôi định triển khai mã vùng js trực tiếp trong mã v8. Mục tiêu ban đầu của tôi là thêm một bản in đơn giản cho mọi câu lệnh trong cây cú pháp trừu tượng. Tôi thấy rằng có một lớp học AstVisitor, cho phép bạn duyệt qua AST. vì vậy câu hỏi của tôi là làm thế nào tôi có thể thêm một tuyên bố cho AST sau khi tuyên bố khách truy cập hiện đang truy cập?Thao tác động cơ V8

+0

Khối cơ bản là cấu trúc cho biểu đồ luồng kiểm soát, không phải cho AST. Bạn có dự định tạo CFG từ AST không? – delnan

+0

tôi có thể trộn hai, nhưng tôi nghĩ rằng các nút của ast là khối cơ bản là tốt? – user2240085

+0

* Nút * nào? Trong mọi trường hợp, tôi không biết bất kỳ nút AST chung nào khớp với các khối cơ bản (mặc dù chắc chắn có thể có một cấu trúc dữ liệu cũng duy trì thông tin CFG-ish và gọi là "AST"). Ví dụ, một vòng lặp thường là một nút AST, nhưng nhiều vòng lặp bao gồm một số BB. Nút vòng lặp có thể chứa danh sách các nút câu lệnh, nhưng một số câu lệnh tương ứng với * phần * của BB (ví dụ: bài tập đơn giản), trong khi các nút khác mở rộng thành * vài * BB (ví dụ: bất kỳ điều kiện nội tuyến nào hoặc vòng lặp lồng nhau). Có lẽ bạn đang lạm dụng thuật ngữ "khối cơ bản"? – delnan

Trả lời

5

Ok, tôi sẽ tóm tắt các thử nghiệm của mình. Trước tiên, những gì tôi viết áp dụng cho V8 như được sử dụng trong phiên bản Chromium r157275, vì vậy mọi thứ có thể không hoạt động nữa - nhưng tôi vẫn sẽ liên kết đến các địa điểm trong phiên bản hiện tại.

Như đã nói, bạn cần khách truy cập AST của riêng mình, nói MyAstVisior, được kế thừa từ AstVisitor và phải triển khai một loạt các phương thức từ VisitXYZ. Người duy nhất cần thiết để kiểm tra/thực thi mã là VisitFunctionLiteral. Mã thực thi là một hàm hoặc một tập hợp các câu lệnh lỏng lẻo trong một nguồn (tệp) mà V8 kết thúc tốt đẹp trong một hàm mà sau đó được thực hiện.

Sau đó, ngay trước khi AST được phân tích cú pháp được chuyển thành mã, here (biên soạn hàm được tạo thành các câu lệnh lỏng lẻo) và there (biên dịch trong thời gian chạy, khi hàm được xác định trước được thực thi lần đầu tiên), bạn chuyển truy cập vào các chức năng theo nghĩa đen, mà sẽ gọi VisitFunctionLiteral trên lượt truy cập:

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

tôi đã thông qua CompilationInfo con trỏ info để khách truy cập tùy chỉnh vì người ta cần đó để thay đổi AST. Các nhà xây dựng trông như thế này:

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

_ci, _nf và _z là con trỏ đến CompilationInfo, AstNodeFactory<AstNullVisitor>Zone.

Bây giờ trong VisitFunctionLiteral bạn có thể lặp qua thân hàm và cũng chèn câu lệnh nếu muốn.

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

Trong phương thức rewriteStatement bạn hiện có thể kiểm tra tuyên bố. Con trỏ _stmts giữ một danh sách các câu lệnh mà cuối cùng sẽ thay thế phần thân hàm ban đầu. Vì vậy, để thêm một tuyên bố in sau mỗi lần tuyên bố lần đầu tiên bạn thêm báo cáo kết quả ban đầu và sau đó thêm tuyên bố in của riêng bạn:

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

Tham số cuối cùng của NewCallNewUnresolved là một số quy định cụ thể vị trí trong kịch bản. Tôi giả sử điều này được sử dụng để gỡ lỗi/thông báo lỗi để biết nơi xảy ra lỗi. Tôi ít nhất không bao giờ gặp phải vấn đề với thiết lập nó đến 0 (cũng có một hằng số ở đâu đó kNoPosition).

Một số từ cuối cùng: Điều này sẽ không thực sự thêm tuyên bố in sau mỗi câu lệnh, vì Blocks (ví dụ: vòng lặp) là các câu lệnh đại diện cho danh sách các câu lệnh và vòng lặp là các câu lệnh có biểu thức điều kiện và khối nội dung. Vì vậy, bạn sẽ cần phải kiểm tra loại tuyên bố hiện đang được xử lý và đệ quy nhìn vào nó. Viết lại các khối là khá nhiều giống như viết lại một cơ quan chức năng.

Nhưng bạn sẽ gặp sự cố khi bắt đầu thay thế hoặc sửa đổi các câu lệnh hiện có, vì AST cũng mang thông tin về phân nhánh. Vì vậy, nếu bạn thay thế một mục tiêu nhảy cho một số điều kiện bạn phá vỡ mã của bạn.Tôi đoán điều này có thể được bao phủ nếu một người trực tiếp thêm khả năng viết lại cho các biểu thức và kiểu câu lệnh thay vì tạo các cái mới để thay thế chúng.

Cho đến giờ, tôi hy vọng điều đó sẽ hữu ích.

+0

Gad. Thay vào đó, hãy xem xét cách tiếp cận của tôi được nêu trong "Bảo hiểm chi nhánh cho ngôn ngữ tùy ý được thực hiện dễ dàng" http://www.semdesigns.com/Company/Publications/TestCoverage.pdf –