/* * Copyright (c) 2021-2023, Andreas Kling * Copyright (c) 2021, Linus Groh * Copyright (c) 2021, Gunnar Beutner * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace JS { class FunctionExpression; } namespace JS::Bytecode::Op { class Load final : public Instruction { public: explicit Load(Register src) : Instruction(Type::Load) , m_src(src) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: Register m_src; }; class LoadImmediate final : public Instruction { public: explicit LoadImmediate(Value value) : Instruction(Type::LoadImmediate) , m_value(value) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: Value m_value; }; class Store final : public Instruction { public: explicit Store(Register dst) : Instruction(Type::Store) , m_dst(dst) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; Register dst() const { return m_dst; } private: Register m_dst; }; #define JS_ENUMERATE_COMMON_BINARY_OPS(O) \ O(Add, add) \ O(Sub, sub) \ O(Mul, mul) \ O(Div, div) \ O(Exp, exp) \ O(Mod, mod) \ O(In, in) \ O(InstanceOf, instance_of) \ O(GreaterThan, greater_than) \ O(GreaterThanEquals, greater_than_equals) \ O(LessThan, less_than) \ O(LessThanEquals, less_than_equals) \ O(LooselyInequals, abstract_inequals) \ O(LooselyEquals, abstract_equals) \ O(StrictlyInequals, typed_inequals) \ O(StrictlyEquals, typed_equals) \ O(BitwiseAnd, bitwise_and) \ O(BitwiseOr, bitwise_or) \ O(BitwiseXor, bitwise_xor) \ O(LeftShift, left_shift) \ O(RightShift, right_shift) \ O(UnsignedRightShift, unsigned_right_shift) #define JS_DECLARE_COMMON_BINARY_OP(OpTitleCase, op_snake_case) \ class OpTitleCase final : public Instruction { \ public: \ explicit OpTitleCase(Register lhs_reg) \ : Instruction(Type::OpTitleCase) \ , m_lhs_reg(lhs_reg) \ { \ } \ \ ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; \ DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; \ \ private: \ Register m_lhs_reg; \ }; JS_ENUMERATE_COMMON_BINARY_OPS(JS_DECLARE_COMMON_BINARY_OP) #undef JS_DECLARE_COMMON_BINARY_OP #define JS_ENUMERATE_COMMON_UNARY_OPS(O) \ O(BitwiseNot, bitwise_not) \ O(Not, not_) \ O(UnaryPlus, unary_plus) \ O(UnaryMinus, unary_minus) \ O(Typeof, typeof_) #define JS_DECLARE_COMMON_UNARY_OP(OpTitleCase, op_snake_case) \ class OpTitleCase final : public Instruction { \ public: \ OpTitleCase() \ : Instruction(Type::OpTitleCase) \ { \ } \ \ ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; \ DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; \ }; JS_ENUMERATE_COMMON_UNARY_OPS(JS_DECLARE_COMMON_UNARY_OP) #undef JS_DECLARE_COMMON_UNARY_OP class NewString final : public Instruction { public: explicit NewString(StringTableIndex string) : Instruction(Type::NewString) , m_string(string) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: StringTableIndex m_string; }; class NewObject final : public Instruction { public: NewObject() : Instruction(Type::NewObject) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; }; class NewRegExp final : public Instruction { public: NewRegExp(StringTableIndex source_index, StringTableIndex flags_index, RegexTableIndex regex_index) : Instruction(Type::NewRegExp) , m_source_index(source_index) , m_flags_index(flags_index) , m_regex_index(regex_index) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: StringTableIndex m_source_index; StringTableIndex m_flags_index; RegexTableIndex m_regex_index; }; #define JS_ENUMERATE_NEW_BUILTIN_ERROR_OPS(O) \ O(TypeError) #define JS_DECLARE_NEW_BUILTIN_ERROR_OP(ErrorName) \ class New##ErrorName final : public Instruction { \ public: \ explicit New##ErrorName(StringTableIndex error_string) \ : Instruction(Type::New##ErrorName) \ , m_error_string(error_string) \ { \ } \ \ ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; \ DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; \ \ private: \ StringTableIndex m_error_string; \ }; JS_ENUMERATE_NEW_BUILTIN_ERROR_OPS(JS_DECLARE_NEW_BUILTIN_ERROR_OP) #undef JS_DECLARE_NEW_BUILTIN_ERROR_OP // NOTE: This instruction is variable-width depending on the number of excluded names class CopyObjectExcludingProperties final : public Instruction { public: CopyObjectExcludingProperties(Register from_object, Vector const& excluded_names) : Instruction(Type::CopyObjectExcludingProperties) , m_from_object(from_object) , m_excluded_names_count(excluded_names.size()) { for (size_t i = 0; i < m_excluded_names_count; i++) m_excluded_names[i] = excluded_names[i]; } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; size_t length_impl() const { return sizeof(*this) + sizeof(Register) * m_excluded_names_count; } private: Register m_from_object; size_t m_excluded_names_count { 0 }; Register m_excluded_names[]; }; class NewBigInt final : public Instruction { public: explicit NewBigInt(Crypto::SignedBigInteger bigint) : Instruction(Type::NewBigInt) , m_bigint(move(bigint)) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: Crypto::SignedBigInteger m_bigint; }; // NOTE: This instruction is variable-width depending on the number of elements! class NewArray final : public Instruction { public: NewArray() : Instruction(Type::NewArray) , m_element_count(0) { } explicit NewArray(AK::Array const& elements_range) : Instruction(Type::NewArray) , m_element_count(elements_range[1].index() - elements_range[0].index() + 1) { m_elements[0] = elements_range[0]; m_elements[1] = elements_range[1]; } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; size_t length_impl() const { return sizeof(*this) + sizeof(Register) * (m_element_count == 0 ? 0 : 2); } Register start() const { VERIFY(m_element_count); return m_elements[0]; } Register end() const { VERIFY(m_element_count); return m_elements[1]; } size_t element_count() const { return m_element_count; } private: size_t m_element_count { 0 }; Register m_elements[]; }; class Append final : public Instruction { public: Append(Register lhs, bool is_spread) : Instruction(Type::Append) , m_lhs(lhs) , m_is_spread(is_spread) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: Register m_lhs; bool m_is_spread = false; }; class ImportCall final : public Instruction { public: ImportCall(Register specifier, Register options) : Instruction(Type::ImportCall) , m_specifier(specifier) , m_options(options) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: Register m_specifier; Register m_options; }; class IteratorToArray final : public Instruction { public: IteratorToArray() : Instruction(Type::IteratorToArray) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; }; class ConcatString final : public Instruction { public: explicit ConcatString(Register lhs) : Instruction(Type::ConcatString) , m_lhs(lhs) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: Register m_lhs; }; enum class EnvironmentMode { Lexical, Var, }; class CreateLexicalEnvironment final : public Instruction { public: explicit CreateLexicalEnvironment() : Instruction(Type::CreateLexicalEnvironment) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; }; class EnterObjectEnvironment final : public Instruction { public: explicit EnterObjectEnvironment() : Instruction(Type::EnterObjectEnvironment) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; }; class CreateVariable final : public Instruction { public: explicit CreateVariable(IdentifierTableIndex identifier, EnvironmentMode mode, bool is_immutable, bool is_global = false) : Instruction(Type::CreateVariable) , m_identifier(identifier) , m_mode(mode) , m_is_immutable(is_immutable) , m_is_global(is_global) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: IdentifierTableIndex m_identifier; EnvironmentMode m_mode; bool m_is_immutable : 4 { false }; bool m_is_global : 4 { false }; }; class SetVariable final : public Instruction { public: enum class InitializationMode { Initialize, Set, InitializeOrSet, }; explicit SetVariable(IdentifierTableIndex identifier, InitializationMode initialization_mode = InitializationMode::Set, EnvironmentMode mode = EnvironmentMode::Lexical) : Instruction(Type::SetVariable) , m_identifier(identifier) , m_mode(mode) , m_initialization_mode(initialization_mode) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; IdentifierTableIndex identifier() const { return m_identifier; } private: IdentifierTableIndex m_identifier; EnvironmentMode m_mode; InitializationMode m_initialization_mode { InitializationMode::Set }; }; class SetLocal final : public Instruction { public: explicit SetLocal(size_t index) : Instruction(Type::SetLocal) , m_index(index) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; size_t index() const { return m_index; } private: size_t m_index; }; class GetVariable final : public Instruction { public: explicit GetVariable(IdentifierTableIndex identifier) : Instruction(Type::GetVariable) , m_identifier(identifier) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; IdentifierTableIndex identifier() const { return m_identifier; } private: IdentifierTableIndex m_identifier; Optional mutable m_cached_environment_coordinate; }; class GetGlobal final : public Instruction { public: explicit GetGlobal(IdentifierTableIndex identifier, u32 cache_index) : Instruction(Type::GetGlobal) , m_identifier(identifier) , m_cache_index(cache_index) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: IdentifierTableIndex m_identifier; u32 m_cache_index { 0 }; }; class GetLocal final : public Instruction { public: explicit GetLocal(size_t index) : Instruction(Type::GetLocal) , m_index(index) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; size_t index() const { return m_index; } private: size_t m_index; }; class DeleteVariable final : public Instruction { public: explicit DeleteVariable(IdentifierTableIndex identifier) : Instruction(Type::DeleteVariable) , m_identifier(identifier) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; IdentifierTableIndex identifier() const { return m_identifier; } private: IdentifierTableIndex m_identifier; }; class GetById final : public Instruction { public: GetById(IdentifierTableIndex property, u32 cache_index) : Instruction(Type::GetById) , m_property(property) , m_cache_index(cache_index) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: IdentifierTableIndex m_property; u32 m_cache_index { 0 }; }; class GetByIdWithThis final : public Instruction { public: GetByIdWithThis(IdentifierTableIndex property, Register this_value, u32 cache_index) : Instruction(Type::GetByIdWithThis) , m_property(property) , m_this_value(this_value) , m_cache_index(cache_index) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: IdentifierTableIndex m_property; Register m_this_value; u32 m_cache_index { 0 }; }; class GetPrivateById final : public Instruction { public: explicit GetPrivateById(IdentifierTableIndex property) : Instruction(Type::GetPrivateById) , m_property(property) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: IdentifierTableIndex m_property; }; class HasPrivateId final : public Instruction { public: explicit HasPrivateId(IdentifierTableIndex property) : Instruction(Type::HasPrivateId) , m_property(property) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: IdentifierTableIndex m_property; }; enum class PropertyKind { Getter, Setter, KeyValue, DirectKeyValue, // Used for Object expressions. Always sets an own property, never calls a setter. Spread, ProtoSetter, }; class PutById final : public Instruction { public: explicit PutById(Register base, IdentifierTableIndex property, PropertyKind kind = PropertyKind::KeyValue) : Instruction(Type::PutById) , m_base(base) , m_property(property) , m_kind(kind) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: Register m_base; IdentifierTableIndex m_property; PropertyKind m_kind; }; class PutByIdWithThis final : public Instruction { public: PutByIdWithThis(Register base, Register this_value, IdentifierTableIndex property, PropertyKind kind = PropertyKind::KeyValue) : Instruction(Type::PutByIdWithThis) , m_base(base) , m_this_value(this_value) , m_property(property) , m_kind(kind) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: Register m_base; Register m_this_value; IdentifierTableIndex m_property; PropertyKind m_kind; }; class PutPrivateById final : public Instruction { public: explicit PutPrivateById(Register base, IdentifierTableIndex property, PropertyKind kind = PropertyKind::KeyValue) : Instruction(Type::PutPrivateById) , m_base(base) , m_property(property) , m_kind(kind) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: Register m_base; IdentifierTableIndex m_property; PropertyKind m_kind; }; class DeleteById final : public Instruction { public: explicit DeleteById(IdentifierTableIndex property) : Instruction(Type::DeleteById) , m_property(property) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: IdentifierTableIndex m_property; }; class DeleteByIdWithThis final : public Instruction { public: DeleteByIdWithThis(Register this_value, IdentifierTableIndex property) : Instruction(Type::DeleteByIdWithThis) , m_this_value(this_value) , m_property(property) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: Register m_this_value; IdentifierTableIndex m_property; }; class GetByValue final : public Instruction { public: explicit GetByValue(Register base) : Instruction(Type::GetByValue) , m_base(base) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: Register m_base; }; class GetByValueWithThis final : public Instruction { public: GetByValueWithThis(Register base, Register this_value) : Instruction(Type::GetByValueWithThis) , m_base(base) , m_this_value(this_value) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: Register m_base; Register m_this_value; }; class PutByValue final : public Instruction { public: PutByValue(Register base, Register property, PropertyKind kind = PropertyKind::KeyValue) : Instruction(Type::PutByValue) , m_base(base) , m_property(property) , m_kind(kind) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: Register m_base; Register m_property; PropertyKind m_kind; }; class PutByValueWithThis final : public Instruction { public: PutByValueWithThis(Register base, Register property, Register this_value, PropertyKind kind = PropertyKind::KeyValue) : Instruction(Type::PutByValueWithThis) , m_base(base) , m_property(property) , m_this_value(this_value) , m_kind(kind) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: Register m_base; Register m_property; Register m_this_value; PropertyKind m_kind; }; class DeleteByValue final : public Instruction { public: DeleteByValue(Register base) : Instruction(Type::DeleteByValue) , m_base(base) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: Register m_base; }; class DeleteByValueWithThis final : public Instruction { public: DeleteByValueWithThis(Register base, Register this_value) : Instruction(Type::DeleteByValueWithThis) , m_base(base) , m_this_value(this_value) { } ThrowCompletionOr execute_impl(Bytecode::Interpreter&) const; DeprecatedString to_deprecated_string_impl(Bytecode::Executable const&) const; private: Register m_base; Register m_this_value; }; class Jump : public Instruction { public: constexpr static bool IsTerminator = true; explicit Jump(Type type, Optional