include(libjs_generators)

set(SOURCES
    Bytecode/AsmInterpreter/AsmInterpreter.cpp
    Bytecode/BasicBlock.cpp
    Bytecode/Executable.cpp
    Bytecode/IdentifierTable.cpp
    Bytecode/Instruction.cpp
    Bytecode/Interpreter.cpp
    Bytecode/Label.cpp
    Bytecode/PropertyNameIterator.cpp
    Bytecode/PropertyKeyTable.cpp
    Bytecode/RegexTable.cpp
    Bytecode/StringTable.cpp
    Bytecode/Validator.cpp
    Console.cpp
    Contrib/Test262/262Object.cpp
    Contrib/Test262/AgentObject.cpp
    Contrib/Test262/GlobalObject.cpp
    Contrib/Test262/IsHTMLDDA.cpp
    CyclicModule.cpp
    Heap/Cell.cpp
    Module.cpp
    ParserError.cpp
    Print.cpp
    RustIntegration.cpp
    Runtime/AbstractOperations.cpp
    Runtime/Accessor.cpp
    Runtime/Agent.cpp
    Runtime/AggregateError.cpp
    Runtime/AggregateErrorConstructor.cpp
    Runtime/AggregateErrorPrototype.cpp
    Runtime/ArgumentsObject.cpp
    Runtime/Array.cpp
    Runtime/ArrayBuffer.cpp
    Runtime/ArrayBufferConstructor.cpp
    Runtime/ArrayBufferPrototype.cpp
    Runtime/ArrayConstructor.cpp
    Runtime/ArrayIterator.cpp
    Runtime/ArrayIteratorPrototype.cpp
    Runtime/ArrayPrototype.cpp
    Runtime/AsyncDisposableStack.cpp
    Runtime/AsyncDisposableStackConstructor.cpp
    Runtime/AsyncDisposableStackPrototype.cpp
    Runtime/AsyncFromSyncIterator.cpp
    Runtime/AsyncFromSyncIteratorPrototype.cpp
    Runtime/AsyncFunctionConstructor.cpp
    Runtime/AsyncFunctionDriverWrapper.cpp
    Runtime/AsyncFunctionPrototype.cpp
    Runtime/AsyncGenerator.cpp
    Runtime/AsyncGeneratorFunctionConstructor.cpp
    Runtime/AsyncGeneratorFunctionPrototype.cpp
    Runtime/AsyncGeneratorPrototype.cpp
    Runtime/AsyncIteratorPrototype.cpp
    Runtime/AtomicsObject.cpp
    Runtime/BigInt.cpp
    Runtime/BigIntConstructor.cpp
    Runtime/BigIntObject.cpp
    Runtime/BigIntPrototype.cpp
    Runtime/BooleanConstructor.cpp
    Runtime/BooleanObject.cpp
    Runtime/BooleanPrototype.cpp
    Runtime/BoundFunction.cpp
    Runtime/ClassConstruction.cpp
    Runtime/ClassFieldDefinition.cpp
    Runtime/Completion.cpp
    Runtime/ConsoleObjectPrototype.cpp
    Runtime/ConsoleObject.cpp
    Runtime/DataView.cpp
    Runtime/DataViewConstructor.cpp
    Runtime/DataViewPrototype.cpp
    Runtime/DescriptorArray.cpp
    Runtime/Date.cpp
    Runtime/DateConstructor.cpp
    Runtime/DatePrototype.cpp
    Runtime/DeclarativeEnvironment.cpp
    Runtime/DisposableStack.cpp
    Runtime/DisposableStackConstructor.cpp
    Runtime/DisposableStackPrototype.cpp
    Runtime/ECMAScriptFunctionObject.cpp
    Runtime/Environment.cpp
    Runtime/EnvironmentShape.cpp
    Runtime/Error.cpp
    Runtime/ErrorConstructor.cpp
    Runtime/ErrorData.cpp
    Runtime/ErrorPrototype.cpp
    Runtime/ErrorTypes.cpp
    Runtime/ExecutionContext.cpp
    Runtime/InterpreterStack.cpp
    Runtime/FinalizationRegistry.cpp
    Runtime/FinalizationRegistryConstructor.cpp
    Runtime/FinalizationRegistryPrototype.cpp
    Runtime/FunctionConstructor.cpp
    Runtime/FunctionEnvironment.cpp
    Runtime/FunctionObject.cpp
    Runtime/FunctionPrototype.cpp
    Runtime/GeneratorFunctionConstructor.cpp
    Runtime/GeneratorFunctionPrototype.cpp
    Runtime/GeneratorObject.cpp
    Runtime/GeneratorPrototype.cpp
    Runtime/GlobalEnvironment.cpp
    Runtime/GlobalObject.cpp
    Runtime/IndexedProperties.cpp
    Runtime/Intl/AbstractOperations.cpp
    Runtime/Intl/Collator.cpp
    Runtime/Intl/CollatorCompareFunction.cpp
    Runtime/Intl/CollatorConstructor.cpp
    Runtime/Intl/CollatorPrototype.cpp
    Runtime/Intl/DateTimeFormat.cpp
    Runtime/Intl/DateTimeFormatConstructor.cpp
    Runtime/Intl/DateTimeFormatFunction.cpp
    Runtime/Intl/DateTimeFormatPrototype.cpp
    Runtime/Intl/DisplayNames.cpp
    Runtime/Intl/DisplayNamesConstructor.cpp
    Runtime/Intl/DisplayNamesPrototype.cpp
    Runtime/Intl/DurationFormat.cpp
    Runtime/Intl/DurationFormatConstructor.cpp
    Runtime/Intl/DurationFormatPrototype.cpp
    Runtime/Intl/Intl.cpp
    Runtime/Intl/ListFormat.cpp
    Runtime/Intl/ListFormatConstructor.cpp
    Runtime/Intl/ListFormatPrototype.cpp
    Runtime/Intl/Locale.cpp
    Runtime/Intl/LocaleConstructor.cpp
    Runtime/Intl/LocalePrototype.cpp
    Runtime/Intl/MathematicalValue.cpp
    Runtime/Intl/NumberFormat.cpp
    Runtime/Intl/NumberFormatConstructor.cpp
    Runtime/Intl/NumberFormatFunction.cpp
    Runtime/Intl/NumberFormatPrototype.cpp
    Runtime/Intl/PluralRules.cpp
    Runtime/Intl/PluralRulesConstructor.cpp
    Runtime/Intl/PluralRulesPrototype.cpp
    Runtime/Intl/RelativeTimeFormat.cpp
    Runtime/Intl/RelativeTimeFormatConstructor.cpp
    Runtime/Intl/RelativeTimeFormatPrototype.cpp
    Runtime/Intl/Segmenter.cpp
    Runtime/Intl/SegmenterConstructor.cpp
    Runtime/Intl/SegmenterPrototype.cpp
    Runtime/Intl/Segments.cpp
    Runtime/Intl/SegmentIterator.cpp
    Runtime/Intl/SegmentIteratorPrototype.cpp
    Runtime/Intl/SegmentsPrototype.cpp
    Runtime/Intrinsics.cpp
    Runtime/Iterator.cpp
    Runtime/IteratorConstructor.cpp
    Runtime/IteratorHelper.cpp
    Runtime/IteratorHelperPrototype.cpp
    Runtime/IteratorPrototype.cpp
    Runtime/JSONObject.cpp
    Runtime/JobCallback.cpp
    Runtime/KeyedCollections.cpp
    Runtime/Map.cpp
    Runtime/MapConstructor.cpp
    Runtime/MapIterator.cpp
    Runtime/MapIteratorPrototype.cpp
    Runtime/MapPrototype.cpp
    Runtime/MathObject.cpp
    Runtime/ModuleEnvironment.cpp
    Runtime/ModuleNamespaceObject.cpp
    Runtime/NativeFunction.cpp
    Runtime/NativeJavaScriptBackedFunction.cpp
    Runtime/NumberConstructor.cpp
    Runtime/NumberObject.cpp
    Runtime/NumberPrototype.cpp
    Runtime/Object.cpp
    Runtime/ObjectConstructor.cpp
    Runtime/ObjectEnvironment.cpp
    Runtime/ObjectPrototype.cpp
    Runtime/PrimitiveString.cpp
    Runtime/PrivateEnvironment.cpp
    Runtime/Promise.cpp
    Runtime/PromiseCapability.cpp
    Runtime/PromiseConstructor.cpp
    Runtime/PromiseJobs.cpp
    Runtime/PromisePrototype.cpp
    Runtime/PromiseReaction.cpp
    Runtime/PromiseResolvingElementFunctions.cpp
    Runtime/PromiseResolvingFunction.cpp
    Runtime/PropertyDescriptor.cpp
    Runtime/ProxyConstructor.cpp
    Runtime/ProxyObject.cpp
    Runtime/RawJSONObject.cpp
    Runtime/Realm.cpp
    Runtime/Reference.cpp
    Runtime/ReflectObject.cpp
    Runtime/RegExpConstructor.cpp
    Runtime/RegExpLegacyStaticProperties.cpp
    Runtime/RegExpObject.cpp
    Runtime/RegExpPrototype.cpp
    Runtime/RegExpStringIterator.cpp
    Runtime/RegExpStringIteratorPrototype.cpp
    Runtime/Set.cpp
    Runtime/SetConstructor.cpp
    Runtime/SetIterator.cpp
    Runtime/SetIteratorPrototype.cpp
    Runtime/SetPrototype.cpp
    Runtime/Shape.cpp
    Runtime/SharedArrayBufferConstructor.cpp
    Runtime/SharedArrayBufferPrototype.cpp
    Runtime/SharedFunctionInstanceData.cpp
    Runtime/StringConstructor.cpp
    Runtime/StringIterator.cpp
    Runtime/StringIteratorPrototype.cpp
    Runtime/StringObject.cpp
    Runtime/StringPrototype.cpp
    Runtime/SuppressedError.cpp
    Runtime/SuppressedErrorConstructor.cpp
    Runtime/SuppressedErrorPrototype.cpp
    Runtime/Symbol.cpp
    Runtime/SymbolConstructor.cpp
    Runtime/SymbolObject.cpp
    Runtime/SymbolPrototype.cpp
    Runtime/Temporal/AbstractOperations.cpp
    Runtime/Temporal/Calendar.cpp
    Runtime/Temporal/DateEquations.cpp
    Runtime/Temporal/Duration.cpp
    Runtime/Temporal/DurationConstructor.cpp
    Runtime/Temporal/DurationPrototype.cpp
    Runtime/Temporal/Instant.cpp
    Runtime/Temporal/InstantConstructor.cpp
    Runtime/Temporal/InstantPrototype.cpp
    Runtime/Temporal/ISO8601.cpp
    Runtime/Temporal/Now.cpp
    Runtime/Temporal/PlainDate.cpp
    Runtime/Temporal/PlainDateConstructor.cpp
    Runtime/Temporal/PlainDatePrototype.cpp
    Runtime/Temporal/PlainDateTime.cpp
    Runtime/Temporal/PlainDateTimeConstructor.cpp
    Runtime/Temporal/PlainDateTimePrototype.cpp
    Runtime/Temporal/PlainMonthDay.cpp
    Runtime/Temporal/PlainMonthDayConstructor.cpp
    Runtime/Temporal/PlainMonthDayPrototype.cpp
    Runtime/Temporal/PlainTime.cpp
    Runtime/Temporal/PlainTimeConstructor.cpp
    Runtime/Temporal/PlainTimePrototype.cpp
    Runtime/Temporal/PlainYearMonth.cpp
    Runtime/Temporal/PlainYearMonthConstructor.cpp
    Runtime/Temporal/PlainYearMonthPrototype.cpp
    Runtime/Temporal/Temporal.cpp
    Runtime/Temporal/TimeZone.cpp
    Runtime/Temporal/ZonedDateTime.cpp
    Runtime/Temporal/ZonedDateTimeConstructor.cpp
    Runtime/Temporal/ZonedDateTimePrototype.cpp
    Runtime/TypedArray.cpp
    Runtime/TypedArrayConstructor.cpp
    Runtime/TypedArrayPrototype.cpp
    Runtime/Uint8Array.cpp
    Runtime/Value.cpp
    Runtime/VM.cpp
    Runtime/WeakMap.cpp
    Runtime/WeakMapConstructor.cpp
    Runtime/WeakMapPrototype.cpp
    Runtime/WeakRef.cpp
    Runtime/WeakRefConstructor.cpp
    Runtime/WeakRefPrototype.cpp
    Runtime/WeakSet.cpp
    Runtime/WeakSetConstructor.cpp
    Runtime/WeakSetPrototype.cpp
    Runtime/WrapForValidIteratorPrototype.cpp
    Script.cpp
    SourceCode.cpp
    SourceTextModule.cpp
    SyntaxHighlighter.cpp
    SyntheticModule.cpp
)

