Để thực thi tương trưng một chương trình thì mã nguồn của chương trình cần được thêm vào các phần mã cho phép việc thực thi tượng trưng. Mỗi câu lệnh trong chương trình đó sẽ được xử lý một lần tại một thời điểm và mã thực hiện việc thực thi tượng trưng sẽ được thêm vào câu lệnh đó nếu cần thiết. Mỗi tham số đầu vào v của chương trình được thay thế bằng một câu lệnh đầu vào tượng trưng getInput(v). Và chương trình sẽ gọi các câu lệnh đầu vào tượng trưng này như là các tham số đầu vào.
Mỗi lần trước khi bắt đầu thực thi một lệnh, phần thực thi tượng trưng của chương trình sẽ được khởi tạo. Trong quá trình khởi tạo, một kết nối được thiết lập với bộ lựa chọn dữ liệu kiểm thử (TIS). TIS sinh ra một dãy các giá trị cụ thể để làm đầu vào cho chương trình. Các giá trị đầu vào nhận được từ TIS được lưu vào một bộ nhớ I. I là một ánh xạ cài đặt bởi cấu trúc dữ liệu Map, ánh xạ mỗi tham số đầu vào tới một giá trị đầu vào cụ thể. Nói cách khác bộ nhớ I có thể xem như một dãy các giá trị đầu vào được sắp xếp theo thứ tự. Ví dụ khi câu lệnh đầu vào tượng trưng đầu tiên được thực thi thì giá trị đầu tiên của dãy được sử dụng. Nếu một số tham số đầu vào của chương trình không được thay thế với câu lệnh đầu vào tượng trưng thì chương trình sẽ không được thực thi theo đường đi mong đợi.
Mỗi câu lệnh getInput(v) là một phương thức được cài đặt để thực hiện việc gán một giá trị đầu vào cụ thể và giá trị tượng trưng cho tham số đầu vào. Lúc bắt đầu thực thi giá trị tượng trưng của tham số đầu vào được gán bằng một ký hiệu đầu vào duy nhất và nó được đưa tới TIS để giá trị tượng trưng này đại diện cho giá trị đầu vào cụ thể mới. Với các giá trị đầu vào cụ thể, ta cần kiểm tra xem có còn các giá trị cụ thể trong I chưa được chọn để thực thi. Nếu không một giá trị được sinh ngẫu nhiên sẽ được sử dụng.
getInput(v)
1: S[v→ si]; // si is a new symbolic value 2: i = i + 1;
3: report(S(v) = si);
4: if (inputNumber є domain(I)) 5: result = I(inputNumber); 6: else
7: result = a random value; 8: inputNumber = inputNumber + 1; 9: return result ;
Hình 5: Gán giá trị tượng trưng cho tham số đầu vào
Với mọi câu lệnh gán, các giá trị tượng trưng của biến cũng cần được cập nhật với các giá trị mới. Khi một giá trị cụ thể được gán tới một biến, giá trị tượng trưng kết hợp với biến đó được gỡ bỏ nếu có một giá trị tượng trưng như thế tồn tại. Với câu lệnh gán dạng v=v1 nếu v1 có giá trị tượng trưng kết hợp cùng thì chỉ cần sao chép nguyên giá trị tượng trưng của v1 cho v. Trường hợp v=v1 op v2, với op là một phép toán số học thì việc gán là khá phức tạp. Đầu tiên op được kiểm tra xem có được hỗ trợ bởi bộ xử lý ràng buộc đang sử dụng. Nếu op không được hỗ trợ bởi bộ xử lý ràng buộc, việc gán được thực thi cụ thể và giá trị tượng trưng của v được gỡ bỏ.
executeAssignment(v, e)
1: match (e)
2: case c: /* c is a contant value */ 3: S = S − v; 4: case v1: /* v1 is a variable */ 5: if (v1 є domain(S)) 6: S = S[v → S(v1)]; 7: else 8: S = S − v; 9: case v1 op v2: 10: if (op {+, −, ∗, /}) 11: S = S − v;
13: S[v → si]; 14: i = i + 1; 15: report(S(v) = S(v1) op S(v2)); 16: else if (v1 Є domain(S)) 17: S[v → si]; 18: i = i + 1; 19: report(S(v) = S(v1) op v2); 20: else if (v2 є domain(S)) 21: S[v → si]; 22: i = i + 1; 23: report(S(v) = v1 op S(v2)); 24: else 25: S = S − v;
Hình 6: Thực thi tượng trưng với câu lệnh gán
Thực thi tượng trưng cho câu lệnh rẽ nhánh được thực hiện như sau. Trước tiên cần xác định nhánh đi mà thực thi cụ thể đi theo và sau đó một ràng buộc đường đi được lưu trữ tới đỉnh tương ứng của SET (Hình 7). Cần kiểm tra xem các giá trị tượng trưng có được kết hợp với các biến xuất hiện trong biểu thức điều kiện rẽ nhánh hay không. Nếu chỉ có giá trị cụ thể của biến được sử dụng, TIS không cần được thông báo, việc thực thi câu lệnh không ảnh hưởng tới SET mà TIS đang quản lý. Nếu giá trị tượng trưng được sử dụng thì ràng buộc đường đi tương ứng với cả hai nhánh sẽ được đưa tới TIS. Đỉnh thuộc nhánh mà sự thực thi cụ thể hiện thời đi theo sẽ được đánh dấu là đã thăm và đỉnh tương ứng với nhánh còn lại được đánh dấu là chưa được thăm để tiếp tục mở rộng.
Chú ý rằng, ở dòng 2 thông tin của nhánh được với tới trong sự thực thi cụ thể hiện thời được lưu vào một bit-vertor. Bit-vertor chứa tất cả lựa chọn biểu thị cho các nhánh đã chọn và được đưa tới TIS khi phương thức report được gọi. Phương thức report làm nhiệm vụ gửi các thông tin về ràng buộc và các lựa chọn tới TIS để khởi tạo các đỉnh của SET tương ứng với ràng buộc đó.
Một chương trình có thể có một vòng lặp vô tận, do đó việc thực thi phải dừng ở một chiều sâu định trước. Vòng lặp trong mã nguồn Java được biến đổi thành câu lệnh goto hoặc một lời gọi đệ quy trong ngôn ngữ trung gian Jimple[17].
executeCondition(op v1 v2)
1: branchTaken = evaluate(v1 op v2); 2: constructBranchBitvector(branchTaken); 3: if (v1 є domain(S) && v2 є domain(S)) 4: report(S(v1) op S(v2), branchTaken); 5: else if (v1 є domain(S))
6: report(S(v1) op v2, branchTaken); 7: else if (v2 є domain(S))
Hình 7: Thực thi tượng trưng với câu lệnh rẽ nhánh