fix code coverage

This commit is contained in:
sfja 2024-12-16 18:23:51 +01:00
parent 1760913909
commit f8b573c135
6 changed files with 122 additions and 45 deletions

View File

@ -0,0 +1,52 @@
#include "arch.hpp"
#include "vm.hpp"
using namespace sliger;
size_t VM::instruction_size(size_t i) const
{
switch (static_cast<Op>(this->program.at(i))) {
case Op::Nop:
case Op::PushNull:
return 1;
case Op::PushInt:
case Op::PushBool:
return 2;
case Op::PushString: {
auto string_length = this->program.at(i + 1);
return 2 + string_length;
}
case Op::PushPtr:
return 2;
case Op::Pop:
return 1;
case Op::ReserveStatic:
case Op::LoadStatic:
case Op::StoreStatic:
case Op::LoadLocal:
case Op::StoreLocal:
case Op::Call:
return 2;
case Op::Return:
case Op::Jump:
case Op::JumpIfTrue:
return 1;
case Op::Builtin:
return 2;
case Op::Add:
case Op::Subtract:
case Op::Multiply:
case Op::Divide:
case Op::Remainder:
case Op::Equal:
case Op::LessThan:
case Op::And:
case Op::Or:
case Op::Xor:
case Op::Not:
return 1;
case Op::SourceMap:
return 4;
}
return 1;
}

View File

@ -451,7 +451,8 @@ void VM::run_file_builtin(Builtin builtin_id)
auto filename = stack_pop().as_string().value; auto filename = stack_pop().as_string().value;
FILE* fp = std::fopen(filename.c_str(), mode.c_str()); FILE* fp = std::fopen(filename.c_str(), mode.c_str());
if (fp == nullptr) { if (fp == nullptr) {
std::cerr << std::format("error: could not open file '{}'\n", filename); std::cerr << std::format(
"error: could not open file '{}'\n", filename);
std::exit(1); std::exit(1);
} }
auto file_id = this->file_id_counter; auto file_id = this->file_id_counter;
@ -568,6 +569,14 @@ auto VM::stack_repr_string(size_t max_items) const -> std::string
return result; return result;
}; };
void VM::assert_program_has(size_t count)
{
if (this->pc + count > program.size()) {
std::cerr << std::format("malformed program, pc = {}", this->pc);
std::exit(1);
}
}
void VM::assert_fn_stack_has(size_t count) void VM::assert_fn_stack_has(size_t count)
{ {
if (this->stack.size() - this->bp < count) { if (this->stack.size() - this->bp < count) {
@ -583,11 +592,3 @@ void VM::assert_stack_has(size_t count)
std::exit(1); std::exit(1);
} }
} }
void VM::assert_program_has(size_t count)
{
if (this->pc + count > program.size()) {
std::cerr << std::format("malformed program, pc = {}", this->pc);
std::exit(1);
}
}

View File

