Skip to main content

lib/quickbeam/native.ex

defmodule QuickBEAM.Native do
  @moduledoc false

  @native_version Mix.Project.config()[:version]
  @local_dev Mix.env() == :dev and Mix.Project.config()[:build_path] == "../../_build"
  @build_from_source @local_dev or
                       System.get_env("DUSKMOON_BUILD_NATIVE_FROM_SOURCE") in ["1", "true"]

  @c_src_dir Application.app_dir(:duskmoon_quickbeam, "priv/c_src")
  @hidden_cflags ["-fvisibility=hidden"]
  @lexbor_base_cflags [
    "-std=c99",
    "-DLEXBOR_STATIC",
    "-I#{@c_src_dir}",
    "-I#{@c_src_dir}/lexbor/ports/posix"
  ]
  @lexbor_cflags @lexbor_base_cflags ++ @hidden_cflags

  @lexbor_src Path.wildcard("priv/c_src/lexbor/{core,dom,html,tag,ns,css,selectors}/**/*.c")
              |> Enum.concat(Path.wildcard("priv/c_src/lexbor/ports/posix/**/*.c"))
              |> Enum.sort()
              |> Enum.map(fn path ->
                {:priv, String.replace_prefix(path, "priv/", ""), @lexbor_cflags}
              end)

  @wamr_base_cflags [
    "-std=c11",
    "-D_GNU_SOURCE",
    "-DWASM_ENABLE_INTERP=1",
    "-DWASM_ENABLE_AOT=0",
    "-DWASM_ENABLE_FAST_INTERP=0",
    "-DWASM_ENABLE_LIBC_BUILTIN=0",
    "-DWASM_ENABLE_LIBC_WASI=0",
    "-DWASM_ENABLE_MULTI_MODULE=0",
    "-DWASM_ENABLE_BULK_MEMORY=1",
    "-DWASM_ENABLE_REF_TYPES=1",
    "-DWASM_ENABLE_SIMD=0",
    "-DWASM_ENABLE_TAIL_CALL=1",
    "-DWASM_ENABLE_MEMORY64=0",
    "-DWASM_ENABLE_GC=0",
    "-DWASM_ENABLE_THREAD_MGR=0",
    "-DWASM_ENABLE_SHARED_MEMORY=0",
    "-DWASM_ENABLE_EXCE_HANDLING=0",
    "-DWASM_ENABLE_MINI_LOADER=0",
    "-DWASM_ENABLE_WAMR_COMPILER=0",
    "-DWASM_ENABLE_JIT=0",
    "-DWASM_ENABLE_FAST_JIT=0",
    "-DWASM_ENABLE_DEBUG_INTERP=0",
    "-DWASM_ENABLE_INSTRUCTION_METERING=1",
    "-DWASM_ENABLE_DUMP_CALL_STACK=0",
    "-DWASM_ENABLE_PERF_PROFILING=0",
    "-DWASM_ENABLE_LOAD_CUSTOM_SECTION=0",
    "-DWASM_ENABLE_CUSTOM_NAME_SECTION=1",
    "-DWASM_ENABLE_GLOBAL_HEAP_POOL=0",
    "-DWASM_ENABLE_SPEC_TEST=0",
    "-DWASM_ENABLE_LABELS_AS_VALUES=1",
    "-DWASM_ENABLE_WASM_CACHE=0",
    "-DWASM_ENABLE_STRINGREF=0",
    "-DWASM_MEM_ALLOC_WITH_SYSTEM_ALLOCATOR=1",
    "-DWASM_API_EXTERN=",
    "-DWASM_RUNTIME_API_EXTERN=",
    "-DBH_MALLOC=wasm_runtime_malloc",
    "-DBH_FREE=wasm_runtime_free",
    "-I#{@c_src_dir}",
    "-I#{@c_src_dir}/wamr/include",
    "-I#{@c_src_dir}/wamr/interpreter",
    "-I#{@c_src_dir}/wamr/common",
    "-I#{@c_src_dir}/wamr/shared/utils",
    "-I#{@c_src_dir}/wamr/shared/platform/include",
    "-I#{@c_src_dir}/wamr/shared/mem-alloc",
    "-I#{@c_src_dir}/wamr/shared/platform/#{if(:os.type() == {:unix, :darwin}, do: "darwin", else: "linux")}"
  ]
  @wamr_cflags @wamr_base_cflags ++ @hidden_cflags

  @wamr_src (Path.wildcard("priv/c_src/wamr/interpreter/wasm_loader.c") ++
               Path.wildcard("priv/c_src/wamr/interpreter/wasm_interp_classic.c") ++
               Path.wildcard("priv/c_src/wamr/interpreter/wasm_runtime.c") ++
               Path.wildcard("priv/c_src/wamr/common/wasm_runtime_common.c") ++
               Path.wildcard("priv/c_src/wamr/common/wasm_exec_env.c") ++
               Path.wildcard("priv/c_src/wamr/common/wasm_memory.c") ++
               Path.wildcard("priv/c_src/wamr/common/wasm_native.c") ++
               Path.wildcard("priv/c_src/wamr/common/wasm_application.c") ++
               Path.wildcard("priv/c_src/wamr/common/wasm_loader_common.c") ++
               Path.wildcard("priv/c_src/wamr/common/wasm_blocking_op.c") ++
               Path.wildcard("priv/c_src/wamr/common/wasm_c_api.c") ++
               Path.wildcard("priv/c_src/wamr/shared/utils/bh_assert.c") ++
               Path.wildcard("priv/c_src/wamr/shared/utils/bh_common.c") ++
               Path.wildcard("priv/c_src/wamr/shared/utils/bh_hashmap.c") ++
               Path.wildcard("priv/c_src/wamr/shared/utils/bh_leb128.c") ++
               Path.wildcard("priv/c_src/wamr/shared/utils/bh_list.c") ++
               Path.wildcard("priv/c_src/wamr/shared/utils/bh_log.c") ++
               Path.wildcard("priv/c_src/wamr/shared/utils/bh_queue.c") ++
               Path.wildcard("priv/c_src/wamr/shared/utils/bh_vector.c") ++
               Path.wildcard("priv/c_src/wamr/shared/utils/bh_bitmap.c") ++
               Path.wildcard("priv/c_src/wamr/shared/utils/runtime_timer.c") ++
               Path.wildcard("priv/c_src/wamr/shared/mem-alloc/mem_alloc.c") ++
               Path.wildcard("priv/c_src/wamr/shared/mem-alloc/ems/*.c") ++
               Path.wildcard("priv/c_src/wamr/shared/platform/common/posix/posix_malloc.c") ++
               Path.wildcard("priv/c_src/wamr/shared/platform/common/posix/posix_memmap.c") ++
               Path.wildcard("priv/c_src/wamr/shared/platform/common/posix/posix_thread.c") ++
               Path.wildcard("priv/c_src/wamr/shared/platform/common/posix/posix_time.c") ++
               Path.wildcard("priv/c_src/wamr/shared/platform/common/posix/posix_blocking_op.c") ++
               [
                 if(:os.type() == {:unix, :darwin},
                   do: "priv/c_src/wamr/shared/platform/darwin/platform_init.c",
                   else: "priv/c_src/wamr/shared/platform/linux/platform_init.c"
                 )
               ] ++
               ["priv/c_src/wamr/common/arch/invokeNative_general.c"] ++
               ["priv/c_src/wamr/shared/platform/common/memory/mremap.c"] ++
               ["priv/c_src/wamr_bridge.c"])
            |> Enum.sort()
            |> Enum.map(fn path ->
              {:priv, String.replace_prefix(path, "priv/", ""), @wamr_cflags}
            end)

  @quickjs_cflags if System.get_env("QUICKBEAM_UBSAN") == "1",
                    do: [
                      "-std=c11",
                      "-D_GNU_SOURCE",
                      "-fsanitize=undefined",
                      "-fno-sanitize=function,unsigned-integer-overflow",
                      "-fsanitize-trap=undefined"
                    ],
                    else: ["-std=c11", "-D_GNU_SOURCE"]

  @quickjs_cflags @quickjs_cflags ++ @hidden_cflags

  if System.get_env("QUICKBEAM_BUILD") in ["1", "true"] and
       is_nil(System.get_env("ZIG_LOCAL_CACHE_DIR")) do
    zig_local_cache_dir = Path.expand(Path.join(Mix.Project.build_path(), "zig-cache"))
    File.mkdir_p!(zig_local_cache_dir)
    System.put_env("ZIG_LOCAL_CACHE_DIR", zig_local_cache_dir)
  end

  use ZiglerPrecompiled,
    otp_app: :duskmoon_quickbeam,
    base_url:
      "https://github.com/duskmoon-dev/phoenix-duskmoon-ui/releases/download/v#{@native_version}",
    version: @native_version,
    force_build: @build_from_source or System.get_env("QUICKBEAM_BUILD") in ["1", "true"],
    targets: ~w(
      aarch64-linux-gnu
      aarch64-macos-none
      x86_64-freebsd-none
      x86_64-linux-gnu
      x86_64-macos-none
    ),
    zig_code_path: "quickbeam.zig",
    optimize: :env,
    c: [
      include_dirs: [
        {:priv, "c_src"},
        {:priv, "c_src/lexbor/ports/posix"},
        {:priv, "c_src/wamr/include"},
        {:priv, "c_src/wamr/interpreter"},
        {:priv, "c_src/wamr/common"},
        {:priv, "c_src/wamr/shared/utils"},
        {:priv, "c_src/wamr/shared/platform/include"},
        {:priv, "c_src/wamr/shared/mem-alloc"}
      ],
      src:
        [
          {:priv, "c_src/quickjs.c", @quickjs_cflags},
          {:priv, "c_src/libregexp.c", @quickjs_cflags},
          {:priv, "c_src/libunicode.c", @quickjs_cflags},
          {:priv, "c_src/dtoa.c", @quickjs_cflags},
          {:priv, "c_src/lexbor_bridge.c", @lexbor_cflags}
        ] ++ @lexbor_src ++ @wamr_src
    ],
    resources: [:RuntimeResource, :PoolResource, :WasmModuleResource, :WasmInstanceResource],
    nifs: [
      eval: 4,
      compile: 2,
      call_function: 4,
      load_module: 3,
      load_bytecode: 2,
      reset_runtime: 1,
      stop_runtime: 1,
      start_runtime: 2,
      resolve_call: 3,
      reject_call: 3,
      resolve_call_term: 3,
      reject_call_term: 3,
      send_message: 2,
      define_global: 3,
      get_global: 2,
      delete_globals: 2,
      snapshot_globals: 1,
      list_globals: 2,
      memory_usage: 1,
      dom_find: 2,
      dom_find_all: 2,
      dom_text: 2,
      dom_attr: 3,
      dom_html: 1,
      pool_start: 1,
      pool_stop: 1,
      pool_create_context: 5,
      pool_destroy_context: 2,
      pool_eval: 4,
      pool_call_function: 5,
      pool_reset_context: 2,
      pool_send_message: 3,
      pool_define_global: 4,
      pool_load_bytecode: 3,
      pool_get_global: 3,
      pool_memory_usage: 2,
      pool_resolve_call_term: 4,
      pool_reject_call_term: 4,
      pool_dom_find: 3,
      pool_dom_find_all: 3,
      pool_dom_text: 3,
      pool_dom_html: 2,
      disasm_bytecode: 1,
      load_addon: 3,
      wasm_compile: 1,
      wasm_start: 3,
      wasm_start_with_imports: 5,
      wasm_stop: 1,
      wasm_call: 3,
      wasm_memory_size: 1,
      wasm_memory_grow: 2,
      wasm_read_memory: 3,
      wasm_write_memory: 3,
      wasm_read_global: 2,
      wasm_write_global: 3,
      enable_coverage: 1,
      get_coverage: 1,
      reset_coverage: 1
    ]
end