mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 10:38:11 +00:00
LibJS/Bytecode: Begin moving shareable (JIT+Interpreter) stuff somewhere
There are a lot of native C++ functions that will be used by both the bytecode interpreter and jitted code. Let's put them in their own file instead of having them in Interpreter.cpp.
This commit is contained in:
parent
b923ca392d
commit
8905682a16
4 changed files with 86 additions and 56 deletions
63
Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp
Normal file
63
Userland/Libraries/LibJS/Bytecode/CommonImplementations.cpp
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021-2023, Andreas Kling <kling@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <LibJS/Bytecode/CommonImplementations.h>
|
||||||
|
#include <LibJS/Bytecode/Interpreter.h>
|
||||||
|
|
||||||
|
namespace JS::Bytecode {
|
||||||
|
|
||||||
|
ThrowCompletionOr<NonnullGCPtr<Object>> 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<Value> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
17
Userland/Libraries/LibJS/Bytecode/CommonImplementations.h
Normal file
17
Userland/Libraries/LibJS/Bytecode/CommonImplementations.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021-2023, Andreas Kling <kling@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibJS/Bytecode/IdentifierTable.h>
|
||||||
|
#include <LibJS/Runtime/Completion.h>
|
||||||
|
|
||||||
|
namespace JS::Bytecode {
|
||||||
|
|
||||||
|
ThrowCompletionOr<NonnullGCPtr<Object>> base_object_for_get(Bytecode::Interpreter&, Value base_value);
|
||||||
|
ThrowCompletionOr<Value> get_by_id(Bytecode::Interpreter&, IdentifierTableIndex, Value base_value, Value this_value, u32 cache_index);
|
||||||
|
|
||||||
|
}
|
|
@ -9,6 +9,7 @@
|
||||||
#include <AK/TemporaryChange.h>
|
#include <AK/TemporaryChange.h>
|
||||||
#include <LibJS/AST.h>
|
#include <LibJS/AST.h>
|
||||||
#include <LibJS/Bytecode/BasicBlock.h>
|
#include <LibJS/Bytecode/BasicBlock.h>
|
||||||
|
#include <LibJS/Bytecode/CommonImplementations.h>
|
||||||
#include <LibJS/Bytecode/Generator.h>
|
#include <LibJS/Bytecode/Generator.h>
|
||||||
#include <LibJS/Bytecode/Instruction.h>
|
#include <LibJS/Bytecode/Instruction.h>
|
||||||
#include <LibJS/Bytecode/Interpreter.h>
|
#include <LibJS/Bytecode/Interpreter.h>
|
||||||
|
@ -1032,71 +1033,19 @@ ThrowCompletionOr<void> SetLocal::execute_impl(Bytecode::Interpreter&) const
|
||||||
__builtin_unreachable();
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
static ThrowCompletionOr<NonnullGCPtr<Object>> 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<void> 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<void> GetById::execute_impl(Bytecode::Interpreter& interpreter) const
|
ThrowCompletionOr<void> GetById::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
auto base_value = interpreter.accumulator();
|
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<void> GetByIdWithThis::execute_impl(Bytecode::Interpreter& interpreter) const
|
ThrowCompletionOr<void> GetByIdWithThis::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
auto base_value = interpreter.accumulator();
|
auto base_value = interpreter.accumulator();
|
||||||
auto this_value = interpreter.reg(m_this_value);
|
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<void> GetPrivateById::execute_impl(Bytecode::Interpreter& interpreter) const
|
ThrowCompletionOr<void> GetPrivateById::execute_impl(Bytecode::Interpreter& interpreter) const
|
||||||
|
|
|
@ -3,6 +3,7 @@ set(SOURCES
|
||||||
Bytecode/ASTCodegen.cpp
|
Bytecode/ASTCodegen.cpp
|
||||||
Bytecode/BasicBlock.cpp
|
Bytecode/BasicBlock.cpp
|
||||||
Bytecode/CodeGenerationError.cpp
|
Bytecode/CodeGenerationError.cpp
|
||||||
|
Bytecode/CommonImplementations.cpp
|
||||||
Bytecode/Executable.cpp
|
Bytecode/Executable.cpp
|
||||||
Bytecode/Generator.cpp
|
Bytecode/Generator.cpp
|
||||||
Bytecode/IdentifierTable.cpp
|
Bytecode/IdentifierTable.cpp
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue