#!/bin/bash
set -e

if [[ "$(uname -s)" != Linux ]]; then
  echo "Fuzzing is only supported on Linux"
  exit 1
fi

if [[ -z "$LIB_FUZZER_PATH" ]]; then
  echo "LIB_FUZZER_PATH not set"
  exit 1
fi

CC=${CC:-clang}
CXX=${CXX:-clang++}
LINK=${LINK:-clang++}

default_fuzz_flags="-fsanitize=address,undefined -fsanitize-coverage=trace-pc-guard"

CFLAGS=${CFLAGS:-"$default_fuzz_flags"}
CXXFLAGS=${CXXFLAGS:-"$default_fuzz_flags"}

CC=$CC CXX=$CXX LINK=$LINK CFLAGS=$CFLAGS CXXFLAGS=$CXXFLAGS ./script/configure

export BUILDTYPE=Fuzz
make runtime


if [ -z "$@" ]; then
  languages=$(ls test/fixtures/grammars)
else
  languages="$@"
fi

for lang in ${languages[@]}; do
  echo "Building $lang fuzzer..."
  lang_dir="test/fixtures/grammars/$lang"

  # The following assumes each language is implemented as src/parser.c plus an
  # optional scanner in src/scanner.{c,cc}
  objects=()

  lang_scanner="${lang_dir}/src/scanner"
  if [ -e "${lang_scanner}.cc" ]; then
    $CXX $CXXFLAGS -g -O1 "-I${lang_dir}/src" -c "${lang_scanner}.cc" -o "${lang_scanner}.o"
    objects+=("${lang_scanner}.o")
  elif [ -e "${lang_scanner}.c" ]; then
    $CC $CFLAGS -std=c99 -g -O1 "-I${lang_dir}/src" -c "${lang_scanner}.c" -o "${lang_scanner}.o"
    objects+=("${lang_scanner}.o")
  fi


  # Compiling with -O0 speeds up the build dramatically
  $CC $CFLAGS -g -O0 "-I${lang_dir}/src" "${lang_dir}/src/parser.c" -c -o "${lang_dir}/src/parser.o"
  objects+=("${lang_dir}/src/parser.o")

  modes=(true halt false recover)
  for i in 0 2; do
    $CXX $CXXFLAGS -std=c++11 -Iinclude -D TS_HALT_ON_ERROR="${modes[i]}" -D TS_LANG="tree_sitter_$lang" \
      "test/fuzz/fuzzer.cc" "${objects[@]}" \
      out/Fuzz/obj.target/libruntime.a "$LIB_FUZZER_PATH" \
      -o "out/${lang}_fuzzer_${modes[i+1]}"
  done

  python test/fuzz/gen-dict.py "${lang_dir}/src/grammar.json" > "out/$lang.dict"
done
