/* * Copyright (c) 2021-2025, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace JS { struct PropertyKeyAndEnumerableFlag { JS::PropertyKey key; bool enumerable { false }; }; } namespace AK { template<> struct Traits : public DefaultTraits { static unsigned hash(JS::PropertyKeyAndEnumerableFlag const& entry) { return Traits::hash(entry.key); } static bool equals(JS::PropertyKeyAndEnumerableFlag const& a, JS::PropertyKeyAndEnumerableFlag const& b) { return Traits::equals(a.key, b.key); } }; } namespace JS::Bytecode { bool g_dump_bytecode = false; static ByteString format_operand(StringView name, Operand operand, Bytecode::Executable const& executable) { StringBuilder builder; if (!name.is_empty()) builder.appendff("\033[32m{}\033[0m:", name); switch (operand.type()) { case Operand::Type::Register: if (operand.index() == Register::this_value().index()) { builder.appendff("\033[33mthis\033[0m"); } else { builder.appendff("\033[33mreg{}\033[0m", operand.index()); } break; case Operand::Type::Local: builder.appendff("\033[34m{}~{}\033[0m", executable.local_variable_names[operand.index() - executable.local_index_base], operand.index() - executable.local_index_base); break; case Operand::Type::Constant: { builder.append("\033[36m"sv); auto value = executable.constants[operand.index() - executable.number_of_registers]; if (value.is_special_empty_value()) builder.append(""sv); else if (value.is_boolean()) builder.appendff("Bool({})", value.as_bool() ? "true"sv : "false"sv); else if (value.is_int32()) builder.appendff("Int32({})", value.as_i32()); else if (value.is_double()) builder.appendff("Double({})", value.as_double()); else if (value.is_bigint()) builder.appendff("BigInt({})", value.as_bigint().to_byte_string()); else if (value.is_string()) builder.appendff("String(\"{}\")", value.as_string().utf8_string_view()); else if (value.is_undefined()) builder.append("Undefined"sv); else if (value.is_null()) builder.append("Null"sv); else builder.appendff("Value: {}", value); builder.append("\033[0m"sv); break; } default: VERIFY_NOT_REACHED(); } return builder.to_byte_string(); } static ByteString format_operand_list(StringView name, ReadonlySpan operands, Bytecode::Executable const& executable) { StringBuilder builder; if (!name.is_empty()) builder.appendff("\033[32m{}\033[0m:[", name); for (size_t i = 0; i < operands.size(); ++i) { if (i != 0) builder.append(", "sv); builder.appendff("{}", format_operand(""sv, operands[i], executable)); } builder.append("]"sv); return builder.to_byte_string(); } static ByteString format_value_list(StringView name, ReadonlySpan values) { StringBuilder builder; if (!name.is_empty()) builder.appendff("\033[32m{}\033[0m:[", name); builder.join(", "sv, values); builder.append("]"sv); return builder.to_byte_string(); } ALWAYS_INLINE static ThrowCompletionOr loosely_inequals(VM& vm, Value src1, Value src2) { if (src1.tag() == src2.tag()) { if (src1.is_int32() || src1.is_object() || src1.is_boolean() || src1.is_nullish()) return Value(src1.encoded() != src2.encoded()); } return Value(!TRY(is_loosely_equal(vm, src1, src2))); } ALWAYS_INLINE static ThrowCompletionOr loosely_equals(VM& vm, Value src1, Value src2) { if (src1.tag() == src2.tag()) { if (src1.is_int32() || src1.is_object() || src1.is_boolean() || src1.is_nullish()) return Value(src1.encoded() == src2.encoded()); } return Value(TRY(is_loosely_equal(vm, src1, src2))); } ALWAYS_INLINE static ThrowCompletionOr strict_inequals(VM&, Value src1, Value src2) { if (src1.tag() == src2.tag()) { if (src1.is_int32() || src1.is_object() || src1.is_boolean() || src1.is_nullish()) return Value(src1.encoded() != src2.encoded()); } return Value(!is_strictly_equal(src1, src2)); } ALWAYS_INLINE static ThrowCompletionOr strict_equals(VM&, Value src1, Value src2) { if (src1.tag() == src2.tag()) { if (src1.is_int32() || src1.is_object() || src1.is_boolean() || src1.is_nullish()) return Value(src1.encoded() == src2.encoded()); } return Value(is_strictly_equal(src1, src2)); } Interpreter::Interpreter(VM& vm) : m_vm(vm) { } Interpreter::~Interpreter() { } ALWAYS_INLINE Value Interpreter::get(Operand op) const { return m_registers_and_constants_and_locals.data()[op.index()]; } ALWAYS_INLINE void Interpreter::set(Operand op, Value value) { m_registers_and_constants_and_locals.data()[op.index()] = value; } ALWAYS_INLINE Value Interpreter::do_yield(Value value, Optional