#!/bin/sh

# This script, and various infrastructure, is a hack to work around the lack of
# stable support in Rust to generate a weak symbol.
#
# The basic problem here is that the Rust `wit-bindgen` crate wants to export
# the `cabi_realloc` symbol from the final binary. This library, however,
# is not stable which means that we're publishing new versions of `wit-bindgen`
# over its development. This means that if `wit-bindgen`-the-crate were to
# export a `#[unsafe(no_mangle)]` symbol of `cabi_realloc` then it wouldn't
# work to have two versions of `wit-bindgen` in the same project. This can
# arise relatively quickly, however, so this is something we want to solve.
#
# The general idea of the solution here is to ensure that the `cabi_realloc`
# symbol itself is declared as a weak symbol. A weakly-defined symbol means
# that if the linker sees multiple copies it can discard all but one. This is
# the semantics we want where some `wit-bindgen` needs to define `cabi_realloc`
# but it doesn't matter much which one.
#
# Stable Rust can't define weak symbols as of the time of this writing. C,
# however, can. Unfortunately users of this crate do not always have a C
# compiler on-hand for wasm, nor do we want to require one. That's where all
# these hacks come into play. With that intro, the purpose of this script is to:
#
# * Generate a `cabi_realloc.rs` file with a "mangled" Rust symbol that's
#   unique per-major-version of the crate.
# * Generate a `cabi_realloc.c` file that defines a weak `cabi_realloc` symbol
#   that calls the above Rust symbol
# * Compile `cabi_realloc.c` into an object and place it into an archive and
#   check that archive into this repo.
#
# This all leads up to the point where we're distributing binary artifacts with
# this crate. These artifacts are verified in CI to ensure what this script
# generates.
#
# Overall this is intended to provide `cabi_realloc` as a weak symbol,
# everything works on stable Rust, and users don't need a C compiler when they
# use this crate.

set -ex

version=$(./ci/print-current-version.sh | sed 's/\./_/g')

realloc=cabi_realloc_wit_bindgen_$version

rm -f crates/guest-rust/src/rt/wit_bindgen_*.{rs,o,c}
rm -f crates/guest-rust/src/rt/libwit_bindgen_cabi.a

cat >./crates/guest-rust/src/rt/wit_bindgen_cabi_realloc.rs <<-EOF
// This file is generated by $0

#[unsafe(no_mangle)]
pub unsafe extern "C" fn $realloc(
    old_ptr: *mut u8,
    old_len: usize,
    align: usize,
    new_len: usize,
) -> *mut u8 {
    unsafe { crate::rt::cabi_realloc(old_ptr, old_len, align, new_len) }
}
EOF

cat >./crates/guest-rust/src/rt/wit_bindgen_cabi_realloc.c <<-EOF
// This file is generated by $0

#include <stdint.h>

extern void *$realloc(void *ptr, size_t old_size, size_t align, size_t new_size);

__attribute__((__weak__, __export_name__("cabi_realloc")))
void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) {
  return $realloc(ptr, old_size, align, new_size);
}
EOF

cat >./crates/guest-rust/src/rt/wit_bindgen_cabi_wasip3.c <<-EOF
// This file is generated by $0

#include <stdlib.h>

static void *WASIP3_TASK = NULL;

__attribute__((__weak__))
void *wasip3_task_set(void *ptr) {
  void *ret = WASIP3_TASK;
  WASIP3_TASK = ptr;
  return ret;
}
EOF

build() {
  file=$1
  $WASI_SDK_PATH/bin/clang crates/guest-rust/src/rt/$1.c \
    -O -c -o crates/guest-rust/src/rt/$1.o -mcpu=mvp -fPIC
  # Remove the `producers` section. This appears to differ whether the host for
  # clang is either macOS or Linux. Not needed here anyway, so discard it to help
  # either host produce the same object.
  wasm-tools strip -d producers ./crates/guest-rust/src/rt/$1.o \
    -o ./crates/guest-rust/src/rt/$1.o
}

build wit_bindgen_cabi_realloc
build wit_bindgen_cabi_wasip3

$WASI_SDK_PATH/bin/llvm-ar crus crates/guest-rust/src/rt/libwit_bindgen_cabi.a \
  crates/guest-rust/src/rt/wit_bindgen_cabi_realloc.o \
  crates/guest-rust/src/rt/wit_bindgen_cabi_wasip3.o
