summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibJS
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2022-11-24 14:16:56 +0100
committerLinus Groh <mail@linusgroh.de>2022-11-24 16:06:20 +0000
commitf0482b61bf3efbce61c52e81c792c072b46b548d (patch)
treed2ecd5933b784da31ae80d05285a1661bfe4c8f9 /Userland/Libraries/LibJS
parente7ba03ddd109dda3f0d1cdac4a8053a4753bfbf0 (diff)
downloadserenity-f0482b61bf3efbce61c52e81c792c072b46b548d.zip
LibJS: Make Error stack trace generation faster with a line break cache
Generating Error objects got a lot slower after the introduction of SourceCode in b0b022507b2f73be2f4cef17deb8ece91dae6822. This was noticeable with `test-js` which generates a lot of errors, so walking the source code over and over to compute (line, column) was eating a ton of time. This patch makes repeated lookups a lot faster by building a cache of line break offsets in the source code. The cache is built on first offset lookup, so we only pay for this in code that actually throws. On my machine, this takes `test-js` runtime from 6.7 sec to 4.3 sec.
Diffstat (limited to 'Userland/Libraries/LibJS')
-rw-r--r--Userland/Libraries/LibJS/SourceCode.cpp41
-rw-r--r--Userland/Libraries/LibJS/SourceCode.h5
2 files changed, 43 insertions, 3 deletions
diff --git a/Userland/Libraries/LibJS/SourceCode.cpp b/Userland/Libraries/LibJS/SourceCode.cpp
index 89a844f3e3..f0700a1c2d 100644
--- a/Userland/Libraries/LibJS/SourceCode.cpp
+++ b/Userland/Libraries/LibJS/SourceCode.cpp
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
+#include <AK/BinarySearch.h>
#include <AK/Utf8View.h>
#include <LibJS/SourceCode.h>
#include <LibJS/SourceRange.h>
@@ -32,18 +33,52 @@ String const& SourceCode::code() const
return m_code;
}
+void SourceCode::compute_line_break_offsets() const
+{
+ m_line_break_offsets = Vector<size_t> {};
+
+ if (m_code.is_empty())
+ return;
+
+ bool previous_code_point_was_carriage_return = false;
+ Utf8View view(m_code.view());
+ for (auto it = view.begin(); it != view.end(); ++it) {
+ u32 code_point = *it;
+ bool is_line_terminator = code_point == '\r' || (code_point == '\n' && !previous_code_point_was_carriage_return) || code_point == LINE_SEPARATOR || code_point == PARAGRAPH_SEPARATOR;
+ previous_code_point_was_carriage_return = code_point == '\r';
+
+ if (is_line_terminator)
+ m_line_break_offsets->append(view.byte_offset_of(it));
+ }
+}
+
SourceRange SourceCode::range_from_offsets(u32 start_offset, u32 end_offset) const
{
+ if (m_code.is_empty())
+ return { *this, {}, {} };
+
+ if (!m_line_break_offsets.has_value())
+ compute_line_break_offsets();
+
+ size_t line = 1;
+ size_t nearest_line_break_index = 0;
+ size_t nearest_preceding_line_break_offset = 0;
+
+ if (!m_line_break_offsets->is_empty()) {
+ binary_search(*m_line_break_offsets, start_offset, &nearest_line_break_index);
+ line = 1 + nearest_line_break_index;
+ nearest_preceding_line_break_offset = (*m_line_break_offsets)[nearest_line_break_index];
+ }
+
Position start;
Position end;
- size_t line = 1;
size_t column = 1;
bool previous_code_point_was_carriage_return = false;
- Utf8View view(m_code);
- for (auto it = view.begin(); it != view.end(); ++it) {
+ Utf8View view(m_code.view());
+ for (auto it = view.iterator_at_byte_offset_without_validation(nearest_preceding_line_break_offset); it != view.end(); ++it) {
if (start_offset == view.byte_offset_of(it)) {
start = Position {
diff --git a/Userland/Libraries/LibJS/SourceCode.h b/Userland/Libraries/LibJS/SourceCode.h
index 3980efed69..1550c50c89 100644
--- a/Userland/Libraries/LibJS/SourceCode.h
+++ b/Userland/Libraries/LibJS/SourceCode.h
@@ -7,6 +7,7 @@
#pragma once
#include <AK/String.h>
+#include <AK/Vector.h>
#include <LibJS/Forward.h>
namespace JS {
@@ -23,8 +24,12 @@ public:
private:
SourceCode(String filename, String code);
+ void compute_line_break_offsets() const;
+
String m_filename;
String m_code;
+
+ Optional<Vector<size_t>> mutable m_line_break_offsets;
};
}