summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnonymous <anon@mous.org>2022-02-12 00:48:23 -0800
committerAndreas Kling <kling@serenityos.org>2022-02-13 14:44:36 +0100
commitd1cc67bbe12d66aca2a716011697337664b72e6d (patch)
treeb4d49b40067009ce0d530775cbe0109b17700a91
parent3a184f784186ad1c5a9704b05ab0902d577d5748 (diff)
downloadserenity-d1cc67bbe12d66aca2a716011697337664b72e6d.zip
LibJS: Avoid unnecessary ToObject conversion when resolving references
When performing GetValue on a primitive type we do not need to perform the ToObject conversion as it will resolve to a property on the prototype object. To avoid this we skip the initial ToObject conversion on the base value as it only serves to get the primitive's boxed prototype. We further specialize on PrimitiveString in order to get efficient behaviour behaviour for the direct properties. Depending on the tests anywhere from 20 to 60%, with significant loop overhead.
-rw-r--r--Userland/Libraries/LibJS/Bytecode/Op.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/Map.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/PrimitiveString.cpp24
-rw-r--r--Userland/Libraries/LibJS/Runtime/PrimitiveString.h3
-rw-r--r--Userland/Libraries/LibJS/Runtime/Reference.cpp15
-rw-r--r--Userland/Libraries/LibJS/Runtime/Value.h22
-rw-r--r--Userland/Libraries/LibJS/Runtime/ValueTraits.h35
7 files changed, 77 insertions, 24 deletions
diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h
index 3472e5b28a..666ce7e2f9 100644
--- a/Userland/Libraries/LibJS/Bytecode/Op.h
+++ b/Userland/Libraries/LibJS/Bytecode/Op.h
@@ -18,6 +18,7 @@
#include <LibJS/Runtime/Environment.h>
#include <LibJS/Runtime/EnvironmentCoordinate.h>
#include <LibJS/Runtime/Value.h>
+#include <LibJS/Runtime/ValueTraits.h>
namespace JS::Bytecode::Op {
diff --git a/Userland/Libraries/LibJS/Runtime/Map.h b/Userland/Libraries/LibJS/Runtime/Map.h
index 476aa7bee7..3cce53cfab 100644
--- a/Userland/Libraries/LibJS/Runtime/Map.h
+++ b/Userland/Libraries/LibJS/Runtime/Map.h
@@ -12,6 +12,7 @@
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Value.h>
+#include <LibJS/Runtime/ValueTraits.h>
namespace JS {
diff --git a/Userland/Libraries/LibJS/Runtime/PrimitiveString.cpp b/Userland/Libraries/LibJS/Runtime/PrimitiveString.cpp
index 063c518f4a..b2d328677c 100644
--- a/Userland/Libraries/LibJS/Runtime/PrimitiveString.cpp
+++ b/Userland/Libraries/LibJS/Runtime/PrimitiveString.cpp
@@ -4,9 +4,13 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
+#include "LibJS/Runtime/Value.h"
#include <AK/CharacterTypes.h>
#include <AK/Utf16View.h>
+#include <LibJS/Runtime/AbstractOperations.h>
+#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/PrimitiveString.h>
+#include <LibJS/Runtime/PropertyKey.h>
#include <LibJS/Runtime/VM.h>
namespace JS {
@@ -51,6 +55,26 @@ Utf16View PrimitiveString::utf16_string_view() const
return utf16_string().view();
}
+Optional<Value> PrimitiveString::get(GlobalObject& global_object, PropertyKey const& property_key) const
+{
+ if (property_key.is_symbol())
+ return {};
+ if (property_key.is_string()) {
+ if (property_key.as_string() == global_object.vm().names.length.as_string()) {
+ auto length = utf16_string().length_in_code_units();
+ return Value(static_cast<double>(length));
+ }
+ }
+ auto index = canonical_numeric_index_string(property_key);
+ if (!index.has_value())
+ return {};
+ auto str = utf16_string_view();
+ auto length = str.length_in_code_units();
+ if (length <= *index)
+ return {};
+ return js_string(vm(), str.substring_view(*index, 1));
+}
+
PrimitiveString* js_string(Heap& heap, Utf16View const& view)
{
return js_string(heap, Utf16String(view));
diff --git a/Userland/Libraries/LibJS/Runtime/PrimitiveString.h b/Userland/Libraries/LibJS/Runtime/PrimitiveString.h
index dd15cb4d49..e293c259be 100644
--- a/Userland/Libraries/LibJS/Runtime/PrimitiveString.h
+++ b/Userland/Libraries/LibJS/Runtime/PrimitiveString.h
@@ -7,7 +7,9 @@
#pragma once
#include <AK/String.h>
+#include <LibJS/Forward.h>
#include <LibJS/Heap/Cell.h>
+#include <LibJS/Runtime/Value.h>
#include <LibJS/Runtime/Utf16String.h>
namespace JS {
@@ -28,6 +30,7 @@ public:
Utf16View utf16_string_view() const;
bool has_utf16_string() const { return m_has_utf16_string; }
+ Optional<Value> get(GlobalObject&, PropertyKey const&) const;
private:
virtual const char* class_name() const override { return "PrimitiveString"; }
diff --git a/Userland/Libraries/LibJS/Runtime/Reference.cpp b/Userland/Libraries/LibJS/Runtime/Reference.cpp
index 8986511909..f9a25872ca 100644
--- a/Userland/Libraries/LibJS/Runtime/Reference.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Reference.cpp
@@ -63,15 +63,26 @@ ThrowCompletionOr<Value> Reference::get_value(GlobalObject& global_object) const
return throw_reference_error(global_object);
if (is_property_reference()) {
- auto* base_obj = TRY(m_base_value.to_object(global_object));
-
if (is_private_reference()) {
// FIXME: We need to be able to specify the receiver for this
// if we want to use it in error messages in future
// as things currently stand this does the "wrong thing" but
// the error is unobservable
+ auto base_obj = TRY(m_base_value.to_object(global_object));
return base_obj->private_get(m_private_name);
}
+ Object* base_obj = nullptr;
+ if (m_base_value.is_string()) {
+ auto string_value = m_base_value.as_string().get(global_object, m_name);
+ if (string_value.has_value())
+ return *string_value;
+ base_obj = global_object.string_prototype();
+ } else if (m_base_value.is_number())
+ base_obj = global_object.number_prototype();
+ else if (m_base_value.is_boolean())
+ base_obj = global_object.boolean_prototype();
+ else
+ base_obj = TRY(m_base_value.to_object(global_object));
return base_obj->internal_get(m_name, m_base_value);
}
diff --git a/Userland/Libraries/LibJS/Runtime/Value.h b/Userland/Libraries/LibJS/Runtime/Value.h
index 6a001cf9b0..fca480e91e 100644
--- a/Userland/Libraries/LibJS/Runtime/Value.h
+++ b/Userland/Libraries/LibJS/Runtime/Value.h
@@ -18,7 +18,6 @@
#include <AK/Types.h>
#include <LibJS/Forward.h>
#include <LibJS/Runtime/BigInt.h>
-#include <LibJS/Runtime/PrimitiveString.h>
#include <LibJS/Runtime/Utf16String.h>
#include <math.h>
@@ -435,27 +434,6 @@ ThrowCompletionOr<TriState> is_less_than(GlobalObject&, bool left_first, Value l
inline bool Value::operator==(Value const& value) const { return same_value(*this, value); }
-struct ValueTraits : public Traits<Value> {
- static unsigned hash(Value value)
- {
- VERIFY(!value.is_empty());
- if (value.is_string())
- return value.as_string().string().hash();
-
- if (value.is_bigint())
- return value.as_bigint().big_integer().hash();
-
- if (value.is_negative_zero())
- value = Value(0);
-
- return u64_hash(value.encoded()); // FIXME: Is this the best way to hash pointers, doubles & ints?
- }
- static bool equals(const Value a, const Value b)
- {
- return same_value_zero(a, b);
- }
-};
-
}
namespace AK {
diff --git a/Userland/Libraries/LibJS/Runtime/ValueTraits.h b/Userland/Libraries/LibJS/Runtime/ValueTraits.h
new file mode 100644
index 0000000000..14e2dfd23c
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ValueTraits.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibJS/Runtime/PrimitiveString.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+struct ValueTraits : public Traits<Value> {
+ static unsigned hash(Value value)
+ {
+ VERIFY(!value.is_empty());
+ if (value.is_string())
+ return value.as_string().string().hash();
+
+ if (value.is_bigint())
+ return value.as_bigint().big_integer().hash();
+
+ if (value.is_negative_zero())
+ value = Value(0);
+
+ return u64_hash(value.encoded()); // FIXME: Is this the best way to hash pointers, doubles & ints?
+ }
+ static bool equals(const Value a, const Value b)
+ {
+ return same_value_zero(a, b);
+ }
+};
+
+}