generate_bytecode_def_derived()

set(GENERATED_SOURCES Bytecode/Op.cpp)

ladybird_lib(LibJS js EXPLICIT_SYMBOL_EXPORT)

target_link_libraries(LibJS PRIVATE LibCore LibCrypto LibFileSystem LibRegex LibSyntax LibTextCodec LibGC simdjson::simdjson)

# Link LibUnicode publicly to ensure ICU data (which is in libicudata.a) is available in any process using LibJS.
target_link_libraries(LibJS PUBLIC LibUnicode)

# TODO: This is probably also needed on RISC-V.
if("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "i.86.*")
    target_link_libraries(LibJS PRIVATE atomic)
endif()

if (WIN32)
    # FIXME: Windows on ARM
    target_link_libraries(LibJS PRIVATE clang_rt.builtins-x86_64.lib kernel32 ntdll Ws2_32 userenv)
else()
    # This flag has no effect on Windows
    target_compile_options(LibJS PRIVATE -fno-omit-frame-pointer)
endif()

target_link_libraries(LibJS PUBLIC JSClangPlugin)

import_rust_crate(MANIFEST_PATH Rust/Cargo.toml CRATE_NAME libjs_rust FFI_HEADER RustFFI.h)
target_link_libraries(LibJS PRIVATE libjs_rust)
if ((LINUX OR BSD) AND NOT BUILD_SHARED_LIBS)
    # Rust staticlibs each carry the same allocator shim symbols. LibJS can
    # pull in multiple crates' copies in static ELF links, but they all
    # forward to the same ladybird_rust_* entry points in AK/kmalloc.cpp.
    target_link_options(LibJS INTERFACE LINKER:--allow-multiple-definition)
endif()

# The Rust library and LibJS have a circular dependency (C++ calls Rust
# entry points, Rust calls C++ callbacks). For static builds, merge the
# Rust archive into the LibJS archive so all symbols are in one place.
if(NOT BUILD_SHARED_LIBS)
    add_custom_command(TARGET LibJS POST_BUILD
        COMMAND ${CMAKE_AR} -x $<TARGET_FILE:libjs_rust>
        COMMAND ${CMAKE_AR} -qS $<TARGET_FILE:LibJS> *.o
        COMMAND ${CMAKE_RANLIB} $<TARGET_FILE:LibJS>
        COMMAND ${CMAKE_COMMAND} -E remove -f *.o
        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/rust_merge_tmp
        COMMENT "Merging Rust archive into LibJS"
    )
    file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/rust_merge_tmp)

    # POST_BUILD steps don't track inputs, so when libjs_rust.a changes ninja
    # leaves liblagom-js.a alone and the merged archive ends up with stale
    # Rust objects. Force the C++ FFI bridge file to depend on the Rust
    # archive: when it changes, RustIntegration.cpp recompiles, LibJS gets
    # re-archived, and POST_BUILD re-merges the fresh Rust objects.
    get_target_property(_libjs_rust_lib libjs_rust IMPORTED_LOCATION)
    set_property(SOURCE RustIntegration.cpp APPEND PROPERTY OBJECT_DEPENDS ${_libjs_rust_lib})
