1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 21:07:35 +00:00

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.
This commit is contained in:
Anonymous 2022-02-12 00:48:23 -08:00 committed by Andreas Kling
parent 3a184f7841
commit d1cc67bbe1
7 changed files with 77 additions and 24 deletions

View file

@ -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 {

View file

@ -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 {

View file

@ -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));

View file

@ -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"; }

View file

@ -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);
}

View file

@ -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 {

View file

@ -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);
}
};
}