dev-test/tests/luau_assert.lua
do
	local WASM_PAGE_SIZE = 65536

	local function is_valid_address(memory, addr, size)
		return addr >= 0 and addr + size <= rt.allocator.size(memory) * WASM_PAGE_SIZE
	end

	local function load_checked(name, size)
		local old = assert(rt.load[name], "Missing load function " .. name)

		rt.load[name] = function(memory, addr)
			assert(is_valid_address(memory, addr, size), "Invalid memory read")

			return old(memory, addr)
		end
	end

	local function store_checked(name, size)
		local old = assert(rt.store[name], "Missing store function " .. name)

		rt.store[name] = function(memory, addr, value)
			assert(is_valid_address(memory, addr, size), "Invalid memory write")

			return old(memory, addr, value)
		end
	end

	do
		local old = rt.store.string

		function rt.store.string(memory, addr, data, len)
			assert(is_valid_address(memory, addr, #data), "Invalid memory write")

			return old(memory, addr, data, len)
		end
	end

	load_checked("i32_i8", 1)
	load_checked("i32_u8", 1)
	load_checked("i32_i16", 2)
	load_checked("i32_u16", 2)
	load_checked("i32", 4)
	load_checked("i64_i8", 1)
	load_checked("i64_u8", 1)
	load_checked("i64_i16", 2)
	load_checked("i64_u16", 2)
	load_checked("i64_i32", 4)
	load_checked("i64_u32", 4)
	load_checked("i64", 8)
	load_checked("f32", 4)
	load_checked("f64", 8)

	store_checked("i32_n8", 1)
	store_checked("i32_n16", 2)
	store_checked("i32", 4)
	store_checked("i64_n8", 1)
	store_checked("i64_n16", 2)
	store_checked("i64_n32", 4)
	store_checked("i64", 8)
	store_checked("f32", 4)
	store_checked("f64", 8)
end

local loaded = {}
local linked = {}

local LUA_NAN_ARITHMETIC = 0 / 0
local LUA_NAN_CANONICAL = 0 / 0
local LUA_NAN_DEFAULT = 0 / 0
local LUA_INFINITY = math.huge

local function is_number_equal(lhs, rhs)
	if type(lhs) == "table" and type(rhs) == "table" then
		return rt.eq.i64(lhs, rhs)
	elseif type(lhs) ~= "number" or type(rhs) ~= "number" then
		return false
	elseif lhs ~= lhs and rhs ~= rhs then
		return true
	end

	return math.abs(lhs - rhs) < 0.00001 or string.format("%.3g", lhs) == string.format("%.3g", rhs)
end

local function to_string(data)
	if type(data) == "table" then
		data = rt.convert.f64_i64(data)
	end

	return tostring(data)
end

local function assert_eq(lhs, rhs, level)
	if lhs == rhs or is_number_equal(lhs, rhs) then
		return
	end

	lhs = to_string(lhs)
	rhs = to_string(rhs)
	level = (level or 1) + 1

	error(lhs .. " ~= " .. rhs, level)
end

local function assert_neq(lhs, rhs, level)
	if lhs ~= rhs and not is_number_equal(lhs, rhs) then
		return
	end

	lhs = to_string(lhs)
	rhs = to_string(rhs)
	level = (level or 1) + 1

	error(lhs .. " == " .. rhs, level)
end

local function raw_invoke(func, ...)
	return func(...)
end

local function assert_trap(func, ...)
	if pcall(func, ...) then
		local trace = debug.traceback("Failed to trap", 2)

		print(trace)
	end
end

local function assert_return(data, wanted)
	for i, v in wanted do
		assert_eq(data[i], v, 2)
	end
end

local function assert_exhaustion(func, ...)
	if pcall(func, ...) then
		error("Failed to exhaust", 2)
	end
end

linked.spectest = {
	func_list = {
		print = print,
		print_f32 = print,
		print_f64 = print,
		print_f64_f64 = print,
		print_i32 = print,
		print_i32_f32 = print,
	},
	global_list = {
		global_f32 = { value = 666 },
		global_f64 = { value = 666 },
		global_i32 = { value = 666 },
		global_i64 = { value = rt.i64.from_u32(666, 0) },
	},
	table_list = { table = { data = {} } },
	memory_list = { memory = rt.allocator.new(1, 2) },
}
