#!/usr/bin/env bash # seam_binding.sh — integration control for the runtime construct seam. # # The seam's whole claim is that a construct declared AFTER a binary exists # applies to that already-built program. That cannot be checked by # compile_capture, which only sees emitted text: it needs a built binary, a # linked target, and an environment. Hence a harness rather than a unit test. # # usage: seam_binding.sh [lang-dir] # exit 0 = all assertions held; non-zero = number of failures set -uo pipefail ELC="${1:?usage: seam_binding.sh [lang-dir]}" LANG_DIR="${2:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}" WORK="$(mktemp -d)"; trap 'rm -rf "$WORK"' EXIT FAILS=0 ok() { printf ' ok %s\n' "$1"; } fail() { printf ' FAIL %s\n expected: %s\n actual: %s\n' "$1" "$2" "$3"; FAILS=$((FAILS+1)); } check(){ [ "$2" = "$3" ] && ok "$1" || fail "$1" "$2" "$3"; } SRCS=$("$LANG_DIR/../scripts/el-runtime-sources.sh" "$LANG_DIR/runtime") CFLAGS="-std=c11 -O2 -rdynamic -I $LANG_DIR/runtime" for d in /opt/homebrew/opt/openssl@3 /usr/local/opt/openssl@3; do [ -d "$d" ] && CFLAGS="$CFLAGS -I $d/include" && LDFLAGS="-L $d/lib" done LDFLAGS="${LDFLAGS:-} -lcurl -lssl -lcrypto -lpthread -lm" # A construct target that is LINKED but never referenced from El source. cat > "$WORK/targets.c" <<'EOF' #include #include typedef int64_t el_val_t; el_val_t observe(el_val_t fn, el_val_t con, el_val_t r){ printf("SEEN %s/%s\n", (const char*)(intptr_t)fn, (const char*)(intptr_t)con); return r; /* zero = do not refuse */ } el_val_t refuse(el_val_t fn, el_val_t con, el_val_t r){ (void)fn; (void)con; (void)r; return 42; /* non-zero = short-circuit */ } EOF # A program with NO construct anywhere in its source. cat > "$WORK/prog.el" <<'EOF' fn work() -> Int { return 7 } fn main() { println(int_to_str(work())) } EOF "$ELC" "$WORK/prog.el" > "$WORK/prog.c" 2>/dev/null cc $CFLAGS -o "$WORK/prog" "$WORK/prog.c" "$WORK/targets.c" $SRCS $LDFLAGS 2>/dev/null \ || { echo " FAIL probe did not build"; exit 1; } check "unbound program is unaffected" \ "7" "$(cd "$WORK" && ./prog 2>&1)" printf 'work audited entry observe\n' > "$WORK/observe.txt" check "a construct declared AFTER the build applies" \ "SEEN work/audited 7" "$(cd "$WORK" && EL_CONSTRUCTS=observe.txt ./prog 2>&1)" printf 'work denied entry refuse\n' > "$WORK/refuse.txt" check "a construct declared after the build can REFUSE" \ "42" "$(cd "$WORK" && EL_CONSTRUCTS=refuse.txt ./prog 2>&1)" printf 'work ghost entry no_such_symbol_anywhere\n' > "$WORK/ghost.txt" check "an unlinked target is skipped, not fatal" \ "7" "$(cd "$WORK" && EL_CONSTRUCTS=ghost.txt ./prog 2>&1)" printf 'other_fn x entry refuse\n' > "$WORK/other.txt" check "a binding for a different fn does not fire" \ "7" "$(cd "$WORK" && EL_CONSTRUCTS=other.txt ./prog 2>&1)" printf 'work a entry observe\nwork b entry observe\n' > "$WORK/two.txt" check "two constructs compose on one crossing" \ "SEEN work/a SEEN work/b 7" "$(cd "$WORK" && EL_CONSTRUCTS=two.txt ./prog 2>&1)" echo echo " 6 assertions, $((6-FAILS)) passed, $FAILS failed" exit $FAILS