/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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; 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); // Shape layout outln("\n# Shape layout"); 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_ENTRIES, PropertyLookupCache, entries); 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); // Composite offsets for entry[0] within a PropertyLookupCache outln("\n# Entry[0] offsets within PropertyLookupCache"); auto plc_entries = offsetof(PropertyLookupCache, entries); outln("const PROPERTY_LOOKUP_CACHE_ENTRY0_PROPERTY_OFFSET = {}", plc_entries + offsetof(PropertyLookupCache::Entry, property_offset)); outln("const PROPERTY_LOOKUP_CACHE_ENTRY0_DICTIONARY_GENERATION = {}", plc_entries + offsetof(PropertyLookupCache::Entry, shape_dictionary_generation)); outln("const PROPERTY_LOOKUP_CACHE_ENTRY0_SHAPE = {}", plc_entries + offsetof(PropertyLookupCache::Entry, shape)); outln("const PROPERTY_LOOKUP_CACHE_ENTRY0_PROTOTYPE = {}", plc_entries + offsetof(PropertyLookupCache::Entry, prototype)); outln("const PROPERTY_LOOKUP_CACHE_ENTRY0_PROTOTYPE_CHAIN_VALIDITY = {}", plc_entries + offsetof(PropertyLookupCache::Entry, prototype_chain_validity)); // Executable layout outln("\n# Executable layout"); EMIT_OFFSET(EXECUTABLE_PROPERTY_LOOKUP_CACHES, Executable, property_lookup_caches); // ExecutionContext layout outln("\n# ExecutionContext layout"); EMIT_OFFSET(EXECUTION_CONTEXT_EXECUTABLE, ExecutionContext, executable); EMIT_OFFSET(EXECUTION_CONTEXT_REALM, ExecutionContext, realm); EMIT_OFFSET(EXECUTION_CONTEXT_LEXICAL_ENVIRONMENT, ExecutionContext, lexical_environment); EMIT_OFFSET(EXECUTION_CONTEXT_CALLER_FRAME, ExecutionContext, caller_frame); EMIT_OFFSET(EXECUTION_CONTEXT_PROGRAM_COUNTER, ExecutionContext, program_counter); EMIT_SIZEOF(SIZEOF_EXECUTION_CONTEXT, ExecutionContext); // Realm layout outln("\n# Realm layout"); EMIT_OFFSET(REALM_GLOBAL_OBJECT, Realm, m_global_object); EMIT_OFFSET(REALM_GLOBAL_DECLARATIVE_ENVIRONMENT, Realm, m_global_declarative_environment); // Interpreter layout outln("\n# Interpreter layout"); EMIT_OFFSET(INTERPRETER_RUNNING_EXECUTION_CONTEXT, Interpreter, m_running_execution_context); // IndexedStorageKind enum values outln("\n# IndexedStorageKind enum values"); outln("const INDEXED_STORAGE_KIND_NONE = {}", static_cast(IndexedStorageKind::None)); outln("const INDEXED_STORAGE_KIND_PACKED = {}", static_cast(IndexedStorageKind::Packed)); outln("const INDEXED_STORAGE_KIND_HOLEY = {}", static_cast(IndexedStorageKind::Holey)); outln("const INDEXED_STORAGE_KIND_DICTIONARY = {}", static_cast(IndexedStorageKind::Dictionary)); // Vector layout (used for bytecode) outln("\n# Vector layout"); { Vector v; auto base = reinterpret_cast(&v); auto vec_data = reinterpret_cast(&v.m_metadata.outline_buffer) - base; auto vec_size = reinterpret_cast(&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) + vec_data); } // PutKind enum outln("\n# PutKind enum"); outln("const PUT_KIND_NORMAL = {}", static_cast(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_SERIAL, DeclarativeEnvironment, m_environment_serial_number); // GlobalVariableCache layout outln("\n# GlobalVariableCache layout"); 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); // Builtin enum values outln("\n# Builtin enum values"); outln("const BUILTIN_MATH_ABS = {}", static_cast(Bytecode::Builtin::MathAbs)); outln("const BUILTIN_MATH_FLOOR = {}", static_cast(Bytecode::Builtin::MathFloor)); outln("const BUILTIN_MATH_CEIL = {}", static_cast(Bytecode::Builtin::MathCeil)); outln("const BUILTIN_MATH_ROUND = {}", static_cast(Bytecode::Builtin::MathRound)); outln("const BUILTIN_MATH_SQRT = {}", static_cast(Bytecode::Builtin::MathSqrt)); outln("const BUILTIN_MATH_EXP = {}", static_cast(Bytecode::Builtin::MathExp)); // FunctionObject layout outln("\n# FunctionObject layout"); EMIT_OFFSET(FUNCTION_OBJECT_BUILTIN, FunctionObject, m_builtin); { // Optional has a value byte and a has_value byte. auto base = offsetof(FunctionObject, m_builtin); // Optional stores value at offset 0, has_value bool at offset 1. // Verify this assumption at compile time: Optional opt_test { Bytecode::Builtin::MathFloor }; auto const* raw = reinterpret_cast(&opt_test); VERIFY(raw[0] == static_cast(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); } // Environment layout outln("\n# Environment layout"); EMIT_OFFSET(ENVIRONMENT_SCREWED_BY_EVAL, Environment, m_permanently_screwed_by_eval); EMIT_OFFSET(ENVIRONMENT_OUTER, Environment, m_outer_environment); // DeclarativeEnvironment / Binding layout outln("\n# DeclarativeEnvironment / Binding layout"); EMIT_OFFSET(DECLARATIVE_ENVIRONMENT_BINDINGS, DeclarativeEnvironment, m_bindings); outln("const BINDING_VALUE = {}", offsetof(DeclarativeEnvironment::Binding, value)); outln("const BINDING_STRICT = {}", offsetof(DeclarativeEnvironment::Binding, strict)); outln("const BINDING_MUTABLE = {}", offsetof(DeclarativeEnvironment::Binding, mutable_)); outln("const BINDING_INITIALIZED = {}", offsetof(DeclarativeEnvironment::Binding, initialized)); outln("const SIZEOF_BINDING = {}", sizeof(DeclarativeEnvironment::Binding)); // Vector layout: m_size(0), m_capacity(8), m_metadata.outline_buffer(16) outln("const BINDINGS_DATA_PTR = {}", offsetof(DeclarativeEnvironment, m_bindings) + 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); // 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) layout outln("\n# ByteLength layout"); { // For Variant: 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 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(TypedArrayBase::Kind::Uint8Array)); outln("const TYPED_ARRAY_KIND_UINT8_CLAMPED = {}", static_cast(TypedArrayBase::Kind::Uint8ClampedArray)); outln("const TYPED_ARRAY_KIND_UINT16 = {}", static_cast(TypedArrayBase::Kind::Uint16Array)); outln("const TYPED_ARRAY_KIND_UINT32 = {}", static_cast(TypedArrayBase::Kind::Uint32Array)); outln("const TYPED_ARRAY_KIND_INT8 = {}", static_cast(TypedArrayBase::Kind::Int8Array)); outln("const TYPED_ARRAY_KIND_INT16 = {}", static_cast(TypedArrayBase::Kind::Int16Array)); outln("const TYPED_ARRAY_KIND_INT32 = {}", static_cast(TypedArrayBase::Kind::Int32Array)); outln("const TYPED_ARRAY_KIND_FLOAT32 = {}", static_cast(TypedArrayBase::Kind::Float32Array)); outln("const TYPED_ARRAY_KIND_FLOAT64 = {}", static_cast(TypedArrayBase::Kind::Float64Array)); // Value tags outln("\n# Value tags"); outln("const OBJECT_TAG = 0x{:X}", static_cast(OBJECT_TAG)); outln("const STRING_TAG = 0x{:X}", static_cast(STRING_TAG)); outln("const SYMBOL_TAG = 0x{:X}", static_cast(SYMBOL_TAG)); outln("const BIGINT_TAG = 0x{:X}", static_cast(BIGINT_TAG)); outln("const ACCESSOR_TAG = 0x{:X}", static_cast(ACCESSOR_TAG)); outln("const INT32_TAG = 0x{:X}", static_cast(INT32_TAG)); outln("const BOOLEAN_TAG = 0x{:X}", static_cast(BOOLEAN_TAG)); outln("const UNDEFINED_TAG = 0x{:X}", static_cast(UNDEFINED_TAG)); outln("const NULL_TAG = 0x{:X}", static_cast(NULL_TAG)); // Shifted value constants outln("\n# Shifted value constants"); outln("const EMPTY_VALUE = 0x{:X}", static_cast(EMPTY_TAG << GC::TAG_SHIFT)); outln("const INT32_TAG_SHIFTED = 0x{:X}", static_cast(INT32_TAG << GC::TAG_SHIFT)); outln("const BOOLEAN_TRUE = 0x{:X}", static_cast((BOOLEAN_TAG << GC::TAG_SHIFT) | 1)); outln("const BOOLEAN_FALSE = 0x{:X}", static_cast(BOOLEAN_TAG << GC::TAG_SHIFT)); outln("const UNDEFINED_SHIFTED = 0x{:X}", static_cast(UNDEFINED_TAG << GC::TAG_SHIFT)); outln("const EMPTY_TAG_SHIFTED = 0x{:X}", static_cast(EMPTY_TAG << GC::TAG_SHIFT)); outln("const NAN_BASE_TAG = 0x{:X}", static_cast(GC::BASE_TAG)); outln("const CANON_NAN_BITS = 0x{:X}", static_cast(GC::CANON_NAN_BITS)); outln("const DOUBLE_ONE = 0x{:X}", bit_cast(1.0)); outln("const NEGATIVE_ZERO = 0x{:X}", static_cast(NEGATIVE_ZERO_BITS)); outln("const CELL_TAG_SHIFTED = 0x{:X}", static_cast(GC::SHIFTED_IS_CELL_PATTERN)); outln("const THIS_VALUE_REG_OFFSET = {}", static_cast(Register::this_value().index()) * sizeof(Value)); return 0; }