endif()

# AsmInterpreter: generate platform-specific assembly from DSL
# NB: Win64 uses a different ABI (shadow space, different arg registers)
#     from SysV AMD64, and the x86_64 backend only supports SysV for now.
if (NOT WIN32 AND ("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "^(x86_64|amd64|AMD64)$"))
    set(ASMINT_ARCH "x86_64")
elseif ("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "^(aarch64|arm64)$")
    set(ASMINT_ARCH "aarch64")
endif()

if (DEFINED ASMINT_ARCH)
    enable_language(ASM)
    set(ASMINTGEN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/AsmIntGen")
    set(ASMINTGEN_BIN "${ASMINTGEN_DIR}/target/release/asmintgen")
    set(ASMINT_DSL "${CMAKE_CURRENT_SOURCE_DIR}/Bytecode/AsmInterpreter/asmint.asm")
    set(ASMINT_GENERATED_S "${CMAKE_CURRENT_BINARY_DIR}/Bytecode/AsmInterpreter/asmint_${ASMINT_ARCH}.S")
    set(ASM_OFFSETS_FILE "${CMAKE_CURRENT_BINARY_DIR}/Bytecode/AsmInterpreter/asm_offsets.conf")

    # Build-time offset generator: compiles a small C++ program that uses
    # offsetof() to emit struct field offsets as DSL constants.
    add_executable(gen_asm_offsets "${CMAKE_CURRENT_SOURCE_DIR}/Bytecode/AsmInterpreter/gen_asm_offsets.cpp")
    target_link_libraries(gen_asm_offsets PRIVATE AK)
    target_compile_definitions(gen_asm_offsets PRIVATE private=public protected=public)
    # NB: Strip fuzzer sanitizer flags so gen_asm_offsets can define its own main().
    if (ENABLE_FUZZERS_LIBFUZZER)
        target_compile_options(gen_asm_offsets PRIVATE -fno-sanitize=fuzzer)
        target_link_options(gen_asm_offsets PRIVATE -fno-sanitize=fuzzer)
    endif()
    # NB: gen_asm_offsets no longer depends on Op.h since opcode field
    # offsets are now computed from Bytecode.def by asmintgen directly.

    add_custom_command(
        OUTPUT "${ASM_OFFSETS_FILE}"
        COMMAND "$<TARGET_FILE:gen_asm_offsets>" > "${ASM_OFFSETS_FILE}"
        DEPENDS gen_asm_offsets
        COMMENT "Generating asm struct offsets"
    )

    file(GLOB ASMINTGEN_RUST_SOURCES CONFIGURE_DEPENDS "${ASMINTGEN_DIR}/src/*.rs")
    file(GLOB ASMINTGEN_BYTECODE_DEF_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/BytecodeDef/src/*.rs")
    set(ASMINTGEN_SOURCES
        "${ASMINTGEN_DIR}/Cargo.toml"
        "${ASMINTGEN_DIR}/Cargo.lock"
        "${CMAKE_CURRENT_SOURCE_DIR}/BytecodeDef/Cargo.toml"
        ${ASMINTGEN_RUST_SOURCES}
        ${ASMINTGEN_BYTECODE_DEF_SOURCES}
    )
    # NB: Clear linker-related env vars so cargo uses the default host
    # linker. The CI may set CC/CXX or CARGO_TARGET_*_LINKER to a
    # compiler that isn't available in all build configurations (e.g.
    # clang-21 in GNU builds). asmintgen is a pure Rust host tool.
    add_custom_command(
        OUTPUT "${ASMINTGEN_BIN}"
        COMMAND ${CMAKE_COMMAND} -E env
            --unset=CC
            --unset=CXX
            --unset=CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER
            --unset=CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER
            cargo build --release
        WORKING_DIRECTORY "${ASMINTGEN_DIR}"
        DEPENDS ${ASMINTGEN_SOURCES}
        COMMENT "Building asmintgen"
    )

    set(BYTECODE_DEF "${CMAKE_CURRENT_SOURCE_DIR}/Bytecode/Bytecode.def")

    if (APPLE)
        set(ASMINT_OBJ_FORMAT "macho")
    else()
        set(ASMINT_OBJ_FORMAT "elf")
    endif()

    set(ASMINT_EXTRA_FLAGS "")
    if (CMAKE_BUILD_TYPE STREQUAL "Debug"
        OR ENABLE_ADDRESS_SANITIZER
        OR ENABLE_MEMORY_SANITIZER
        OR ENABLE_UNDEFINED_SANITIZER)
        list(APPEND ASMINT_EXTRA_FLAGS "--enable-assertions")
    endif()
    # All Apple Silicon chips are ARMv8.5+ which includes FEAT_JSCVT.
    if ("${ASMINT_ARCH}" STREQUAL "aarch64" AND APPLE)
        list(APPEND ASMINT_EXTRA_FLAGS "--has-jscvt")
    endif()

    add_custom_command(
        OUTPUT "${ASMINT_GENERATED_S}"
        COMMAND "${ASMINTGEN_BIN}" --arch ${ASMINT_ARCH}
            --object-format ${ASMINT_OBJ_FORMAT}
            --constants "${ASM_OFFSETS_FILE}"
            --bytecode-def "${BYTECODE_DEF}"
            --input "${ASMINT_DSL}"
            --output "${ASMINT_GENERATED_S}"
            ${ASMINT_EXTRA_FLAGS}
        DEPENDS "${ASMINTGEN_BIN}" "${ASMINT_DSL}" "${ASM_OFFSETS_FILE}" "${BYTECODE_DEF}"
        COMMENT "Generating asmint_${ASMINT_ARCH}.S from DSL"
    )

    target_sources(LibJS PRIVATE "${ASMINT_GENERATED_S}")
endif()