@ -8,6 +8,7 @@
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
namespace sliger { namespace sliger {
@ -108,6 +109,11 @@ struct CCPosEntry {
class CodeCoverageBuilder : public json::ToAndFromJson { class CodeCoverageBuilder : public json::ToAndFromJson {
public: public:
inline void make_sure_entry_exists(SourcePos pos)
{
find_or_create_entry(pos);
}
/// call when leaving a source location /// call when leaving a source location
inline void report_cover(SourcePos pos) inline void report_cover(SourcePos pos)
{ {
@ -166,8 +172,19 @@ public:
return json::to_json(this->flame_graph); return json::to_json(this->flame_graph);
} }
inline auto code_coverage_json() const -> std::string inline auto code_coverage_json() -> std::string
{ {
for (size_t i = 0; i < this->program.size(); ++i) {
if (this->program.at(i) == std::to_underlying(Op::SourceMap)
&& this->program.size() - 1 - i >= 3) {
auto index = static_cast<int32_t>(this->program.at(i + 1));
auto line = static_cast<int32_t>(this->program.at(i + 2));
auto col = static_cast<int32_t>(this->program.at(i + 3));
this->code_coverage.make_sure_entry_exists(
{ index, line, col });
}
i += instruction_size(i);
}
return json::to_json(this->code_coverage); return json::to_json(this->code_coverage);
} }
@ -223,6 +240,7 @@ private:
{ {
return this->stack.at(this->bp + idx); return this->stack.at(this->bp + idx);
} }
void assert_program_has(size_t count);
void assert_fn_stack_has(size_t count); void assert_fn_stack_has(size_t count);
void assert_stack_has(size_t count); void assert_stack_has(size_t count);
inline void stack_push(Value&& value) { this->stack.push_back(value); } inline void stack_push(Value&& value) { this->stack.push_back(value); }
@ -233,7 +251,7 @@ private:
this->stack.pop_back(); this->stack.pop_back();
return value; return value;
} }
void assert_program_has(size_t count); size_t instruction_size(size_t i) const;
VMOpts opts; VMOpts opts;
uint32_t pc = 0; uint32_t pc = 0;

View File

@ -97,21 +97,6 @@ export function loadCodeCoverage(
elements.push(span); elements.push(span);
col += 1; col += 1;
} }
function positionInBox(
position: [number, number],
boundingRect: {
left: number;
top: number;
right: number;
bottom: number;
},
) {
const [x, y] = position;
const outside = x < boundingRect.left ||
x >= boundingRect.right || y < boundingRect.top ||
y >= boundingRect.bottom;
return !outside;
}
container.append(...elements); container.append(...elements);
document.addEventListener("mousemove", (event) => { document.addEventListener("mousemove", (event) => {
const [x, y] = [event.clientX, event.clientY]; const [x, y] = [event.clientX, event.clientY];
@ -146,3 +131,19 @@ export function loadCodeCoverage(
}); });
return container; return container;
} }
function positionInBox(
position: [number, number],
boundingRect: {
left: number;
top: number;
right: number;
bottom: number;
},
) {
const [x, y] = position;
const outside = x < boundingRect.left ||
x >= boundingRect.right || y < boundingRect.top ||
y >= boundingRect.bottom;
return !outside;
}

View File

@ -57,6 +57,26 @@ function sourceCode(view: Element, codeData: string) {
view.replaceChildren(outerContainer); view.replaceChildren(outerContainer);
} }
function createRadio(
id: string,
content: string,
checked: boolean,
): [HTMLDivElement, HTMLInputElement] {
const label = document.createElement("label");
label.htmlFor = id;
label.innerText = content;
const input = document.createElement("input");
input.id = id;
input.name = "coverage-radio";
input.type = "radio";
input.hidden = true;
input.checked = checked;
const container = document.createElement("div");
container.classList.add("coverage-radio-group");
container.append(input, label);
return [container, input];
}
async function codeCoverage(view: Element, codeData: string) { async function codeCoverage(view: Element, codeData: string) {
const codeCoverageData = await data.codeCoverageData(); const codeCoverageData = await data.codeCoverageData();
@ -66,25 +86,6 @@ async function codeCoverage(view: Element, codeData: string) {
const innerContainer = document.createElement("div"); const innerContainer = document.createElement("div");
innerContainer.classList.add("code-container-inner"); innerContainer.classList.add("code-container-inner");
function createRadio(
id: string,
content: string,
checked: boolean,
): [HTMLDivElement, HTMLInputElement] {
const label = document.createElement("label");
label.htmlFor = id;
label.innerText = content;
const input = document.createElement("input");
input.id = id;
input.name = "coverage-radio";
input.type = "radio";
input.hidden = true;
input.checked = checked;
const container = document.createElement("div");
container.classList.add("coverage-radio-group");
container.append(input, label);
return [container, input];
}
const [perfGroup, perfInput] = createRadio( const [perfGroup, perfInput] = createRadio(
"performance-coverage", "performance-coverage",
"Performance view", "Performance view",

View File

@ -159,6 +159,7 @@ main #cover {
.coverage-radio { .coverage-radio {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center;
} }
.coverage-radio-group { .coverage-radio-group {
@ -166,6 +167,7 @@ main #cover {
justify-content: center; justify-content: center;
flex: 1; flex: 1;
padding: 2rem; padding: 2rem;
max-width: max-content;
} }
.coverage-radio-group label { .coverage-radio-group label {
@ -173,6 +175,8 @@ main #cover {
border-radius: 0.25rem; border-radius: 0.25rem;
cursor: pointer; cursor: pointer;
border: 2px solid var(--code-status); border: 2px solid var(--code-status);
width: 280px;
text-align: center;
} }
.coverage-radio-group input:checked ~ label { .coverage-radio-group input:checked ~ label {