diff --git a/runtime/instruction_size.cpp b/runtime/instruction_size.cpp new file mode 100644 index 0000000..6eba25a --- /dev/null +++ b/runtime/instruction_size.cpp @@ -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(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; +} diff --git a/runtime/vm.cpp b/runtime/vm.cpp index c34a83a..adcffb1 100644 --- a/runtime/vm.cpp +++ b/runtime/vm.cpp @@ -451,7 +451,8 @@ void VM::run_file_builtin(Builtin builtin_id) auto filename = stack_pop().as_string().value; FILE* fp = std::fopen(filename.c_str(), mode.c_str()); 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); } 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; }; +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) { if (this->stack.size() - this->bp < count) { @@ -583,11 +592,3 @@ void VM::assert_stack_has(size_t count) 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); - } -} diff --git a/runtime/vm.hpp b/runtime/vm.hpp index 299148b..5ed6278 100644 --- a/runtime/vm.hpp +++ b/runtime/vm.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include namespace sliger { @@ -108,6 +109,11 @@ struct CCPosEntry { class CodeCoverageBuilder : public json::ToAndFromJson { public: + inline void make_sure_entry_exists(SourcePos pos) + { + find_or_create_entry(pos); + } + /// call when leaving a source location inline void report_cover(SourcePos pos) { @@ -166,8 +172,19 @@ public: 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(this->program.at(i + 1)); + auto line = static_cast(this->program.at(i + 2)); + auto col = static_cast(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); } @@ -223,6 +240,7 @@ private: { return this->stack.at(this->bp + idx); } + void assert_program_has(size_t count); void assert_fn_stack_has(size_t count); void assert_stack_has(size_t count); inline void stack_push(Value&& value) { this->stack.push_back(value); } @@ -233,7 +251,7 @@ private: this->stack.pop_back(); return value; } - void assert_program_has(size_t count); + size_t instruction_size(size_t i) const; VMOpts opts; uint32_t pc = 0; diff --git a/web/public/src/code_coverage.ts b/web/public/src/code_coverage.ts index c60a4c0..5e5b2e0 100644 --- a/web/public/src/code_coverage.ts +++ b/web/public/src/code_coverage.ts @@ -97,21 +97,6 @@ export function loadCodeCoverage( elements.push(span); 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); document.addEventListener("mousemove", (event) => { const [x, y] = [event.clientX, event.clientY]; @@ -146,3 +131,19 @@ export function loadCodeCoverage( }); 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; +} diff --git a/web/public/src/index.ts b/web/public/src/index.ts index 85c7a1b..0ca2e85 100644 --- a/web/public/src/index.ts +++ b/web/public/src/index.ts @@ -57,6 +57,26 @@ function sourceCode(view: Element, codeData: string) { 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) { const codeCoverageData = await data.codeCoverageData(); @@ -66,25 +86,6 @@ async function codeCoverage(view: Element, codeData: string) { const innerContainer = document.createElement("div"); 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( "performance-coverage", "Performance view", diff --git a/web/public/style.css b/web/public/style.css index 9009b0f..a3769da 100644 --- a/web/public/style.css +++ b/web/public/style.css @@ -159,6 +159,7 @@ main #cover { .coverage-radio { display: flex; flex-direction: row; + justify-content: center; } .coverage-radio-group { @@ -166,6 +167,7 @@ main #cover { justify-content: center; flex: 1; padding: 2rem; + max-width: max-content; } .coverage-radio-group label { @@ -173,6 +175,8 @@ main #cover { border-radius: 0.25rem; cursor: pointer; border: 2px solid var(--code-status); + width: 280px; + text-align: center; } .coverage-radio-group input:checked ~ label {