diff --git a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp new file mode 100644 index 0000000000..d4679288e4 --- /dev/null +++ b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021-2023, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace JS::Bytecode { + +ThrowCompletionOr> base_object_for_get(Bytecode::Interpreter& interpreter, Value base_value) +{ + auto& vm = interpreter.vm(); + if (base_value.is_object()) + return base_value.as_object(); + + // OPTIMIZATION: For various primitives we can avoid actually creating a new object for them. + if (base_value.is_string()) + return vm.current_realm()->intrinsics().string_prototype(); + if (base_value.is_number()) + return vm.current_realm()->intrinsics().number_prototype(); + if (base_value.is_boolean()) + return vm.current_realm()->intrinsics().boolean_prototype(); + + return base_value.to_object(vm); +} + +ThrowCompletionOr get_by_id(Bytecode::Interpreter& interpreter, IdentifierTableIndex property, Value base_value, Value this_value, u32 cache_index) +{ + auto& vm = interpreter.vm(); + auto const& name = interpreter.current_executable().get_identifier(property); + auto& cache = interpreter.current_executable().property_lookup_caches[cache_index]; + + if (base_value.is_string()) { + auto string_value = TRY(base_value.as_string().get(vm, name)); + if (string_value.has_value()) + return *string_value; + } + + auto base_obj = TRY(base_object_for_get(interpreter, base_value)); + + // OPTIMIZATION: If the shape of the object hasn't changed, we can use the cached property offset. + // NOTE: Unique shapes don't change identity, so we compare their serial numbers instead. + auto& shape = base_obj->shape(); + if (&shape == cache.shape + && (!shape.is_unique() || shape.unique_shape_serial_number() == cache.unique_shape_serial_number)) { + return base_obj->get_direct(cache.property_offset.value()); + } + + CacheablePropertyMetadata cacheable_metadata; + auto value = TRY(base_obj->internal_get(name, this_value, &cacheable_metadata)); + + if (cacheable_metadata.type == CacheablePropertyMetadata::Type::OwnProperty) { + cache.shape = shape; + cache.property_offset = cacheable_metadata.property_offset.value(); + cache.unique_shape_serial_number = shape.unique_shape_serial_number(); + } + + return value; +} + +} diff --git a/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h new file mode 100644 index 0000000000..0171770210 --- /dev/null +++ b/Userland/Libraries/LibJS/Bytecode/CommonImplementations.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2021-2023, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace JS::Bytecode { + +ThrowCompletionOr> base_object_for_get(Bytecode::Interpreter&, Value base_value); +ThrowCompletionOr get_by_id(Bytecode::Interpreter&, IdentifierTableIndex, Value base_value, Value this_value, u32 cache_index); + +} diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index 20756165c1..73dce5e60b 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -1032,71 +1033,19 @@ ThrowCompletionOr SetLocal::execute_impl(Bytecode::Interpreter&) const __builtin_unreachable(); } -static ThrowCompletionOr> base_object_for_get(Bytecode::Interpreter& interpreter, Value base_value) -{ - auto& vm = interpreter.vm(); - if (base_value.is_object()) - return base_value.as_object(); - - // OPTIMIZATION: For various primitives we can avoid actually creating a new object for them. - if (base_value.is_string()) - return vm.current_realm()->intrinsics().string_prototype(); - if (base_value.is_number()) - return vm.current_realm()->intrinsics().number_prototype(); - if (base_value.is_boolean()) - return vm.current_realm()->intrinsics().boolean_prototype(); - - return base_value.to_object(vm); -} - -static ThrowCompletionOr get_by_id(Bytecode::Interpreter& interpreter, IdentifierTableIndex property, Value base_value, Value this_value, u32 cache_index) -{ - auto& vm = interpreter.vm(); - auto const& name = interpreter.current_executable().get_identifier(property); - auto& cache = interpreter.current_executable().property_lookup_caches[cache_index]; - - if (base_value.is_string()) { - auto string_value = TRY(base_value.as_string().get(vm, name)); - if (string_value.has_value()) { - interpreter.accumulator() = *string_value; - return {}; - } - } - - auto base_obj = TRY(base_object_for_get(interpreter, base_value)); - - // OPTIMIZATION: If the shape of the object hasn't changed, we can use the cached property offset. - // NOTE: Unique shapes don't change identity, so we compare their serial numbers instead. - auto& shape = base_obj->shape(); - if (&shape == cache.shape - && (!shape.is_unique() || shape.unique_shape_serial_number() == cache.unique_shape_serial_number)) { - interpreter.accumulator() = base_obj->get_direct(cache.property_offset.value()); - return {}; - } - - CacheablePropertyMetadata cacheable_metadata; - interpreter.accumulator() = TRY(base_obj->internal_get(name, this_value, &cacheable_metadata)); - - if (cacheable_metadata.type == CacheablePropertyMetadata::Type::OwnProperty) { - cache.shape = shape; - cache.property_offset = cacheable_metadata.property_offset.value(); - cache.unique_shape_serial_number = shape.unique_shape_serial_number(); - } - - return {}; -} - ThrowCompletionOr GetById::execute_impl(Bytecode::Interpreter& interpreter) const { auto base_value = interpreter.accumulator(); - return get_by_id(interpreter, m_property, base_value, base_value, m_cache_index); + interpreter.accumulator() = TRY(get_by_id(interpreter, m_property, base_value, base_value, m_cache_index)); + return {}; } ThrowCompletionOr GetByIdWithThis::execute_impl(Bytecode::Interpreter& interpreter) const { auto base_value = interpreter.accumulator(); auto this_value = interpreter.reg(m_this_value); - return get_by_id(interpreter, m_property, base_value, this_value, m_cache_index); + interpreter.accumulator() = TRY(get_by_id(interpreter, m_property, base_value, this_value, m_cache_index)); + return {}; } ThrowCompletionOr GetPrivateById::execute_impl(Bytecode::Interpreter& interpreter) const diff --git a/Userland/Libraries/LibJS/CMakeLists.txt b/Userland/Libraries/LibJS/CMakeLists.txt index dce7cc4e74..19c058a06b 100644 --- a/Userland/Libraries/LibJS/CMakeLists.txt +++ b/Userland/Libraries/LibJS/CMakeLists.txt @@ -3,6 +3,7 @@ set(SOURCES Bytecode/ASTCodegen.cpp Bytecode/BasicBlock.cpp Bytecode/CodeGenerationError.cpp + Bytecode/CommonImplementations.cpp Bytecode/Executable.cpp Bytecode/Generator.cpp Bytecode/IdentifierTable.cpp