/*
 * Copyright (c) 2026, the Ladybird developers.
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

// Small build-time tool that prints struct field offsets as DSL constants.
// Compiled with the same flags as LibJS so layouts match exactly.

#include <AK/Format.h>
#include <AK/StringBase.h>
#include <AK/Utf16StringData.h>
#include <LibJS/Bytecode/Builtins.h>
#include <LibJS/Bytecode/Executable.h>
#include <LibJS/Bytecode/PropertyNameIterator.h>
#include <LibJS/Bytecode/PutKind.h>
#include <LibJS/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/DeclarativeEnvironment.h>
#include <LibJS/Runtime/ECMAScriptFunctionObject.h>
#include <LibJS/Runtime/ExecutionContext.h>
#include <LibJS/Runtime/FunctionObject.h>
#include <LibJS/Runtime/GlobalEnvironment.h>
#include <LibJS/Runtime/IndexedProperties.h>
#include <LibJS/Runtime/NativeFunction.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/PrimitiveString.h>
#include <LibJS/Runtime/Realm.h>
#include <LibJS/Runtime/Shape.h>
#include <LibJS/Runtime/SharedFunctionInstanceData.h>
#include <LibJS/Runtime/TypedArray.h>
#include <LibJS/Runtime/VM.h>

#define EMIT_OFFSET(name, type, member) \
    outln("const " #name " = {}", offsetof(type, member))

#define EMIT_SIZEOF(name, type) \
    outln("const " #name " = {}", sizeof(type))

int main()
{
    using namespace JS;
    using namespace JS::Bytecode;

    struct PrimitiveStringLayoutAccessor : PrimitiveString {
        using PrimitiveString::DeferredKind;
        using PrimitiveString::m_deferred_kind;
        using PrimitiveString::m_utf16_string;
    };
    outln("# Generated by gen_asm_offsets -- DO NOT EDIT\n");

    // Object layout
    outln("# Object layout");
    EMIT_OFFSET(OBJECT_SHAPE, Object, m_shape);
    EMIT_OFFSET(OBJECT_NAMED_PROPERTIES, Object, m_named_properties);
    EMIT_OFFSET(OBJECT_INDEXED_ELEMENTS, Object, m_indexed_elements);
    EMIT_OFFSET(OBJECT_INDEXED_STORAGE_KIND, Object, m_indexed_storage_kind);
    EMIT_OFFSET(OBJECT_INDEXED_ARRAY_LIKE_SIZE, Object, m_indexed_array_like_size);
    EMIT_SIZEOF(OBJECT_SIZE, Object);

    // Object flags byte
    outln("\n# Object flags");
    EMIT_OFFSET(OBJECT_FLAGS, Object, m_flags);
    outln("const OBJECT_FLAG_HAS_MAGICAL_LENGTH = {}", Object::Flag::HasMagicalLengthProperty);
    outln("const OBJECT_FLAG_MAY_INTERFERE = {}", Object::Flag::MayInterfereWithIndexedPropertyAccess);
    outln("const OBJECT_FLAG_IS_TYPED_ARRAY = {}", Object::Flag::IsTypedArray);
    outln("const OBJECT_FLAG_IS_FUNCTION = {}", Object::Flag::IsFunction);
    outln("const OBJECT_FLAG_IS_ECMASCRIPT_FUNCTION_OBJECT = {}", Object::Flag::IsECMAScriptFunctionObject);
    outln("const OBJECT_FLAG_IS_RAW_NATIVE_FUNCTION = {}", Object::Flag::IsRawNativeFunction);

    // Shape layout
    outln("\n# Shape layout");
    EMIT_OFFSET(SHAPE_REALM, Shape, m_realm);
    EMIT_OFFSET(SHAPE_PROTOTYPE, Shape, m_prototype);
    EMIT_OFFSET(SHAPE_DICTIONARY_GENERATION, Shape, m_dictionary_generation);
    EMIT_SIZEOF(SHAPE_SIZE, Shape);

    // PropertyLookupCache layout
    outln("\n# PropertyLookupCache layout");
    EMIT_OFFSET(PROPERTY_LOOKUP_CACHE_DATA, PropertyLookupCache, m_data);
    outln("const PROPERTY_LOOKUP_CACHE_POLYMORPHIC_DATA_TAG = {}", PropertyLookupCache::polymorphic_data_tag);
    EMIT_SIZEOF(PROPERTY_LOOKUP_CACHE_SIZE, PropertyLookupCache);

    // PropertyLookupCache::Entry layout
    outln("\n# PropertyLookupCache::Entry layout");
    EMIT_OFFSET(PROPERTY_LOOKUP_CACHE_ENTRY_PROPERTY_OFFSET, PropertyLookupCache::Entry, property_offset);
    EMIT_OFFSET(PROPERTY_LOOKUP_CACHE_ENTRY_DICTIONARY_GENERATION, PropertyLookupCache::Entry, shape_dictionary_generation);
    EMIT_OFFSET(PROPERTY_LOOKUP_CACHE_ENTRY_FROM_SHAPE, PropertyLookupCache::Entry, from_shape);
    EMIT_OFFSET(PROPERTY_LOOKUP_CACHE_ENTRY_SHAPE, PropertyLookupCache::Entry, shape);
    EMIT_OFFSET(PROPERTY_LOOKUP_CACHE_ENTRY_PROTOTYPE, PropertyLookupCache::Entry, prototype);
    EMIT_OFFSET(PROPERTY_LOOKUP_CACHE_ENTRY_PROTOTYPE_CHAIN_VALIDITY, PropertyLookupCache::Entry, prototype_chain_validity);
    EMIT_SIZEOF(PROPERTY_LOOKUP_CACHE_ENTRY_SIZE, PropertyLookupCache::Entry);

    // ObjectPropertyIteratorCacheData layout
    outln("\n# ObjectPropertyIteratorCacheData layout");
    EMIT_OFFSET(OBJECT_PROPERTY_ITERATOR_CACHE_DATA_PROPERTIES, ObjectPropertyIteratorCacheData, m_properties);
    EMIT_OFFSET(OBJECT_PROPERTY_ITERATOR_CACHE_DATA_PROPERTY_VALUES, ObjectPropertyIteratorCacheData, m_property_values);
    EMIT_OFFSET(OBJECT_PROPERTY_ITERATOR_CACHE_DATA_SHAPE, ObjectPropertyIteratorCacheData, m_shape);
    EMIT_OFFSET(OBJECT_PROPERTY_ITERATOR_CACHE_DATA_PROTOTYPE_CHAIN_VALIDITY, ObjectPropertyIteratorCacheData, m_prototype_chain_validity);
    EMIT_OFFSET(OBJECT_PROPERTY_ITERATOR_CACHE_DATA_INDEXED_PROPERTY_COUNT, ObjectPropertyIteratorCacheData, m_indexed_property_count);
    EMIT_OFFSET(OBJECT_PROPERTY_ITERATOR_CACHE_DATA_SHAPE_DICTIONARY_GENERATION, ObjectPropertyIteratorCacheData, m_shape_dictionary_generation);
    EMIT_OFFSET(OBJECT_PROPERTY_ITERATOR_CACHE_DATA_FAST_PATH, ObjectPropertyIteratorCacheData, m_fast_path);

    // ObjectPropertyIteratorCache layout
    outln("\n# ObjectPropertyIteratorCache layout");
    EMIT_OFFSET(OBJECT_PROPERTY_ITERATOR_CACHE_DATA_PTR, ObjectPropertyIteratorCache, data);
    EMIT_OFFSET(OBJECT_PROPERTY_ITERATOR_CACHE_REUSABLE_PROPERTY_NAME_ITERATOR, ObjectPropertyIteratorCache, reusable_property_name_iterator);

    // PropertyNameIterator layout
    outln("\n# PropertyNameIterator layout");
    EMIT_OFFSET(PROPERTY_NAME_ITERATOR_OBJECT, PropertyNameIterator, m_object);
    EMIT_OFFSET(PROPERTY_NAME_ITERATOR_PROPERTY_CACHE, PropertyNameIterator, m_property_cache);
    EMIT_OFFSET(PROPERTY_NAME_ITERATOR_SHAPE, PropertyNameIterator, m_shape);
    EMIT_OFFSET(PROPERTY_NAME_ITERATOR_PROTOTYPE_CHAIN_VALIDITY, PropertyNameIterator, m_prototype_chain_validity);
    EMIT_OFFSET(PROPERTY_NAME_ITERATOR_ITERATOR_CACHE_SLOT, PropertyNameIterator, m_iterator_cache_slot);
    EMIT_OFFSET(PROPERTY_NAME_ITERATOR_INDEXED_PROPERTY_COUNT, PropertyNameIterator, m_indexed_property_count);
    EMIT_OFFSET(PROPERTY_NAME_ITERATOR_NEXT_INDEXED_PROPERTY, PropertyNameIterator, m_next_indexed_property);
    EMIT_OFFSET(PROPERTY_NAME_ITERATOR_NEXT_PROPERTY, PropertyNameIterator, m_next_property);
    EMIT_OFFSET(PROPERTY_NAME_ITERATOR_SHAPE_IS_DICTIONARY, PropertyNameIterator, m_shape_is_dictionary);
    EMIT_OFFSET(PROPERTY_NAME_ITERATOR_SHAPE_DICTIONARY_GENERATION, PropertyNameIterator, m_shape_dictionary_generation);
    EMIT_OFFSET(PROPERTY_NAME_ITERATOR_FAST_PATH, PropertyNameIterator, m_fast_path);

    // Executable layout
    outln("\n# Executable layout");
    EMIT_OFFSET(EXECUTABLE_CONSTANTS, Executable, constants);
    EMIT_OFFSET(EXECUTABLE_PROPERTY_LOOKUP_CACHES, Executable, property_lookup_caches);
    EMIT_OFFSET(EXECUTABLE_GLOBAL_VARIABLE_CACHES, Executable, global_variable_caches);
    EMIT_OFFSET(EXECUTABLE_ENVIRONMENT_COORDINATE_CACHES, Executable, environment_coordinate_caches);
    EMIT_OFFSET(EXECUTABLE_REGISTERS_AND_LOCALS_COUNT, Executable, registers_and_locals_count);
    EMIT_OFFSET(EXECUTABLE_REGISTERS_AND_LOCALS_AND_CONSTANTS_COUNT, Executable, registers_and_locals_and_constants_count);
    EMIT_OFFSET(EXECUTABLE_ASM_CONSTANTS_SIZE, Executable, asm_constants_size);
    EMIT_OFFSET(EXECUTABLE_ASM_CONSTANTS_DATA, Executable, asm_constants_data);

    // ExecutionContext layout
    outln("\n# ExecutionContext layout");
    VERIFY(alignof(ExecutionContext) == sizeof(Value));
    EMIT_OFFSET(EXECUTION_CONTEXT_EXECUTABLE, ExecutionContext, executable);
    EMIT_OFFSET(EXECUTION_CONTEXT_FUNCTION, ExecutionContext, function);
    EMIT_OFFSET(EXECUTION_CONTEXT_REALM, ExecutionContext, realm);
    EMIT_OFFSET(EXECUTION_CONTEXT_SCRIPT_OR_MODULE, ExecutionContext, script_or_module);
    EMIT_OFFSET(EXECUTION_CONTEXT_LEXICAL_ENVIRONMENT, ExecutionContext, lexical_environment);
    EMIT_OFFSET(EXECUTION_CONTEXT_VARIABLE_ENVIRONMENT, ExecutionContext, variable_environment);
    EMIT_OFFSET(EXECUTION_CONTEXT_PRIVATE_ENVIRONMENT, ExecutionContext, private_environment);
    EMIT_OFFSET(EXECUTION_CONTEXT_SKIP_WHEN_DETERMINING_INCUMBENT_COUNTER, ExecutionContext, skip_when_determining_incumbent_counter);
    EMIT_OFFSET(EXECUTION_CONTEXT_YIELD_CONTINUATION, ExecutionContext, yield_continuation);
    EMIT_OFFSET(EXECUTION_CONTEXT_YIELD_IS_AWAIT, ExecutionContext, yield_is_await);
    EMIT_OFFSET(EXECUTION_CONTEXT_YIELD_VALUE_IS_ITERATOR_RESULT, ExecutionContext, yield_value_is_iterator_result);
    EMIT_OFFSET(EXECUTION_CONTEXT_CALLER_IS_CONSTRUCT, ExecutionContext, caller_is_construct);
    EMIT_OFFSET(EXECUTION_CONTEXT_THIS_VALUE, ExecutionContext, this_value);
    EMIT_OFFSET(EXECUTION_CONTEXT_CALLER_FRAME, ExecutionContext, caller_frame);
    EMIT_OFFSET(EXECUTION_CONTEXT_PASSED_ARGUMENT_COUNT, ExecutionContext, passed_argument_count);
    EMIT_OFFSET(EXECUTION_CONTEXT_CALLER_RETURN_PC, ExecutionContext, caller_return_pc);
    EMIT_OFFSET(EXECUTION_CONTEXT_CALLER_DST_RAW, ExecutionContext, caller_dst_raw);
    EMIT_OFFSET(EXECUTION_CONTEXT_PROGRAM_COUNTER, ExecutionContext, program_counter);
    EMIT_OFFSET(EXECUTION_CONTEXT_REGISTERS_AND_CONSTANTS_AND_LOCALS_AND_ARGUMENTS_COUNT, ExecutionContext, registers_and_constants_and_locals_and_arguments_count);
    EMIT_OFFSET(EXECUTION_CONTEXT_ARGUMENT_COUNT, ExecutionContext, argument_count);
    EMIT_SIZEOF(SIZEOF_EXECUTION_CONTEXT, ExecutionContext);
    outln("const ALIGNOF_EXECUTION_CONTEXT = {}", alignof(ExecutionContext));
    outln("const EXECUTION_CONTEXT_NO_YIELD_CONTINUATION = {}", ExecutionContext::no_yield_continuation);
    outln("const SIZEOF_SCRIPT_OR_MODULE = {}", sizeof(ScriptOrModule));

    // InterpreterStack layout
    outln("\n# InterpreterStack layout");
    EMIT_OFFSET(INTERPRETER_STACK_LIMIT, InterpreterStack, m_limit);
    EMIT_OFFSET(INTERPRETER_STACK_TOP, InterpreterStack, m_top);

    // Realm layout
    outln("\n# Realm layout");
    EMIT_OFFSET(REALM_GLOBAL_ENVIRONMENT, Realm, m_global_environment);
    EMIT_OFFSET(REALM_GLOBAL_OBJECT, Realm, m_global_object);
    EMIT_OFFSET(REALM_GLOBAL_DECLARATIVE_ENVIRONMENT, Realm, m_global_declarative_environment);

    // VM layout
    outln("\n# VM layout");
    EMIT_OFFSET(VM_RUNNING_EXECUTION_CONTEXT, VM, m_running_execution_context);
    EMIT_OFFSET(VM_INTERPRETER_STACK, VM, m_interpreter_stack);
    EMIT_OFFSET(VM_STACK_INFO, VM, m_stack_info);
    EMIT_OFFSET(VM_EXECUTION_GENERATION, VM, m_execution_generation);
    outln("const VM_INTERPRETER_STACK_TOP = {}", offsetof(VM, m_interpreter_stack) + offsetof(InterpreterStack, m_top));
#if defined(HAS_ADDRESS_SANITIZER)
    outln("const VM_STACK_SPACE_LIMIT = {}", 96 * KiB);
#else
    outln("const VM_STACK_SPACE_LIMIT = {}", 32 * KiB);
#endif

    // StackInfo layout
    outln("\n# StackInfo layout");
    EMIT_OFFSET(STACK_INFO_BASE, StackInfo, m_base);

    // IndexedStorageKind enum values
    outln("\n# IndexedStorageKind enum values");
    outln("const INDEXED_STORAGE_KIND_NONE = {}", static_cast<u8>(IndexedStorageKind::None));
    outln("const INDEXED_STORAGE_KIND_PACKED = {}", static_cast<u8>(IndexedStorageKind::Packed));
    outln("const INDEXED_STORAGE_KIND_HOLEY = {}", static_cast<u8>(IndexedStorageKind::Holey));
    outln("const INDEXED_STORAGE_KIND_DICTIONARY = {}", static_cast<u8>(IndexedStorageKind::Dictionary));

    // ObjectPropertyIteratorFastPath enum values
    outln("\n# ObjectPropertyIteratorFastPath enum values");
    outln("const OBJECT_PROPERTY_ITERATOR_FAST_PATH_NONE = {}", static_cast<u8>(ObjectPropertyIteratorFastPath::None));
    outln("const OBJECT_PROPERTY_ITERATOR_FAST_PATH_PLAIN_NAMED = {}", static_cast<u8>(ObjectPropertyIteratorFastPath::PlainNamed));
    outln("const OBJECT_PROPERTY_ITERATOR_FAST_PATH_PACKED_INDEXED = {}", static_cast<u8>(ObjectPropertyIteratorFastPath::PackedIndexed));

    // Vector<Value> layout (used for bytecode)
    outln("\n# Vector<Value> layout");
    {
        Vector<Value> v;
        auto base = reinterpret_cast<uintptr_t>(&v);
        auto vec_data = reinterpret_cast<uintptr_t>(&v.m_metadata.outline_buffer) - base;
        auto vec_size = reinterpret_cast<uintptr_t>(&v.m_size) - base;
        outln("const VECTOR_DATA = {}", vec_data);
        outln("const VECTOR_SIZE = {}", vec_size);

        // Composite offset for Executable.bytecode data pointer
        outln("const EXECUTABLE_BYTECODE_DATA = {}", offsetof(Executable, bytecode) + InstructionStream::data_member_offset());
        outln("const EXECUTABLE_PROPERTY_LOOKUP_CACHES_DATA = {}", offsetof(Executable, property_lookup_caches) + vec_data);
        outln("const EXECUTABLE_GLOBAL_VARIABLE_CACHES_DATA = {}", offsetof(Executable, global_variable_caches) + vec_data);
        outln("const EXECUTABLE_ENVIRONMENT_COORDINATE_CACHES_DATA = {}", offsetof(Executable, environment_coordinate_caches) + vec_data);
        outln("const EXECUTABLE_CONSTANTS_DATA = {}", offsetof(Executable, constants) + vec_data);
        outln("const EXECUTABLE_CONSTANTS_SIZE = {}", offsetof(Executable, constants) + vec_size);
        outln("const OBJECT_PROPERTY_ITERATOR_CACHE_DATA_PROPERTY_VALUES_DATA = {}", offsetof(ObjectPropertyIteratorCacheData, m_property_values) + vec_data);
        outln("const OBJECT_PROPERTY_ITERATOR_CACHE_DATA_PROPERTY_VALUES_SIZE = {}", offsetof(ObjectPropertyIteratorCacheData, m_property_values) + vec_size);
    }

    // PutKind enum
    outln("\n# PutKind enum");
    outln("const PUT_KIND_NORMAL = {}", static_cast<u8>(JS::Bytecode::PutKind::Normal));

    // PrototypeChainValidity layout
    outln("\n# PrototypeChainValidity layout");
    EMIT_OFFSET(PROTOTYPE_CHAIN_VALIDITY_VALID, PrototypeChainValidity, m_valid);

    // DeclarativeEnvironment layout
    outln("\n# DeclarativeEnvironment layout");
    EMIT_OFFSET(DECLARATIVE_ENVIRONMENT_RARE_DATA, DeclarativeEnvironment, m_rare_data);
    EMIT_OFFSET(DECLARATIVE_ENVIRONMENT_RARE_DATA_SERIAL, DeclarativeEnvironment::RareData, m_environment_serial_number);

    // GlobalVariableCache layout
    outln("\n# GlobalVariableCache layout");
    EMIT_OFFSET(GLOBAL_VARIABLE_CACHE_ENTRY, GlobalVariableCache, entry);
    EMIT_OFFSET(GLOBAL_VARIABLE_CACHE_ENVIRONMENT_SERIAL, GlobalVariableCache, environment_serial_number);
    EMIT_OFFSET(GLOBAL_VARIABLE_CACHE_ENVIRONMENT_BINDING_INDEX, GlobalVariableCache, environment_binding_index);
    EMIT_OFFSET(GLOBAL_VARIABLE_CACHE_HAS_ENVIRONMENT_BINDING, GlobalVariableCache, has_environment_binding_index);
    EMIT_OFFSET(GLOBAL_VARIABLE_CACHE_IN_MODULE_ENVIRONMENT, GlobalVariableCache, in_module_environment);
    EMIT_SIZEOF(GLOBAL_VARIABLE_CACHE_SIZE, GlobalVariableCache);
    outln("const GLOBAL_VARIABLE_CACHE_ENTRY_PROPERTY_OFFSET = {}", offsetof(GlobalVariableCache, entry) + offsetof(PropertyLookupCache::Entry, property_offset));
    outln("const GLOBAL_VARIABLE_CACHE_ENTRY_DICTIONARY_GENERATION = {}", offsetof(GlobalVariableCache, entry) + offsetof(PropertyLookupCache::Entry, shape_dictionary_generation));
    outln("const GLOBAL_VARIABLE_CACHE_ENTRY_SHAPE = {}", offsetof(GlobalVariableCache, entry) + offsetof(PropertyLookupCache::Entry, shape));

    // Builtin enum values
    outln("\n# Builtin enum values");
    outln("const BUILTIN_MATH_ABS = {}", static_cast<u8>(Bytecode::Builtin::MathAbs));
    outln("const BUILTIN_MATH_FLOOR = {}", static_cast<u8>(Bytecode::Builtin::MathFloor));
    outln("const BUILTIN_MATH_CEIL = {}", static_cast<u8>(Bytecode::Builtin::MathCeil));
    outln("const BUILTIN_MATH_ROUND = {}", static_cast<u8>(Bytecode::Builtin::MathRound));
    outln("const BUILTIN_MATH_SQRT = {}", static_cast<u8>(Bytecode::Builtin::MathSqrt));
    outln("const BUILTIN_MATH_EXP = {}", static_cast<u8>(Bytecode::Builtin::MathExp));
    outln("const BUILTIN_STRING_FROM_CHAR_CODE = {}", static_cast<u8>(Bytecode::Builtin::StringFromCharCode));
    outln("const BUILTIN_STRING_PROTOTYPE_CHAR_CODE_AT = {}", static_cast<u8>(Bytecode::Builtin::StringPrototypeCharCodeAt));
    outln("const BUILTIN_STRING_PROTOTYPE_CHAR_AT = {}", static_cast<u8>(Bytecode::Builtin::StringPrototypeCharAt));

    // FunctionObject layout
    outln("\n# FunctionObject layout");
    EMIT_OFFSET(FUNCTION_OBJECT_BUILTIN, FunctionObject, m_builtin);
    {
        // Optional<Builtin> has a value byte and a has_value byte.
        auto base = offsetof(FunctionObject, m_builtin);
        // Optional<Builtin> stores value at offset 0, has_value bool at offset 1.
        // Verify this assumption at compile time:
        Optional<Bytecode::Builtin> opt_test { Bytecode::Builtin::MathFloor };
        auto const* raw = reinterpret_cast<u8 const*>(&opt_test);
        VERIFY(raw[0] == static_cast<u8>(Bytecode::Builtin::MathFloor));
        VERIFY(raw[1] == 1); // has_value
        outln("const FUNCTION_OBJECT_BUILTIN_VALUE = {}", base);
        outln("const FUNCTION_OBJECT_BUILTIN_HAS_VALUE = {}", base + 1);
    }

    // RawNativeFunction layout
    outln("\n# RawNativeFunction layout");
    EMIT_OFFSET(RAW_NATIVE_FUNCTION_NATIVE_FUNCTION, RawNativeFunction, m_native_function);

    // ECMAScriptFunctionObject layout
    outln("\n# ECMAScriptFunctionObject layout");
    EMIT_OFFSET(ECMASCRIPT_FUNCTION_OBJECT_SHARED_DATA, ECMAScriptFunctionObject, m_shared_data);
    EMIT_OFFSET(ECMASCRIPT_FUNCTION_OBJECT_ENVIRONMENT, ECMAScriptFunctionObject, m_environment);
    EMIT_OFFSET(ECMASCRIPT_FUNCTION_OBJECT_PRIVATE_ENVIRONMENT, ECMAScriptFunctionObject, m_private_environment);
    EMIT_OFFSET(ECMASCRIPT_FUNCTION_OBJECT_SCRIPT_OR_MODULE, ECMAScriptFunctionObject, m_script_or_module);

    // SharedFunctionInstanceData layout
    outln("\n# SharedFunctionInstanceData layout");
    EMIT_OFFSET(SHARED_FUNCTION_INSTANCE_DATA_EXECUTABLE, SharedFunctionInstanceData, m_executable);
    EMIT_OFFSET(SHARED_FUNCTION_INSTANCE_DATA_ASM_CALL_METADATA, SharedFunctionInstanceData, m_asm_call_metadata);
    EMIT_OFFSET(SHARED_FUNCTION_INSTANCE_DATA_FORMAL_PARAMETER_COUNT, SharedFunctionInstanceData, m_formal_parameter_count);
    EMIT_OFFSET(SHARED_FUNCTION_INSTANCE_DATA_STRICT, SharedFunctionInstanceData, m_strict);
    EMIT_OFFSET(SHARED_FUNCTION_INSTANCE_DATA_FUNCTION_ENVIRONMENT_NEEDED, SharedFunctionInstanceData, m_function_environment_needed);
    EMIT_OFFSET(SHARED_FUNCTION_INSTANCE_DATA_USES_THIS, SharedFunctionInstanceData, m_uses_this);
    EMIT_OFFSET(SHARED_FUNCTION_INSTANCE_DATA_CAN_INLINE_CALL, SharedFunctionInstanceData, m_can_inline_call);
    outln("const SHARED_FUNCTION_INSTANCE_DATA_ASM_CALL_METADATA_CAN_INLINE_CALL = {}", SharedFunctionInstanceData::asm_call_metadata_can_inline_call);
    outln("const SHARED_FUNCTION_INSTANCE_DATA_ASM_CALL_METADATA_NEEDS_ENVIRONMENT_OR_THIS_VALUE_RESOLUTION = {}", SharedFunctionInstanceData::asm_call_metadata_needs_environment_or_this_value_resolution);
    outln("const SHARED_FUNCTION_INSTANCE_DATA_ASM_CALL_METADATA_USES_THIS = {}", SharedFunctionInstanceData::asm_call_metadata_uses_this);
    outln("const SHARED_FUNCTION_INSTANCE_DATA_ASM_CALL_METADATA_STRICT = {}", SharedFunctionInstanceData::asm_call_metadata_strict);

    // GlobalEnvironment layout
    outln("\n# GlobalEnvironment layout");
    EMIT_OFFSET(GLOBAL_ENVIRONMENT_GLOBAL_THIS_VALUE, GlobalEnvironment, m_global_this_value);

    // PrimitiveString layout
    outln("\n# PrimitiveString layout");
    EMIT_OFFSET(PRIMITIVE_STRING_DEFERRED_KIND, PrimitiveStringLayoutAccessor, m_deferred_kind);
    EMIT_OFFSET(PRIMITIVE_STRING_UTF16_STRING, PrimitiveStringLayoutAccessor, m_utf16_string);
    outln("const PRIMITIVE_STRING_DEFERRED_KIND_NONE = {}", static_cast<u8>(PrimitiveStringLayoutAccessor::DeferredKind::None));

    // Utf16String / ShortString layout
    outln("\n# Utf16String layout");
    outln("const UTF16_SHORT_STRING_FLAG = {}", AK::Detail::StringBase::SHORT_STRING_FLAG);
    outln("const UTF16_SHORT_STRING_BYTE_COUNT_SHIFT_COUNT = {}", AK::Detail::StringBase::SHORT_STRING_BYTE_COUNT_SHIFT_COUNT);
    EMIT_OFFSET(UTF16_SHORT_STRING_BYTE_COUNT_AND_FLAG, AK::Detail::ShortString, byte_count_and_short_string_flag);
    EMIT_OFFSET(UTF16_SHORT_STRING_STORAGE, AK::Detail::ShortString, storage);
    outln("const PRIMITIVE_STRING_UTF16_SHORT_STRING_BYTE_COUNT_AND_FLAG = {}", offsetof(PrimitiveStringLayoutAccessor, m_utf16_string) + offsetof(AK::Detail::ShortString, byte_count_and_short_string_flag));
    outln("const PRIMITIVE_STRING_UTF16_SHORT_STRING_STORAGE = {}", offsetof(PrimitiveStringLayoutAccessor, m_utf16_string) + offsetof(AK::Detail::ShortString, storage));

    // Utf16StringData layout
    outln("\n# Utf16StringData layout");
    EMIT_OFFSET(UTF16_STRING_DATA_LENGTH_IN_CODE_UNITS, AK::Detail::Utf16StringData, m_length_in_code_units);
    outln("const UTF16_STRING_DATA_STRING_STORAGE = {}", AK::Detail::Utf16StringData::offset_of_string_storage());

    // Environment layout
    outln("\n# Environment layout");
    EMIT_OFFSET(ENVIRONMENT_SCREWED_BY_EVAL, Environment, m_permanently_screwed_by_eval);
    EMIT_OFFSET(ENVIRONMENT_DECLARATIVE, Environment, m_declarative);
    EMIT_OFFSET(ENVIRONMENT_OUTER, Environment, m_outer_environment);

    // DeclarativeEnvironment binding storage layout
    outln("\n# DeclarativeEnvironment binding storage layout");
    EMIT_OFFSET(DECLARATIVE_ENVIRONMENT_SHAPE, DeclarativeEnvironment, m_shape);
    EMIT_OFFSET(DECLARATIVE_ENVIRONMENT_BINDING_VALUES, DeclarativeEnvironment, m_binding_values);
    EMIT_OFFSET(DECLARATIVE_ENVIRONMENT_RARE_DATA_BINDING_FLAGS, DeclarativeEnvironment::RareData, m_binding_flags);
    EMIT_OFFSET(ENVIRONMENT_SHAPE_BINDING_FLAGS, EnvironmentShape, m_binding_flags);
    outln("const BINDING_FLAG_MUTABLE = {}", 1 << 1);

    // Vector<T> layout: m_size(0), m_capacity(8), m_metadata.outline_buffer(16)
    outln("const BINDING_VALUES_DATA_PTR = {}", offsetof(DeclarativeEnvironment, m_binding_values) + sizeof(size_t) * 2);
    outln("const BINDING_FLAGS_DATA_PTR = {}", offsetof(DeclarativeEnvironment::RareData, m_binding_flags) + sizeof(size_t) * 2);
    outln("const ENVIRONMENT_SHAPE_BINDING_FLAGS_DATA_PTR = {}", offsetof(EnvironmentShape, m_binding_flags) + sizeof(size_t) * 2);

    // EnvironmentCoordinate layout
    outln("\n# EnvironmentCoordinate layout");
    outln("const ENVIRONMENT_COORDINATE_HOPS = {}", offsetof(EnvironmentCoordinate, hops));
    outln("const ENVIRONMENT_COORDINATE_INDEX = {}", offsetof(EnvironmentCoordinate, index));
    outln("const ENVIRONMENT_COORDINATE_INVALID = 0x{:X}", EnvironmentCoordinate::invalid_marker);
    EMIT_SIZEOF(ENVIRONMENT_COORDINATE_SIZE, EnvironmentCoordinate);

    // TypedArrayBase layout
    outln("\n# TypedArrayBase layout");
    EMIT_OFFSET(TYPED_ARRAY_ELEMENT_SIZE, TypedArrayBase, m_element_size);
    EMIT_OFFSET(TYPED_ARRAY_ARRAY_LENGTH, TypedArrayBase, m_array_length);
    EMIT_OFFSET(TYPED_ARRAY_BYTE_OFFSET, TypedArrayBase, m_byte_offset);
    EMIT_OFFSET(TYPED_ARRAY_KIND, TypedArrayBase, m_kind);
    EMIT_OFFSET(TYPED_ARRAY_CACHED_DATA_PTR, TypedArrayBase, m_data);

    // ByteLength (Variant<Auto, Detached, u32>) layout
    outln("\n# ByteLength layout");
    {
        // For Variant<Auto, Detached, u32>: u32 index 2 means it holds a u32
        outln("const BYTE_LENGTH_U32_INDEX = 2");
        EMIT_SIZEOF(BYTE_LENGTH_SIZE, ByteLength);
        // Composite: array_length value (u32) and index byte within TypedArrayBase
        auto ta_al = offsetof(TypedArrayBase, m_array_length);
        // Variant<Auto, Detached, u32> stores m_data[4] then m_index (u8)
        outln("const TYPED_ARRAY_ARRAY_LENGTH_VALUE = {}", ta_al);     // u32 data at start
        outln("const TYPED_ARRAY_ARRAY_LENGTH_INDEX = {}", ta_al + 4); // index byte after 4 bytes of data
    }

    // TypedArrayBase::Kind enum values
    outln("\n# TypedArrayBase::Kind values");
    outln("const TYPED_ARRAY_KIND_UINT8 = {}", static_cast<u8>(TypedArrayBase::Kind::Uint8Array));
    outln("const TYPED_ARRAY_KIND_UINT8_CLAMPED = {}", static_cast<u8>(TypedArrayBase::Kind::Uint8ClampedArray));
    outln("const TYPED_ARRAY_KIND_UINT16 = {}", static_cast<u8>(TypedArrayBase::Kind::Uint16Array));
    outln("const TYPED_ARRAY_KIND_UINT32 = {}", static_cast<u8>(TypedArrayBase::Kind::Uint32Array));
    outln("const TYPED_ARRAY_KIND_INT8 = {}", static_cast<u8>(TypedArrayBase::Kind::Int8Array));
    outln("const TYPED_ARRAY_KIND_INT16 = {}", static_cast<u8>(TypedArrayBase::Kind::Int16Array));
    outln("const TYPED_ARRAY_KIND_INT32 = {}", static_cast<u8>(TypedArrayBase::Kind::Int32Array));
    outln("const TYPED_ARRAY_KIND_FLOAT32 = {}", static_cast<u8>(TypedArrayBase::Kind::Float32Array));
    outln("const TYPED_ARRAY_KIND_FLOAT64 = {}", static_cast<u8>(TypedArrayBase::Kind::Float64Array));

    // Value tags
    outln("\n# Value tags");
    outln("const OBJECT_TAG = 0x{:X}", static_cast<u64>(OBJECT_TAG));
    outln("const STRING_TAG = 0x{:X}", static_cast<u64>(STRING_TAG));
    outln("const SYMBOL_TAG = 0x{:X}", static_cast<u64>(SYMBOL_TAG));
    outln("const BIGINT_TAG = 0x{:X}", static_cast<u64>(BIGINT_TAG));
    outln("const ACCESSOR_TAG = 0x{:X}", static_cast<u64>(ACCESSOR_TAG));
    outln("const INT32_TAG = 0x{:X}", static_cast<u64>(INT32_TAG));
    outln("const BOOLEAN_TAG = 0x{:X}", static_cast<u64>(BOOLEAN_TAG));
    outln("const UNDEFINED_TAG = 0x{:X}", static_cast<u64>(UNDEFINED_TAG));
    outln("const NULL_TAG = 0x{:X}", static_cast<u64>(NULL_TAG));

    // Shifted value constants
    outln("\n# Shifted value constants");
    outln("const OBJECT_TAG_SHIFTED = 0x{:X}", static_cast<u64>(OBJECT_TAG << GC::TAG_SHIFT));
    outln("const EMPTY_VALUE = 0x{:X}", static_cast<u64>(EMPTY_TAG << GC::TAG_SHIFT));
    outln("const INT32_TAG_SHIFTED = 0x{:X}", static_cast<u64>(INT32_TAG << GC::TAG_SHIFT));
    outln("const BOOLEAN_TRUE = 0x{:X}", static_cast<u64>((BOOLEAN_TAG << GC::TAG_SHIFT) | 1));
    outln("const BOOLEAN_FALSE = 0x{:X}", static_cast<u64>(BOOLEAN_TAG << GC::TAG_SHIFT));
    outln("const UNDEFINED_SHIFTED = 0x{:X}", static_cast<u64>(UNDEFINED_TAG << GC::TAG_SHIFT));
    outln("const EMPTY_TAG_SHIFTED = 0x{:X}", static_cast<u64>(EMPTY_TAG << GC::TAG_SHIFT));
    outln("const NAN_BASE_TAG = 0x{:X}", static_cast<u64>(GC::BASE_TAG));
    outln("const CANON_NAN_BITS = 0x{:X}", static_cast<u64>(GC::CANON_NAN_BITS));
    outln("const DOUBLE_ONE = 0x{:X}", bit_cast<u64>(1.0));
    outln("const NEGATIVE_ZERO = 0x{:X}", static_cast<u64>(NEGATIVE_ZERO_BITS));
    outln("const CELL_TAG_SHIFTED = 0x{:X}", static_cast<u64>(GC::SHIFTED_IS_CELL_PATTERN));

    outln("const ACCUMULATOR_REG_OFFSET = {}", static_cast<size_t>(Register::accumulator().index()) * sizeof(Value));
    outln("const EXCEPTION_REG_OFFSET = {}", static_cast<size_t>(Register::exception().index()) * sizeof(Value));
    outln("const THIS_VALUE_REG_OFFSET = {}", static_cast<size_t>(Register::this_value().index()) * sizeof(Value));
    outln("const RETURN_VALUE_REG_OFFSET = {}", static_cast<size_t>(Register::return_value().index()) * sizeof(Value));
    outln("const SAVED_LEXICAL_ENVIRONMENT_REG_OFFSET = {}", static_cast<size_t>(Register::saved_lexical_environment().index()) * sizeof(Value));
    outln("const RESERVED_REGISTERS_SIZE = {}", static_cast<size_t>(Register::reserved_register_count) * sizeof(Value));

    return 0;
}
