#!/bin/bash set -e if [[ "$(uname -s)" != Linux ]]; then echo "Fuzzing is only supported on Linux" exit 1 fi CC=${CC:-clang} CXX=${CXX:-clang++} default_fuzz_flags="-fsanitize=fuzzer,address,undefined" CFLAGS=${CFLAGS:-"$default_fuzz_flags"} CXXFLAGS=${CXXFLAGS:-"$default_fuzz_flags"} export CFLAGS make CC="$CC" CXX="$CXX" if [ -z "$@" ]; then languages=$(ls test/fixtures/grammars) else languages="$@" fi mkdir -p test/fuzz/out for lang in ${languages[@]}; do # skip typescript if [[ $lang == "typescript" ]]; then continue fi 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=c11 -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") highlights_filename="${lang_dir}/queries/highlights.scm" if [ -e "${highlights_filename}" ]; then ts_lang_query_filename="${lang}.scm" cp "${highlights_filename}" "test/fuzz/out/${ts_lang_query_filename}" else ts_lang_query_filename="" fi # FIXME: We should extract the grammar name from grammar.js. Use the name of # the directory instead. Also, the grammar name needs to be a valid C # identifier so replace any '-' characters ts_lang="tree_sitter_$(echo "$lang" | tr -- - _)" $CXX $CXXFLAGS -std=c++11 -I lib/include -D TS_LANG="$ts_lang" -D TS_LANG_QUERY_FILENAME="\"${ts_lang_query_filename}\"" \ "test/fuzz/fuzzer.cc" "${objects[@]}" \ libtree-sitter.a \ -o "test/fuzz/out/${lang}_fuzzer" jq ' [ .. | if .type? == "STRING" or (.type? == "ALIAS" and .named? == false) then .value else empty end | select(test("\\S") and length == utf8bytelength) ] | unique | .[] ' | sort done