slot0 = require("jit")
slot1 = require("bit")
slot2 = "luaJIT_BC_"

function slot3()
	io.stderr:write([[
Save LuaJIT bytecode: luajit -b[options] input output
  -l        Only list bytecode.
  -s        Strip debug info (default).
  -g        Keep debug info.
  -n name   Set module name (default: auto-detect from input name).
  -t type   Set output file type (default: auto-detect from output name).
  -a arch   Override architecture for object files (default: native).
  -o os     Override OS for object files (default: native).
  -e chunk  Use chunk string as input.
  --        Stop handling options.
  -         Use stdin as input and/or stdout as output.

File types: c h obj o raw (default)
]])
	os.exit(1)
end

function slot4(slot0, ...)
	if slot0 then
		return slot0, ...
	end

	io.stderr:write("luajit: ", ...)
	io.stderr:write("\n")
	os.exit(1)
end

function slot5(slot0)
	if type(slot0) == "function" then
		return slot0
	end

	if slot0 == "-" then
		slot0 = nil
	end

	return uv0(loadfile(slot0))
end

function slot6(slot0, slot1)
	if slot0 == "-" then
		return io.stdout
	end

	return uv0(io.open(slot0, slot1))
end

slot7 = {
	raw = "raw",
	c = "c",
	h = "h",
	obj = "obj",
	o = "obj"
}
slot8 = {
	x86 = true,
	arm = true,
	mips = true,
	arm64be = true,
	x64 = true,
	arm64 = true,
	ppc = true,
	mipsel = true
}
slot9 = {
	netbsd = true,
	dragonfly = true,
	openbsd = true,
	osx = true,
	freebsd = true,
	solaris = true,
	windows = true,
	linux = true
}

function slot10(slot0, slot1, slot2)
	return uv0(slot1[string.lower(slot0)], "unknown ", slot2) == true and slot0 or slot3
end

function slot11(slot0)
	return uv0[string.match(string.lower(slot0), "%.(%a+)$")] or "raw"
end

function slot12(slot0)
	uv0(string.match(slot0, "^[%w_.%-]+$"), "bad module name")

	return string.gsub(slot0, "[%.%-]", "_")
end

function slot13(slot0)
	slot0 = (type(slot0) ~= "string" or string.match(slot2, "^[%w_.%-]+")) and nil

	uv0(slot0, "cannot derive module name, use -n name")

	return string.gsub(slot0, "[%.%-]", "_")
end

function slot14(slot0, slot1, slot2)
	slot3, slot4 = slot0:write(slot2)

	if slot3 and slot1 ~= "-" then
		slot3, slot4 = slot0:close()
	end

	uv0(slot3, "cannot write ", slot1, ": ", slot4)
end

function slot15(slot0, slot1)
	uv1(uv0(slot0, "wb"), slot0, slot1)
end

function slot16(slot0, slot1, slot2)
	if slot0.type == "c" then
		uv0(slot1, "w"):write(string.format([[
#ifdef _cplusplus
extern "C"
#endif
#ifdef _WIN32
__declspec(dllexport)
#endif
const unsigned char %s%s[] = {
]], uv1, slot0.modname))
	else
		slot3:write(string.format("#define %s%s_SIZE %d\nstatic const unsigned char %s%s[] = {\n", uv1, slot0.modname, #slot2, uv1, slot0.modname))
	end

	slot4 = {}

	for slot10 = 1, #slot2 do
		if 0 + #tostring(string.byte(slot2, slot10)) + 1 > 78 then
			slot3:write(table.concat(slot4, ",", 1, 0), ",\n")

			slot6 = #slot11 + 1
			slot5 = 0
		end

		slot4[slot5 + 1] = slot11
	end

	uv2(slot3, slot1, table.concat(slot4, ",", 1, slot5) .. "\n};\n")
end

function slot17(slot0, slot1, slot2, slot3)
	slot3.cdef([[
typedef struct {
  uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];
  uint16_t type, machine;
  uint32_t version;
  uint32_t entry, phofs, shofs;
  uint32_t flags;
  uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;
} ELF32header;
typedef struct {
  uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];
  uint16_t type, machine;
  uint32_t version;
  uint64_t entry, phofs, shofs;
  uint32_t flags;
  uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;
} ELF64header;
typedef struct {
  uint32_t name, type, flags, addr, ofs, size, link, info, align, entsize;
} ELF32sectheader;
typedef struct {
  uint32_t name, type;
  uint64_t flags, addr, ofs, size;
  uint32_t link, info;
  uint64_t align, entsize;
} ELF64sectheader;
typedef struct {
  uint32_t name, value, size;
  uint8_t info, other;
  uint16_t sectidx;
} ELF32symbol;
typedef struct {
  uint32_t name;
  uint8_t info, other;
  uint16_t sectidx;
  uint64_t value, size;
} ELF64symbol;
typedef struct {
  ELF32header hdr;
  ELF32sectheader sect[6];
  ELF32symbol sym[2];
  uint8_t space[4096];
} ELF32obj;
typedef struct {
  ELF64header hdr;
  ELF64sectheader sect[6];
  ELF64symbol sym[2];
  uint8_t space[4096];
} ELF64obj;
]])

	slot4 = uv0 .. slot0.modname
	slot5 = false
	slot6 = false

	if slot0.arch == "x64" or slot0.arch == "arm64" or slot0.arch == "arm64be" then
		slot5 = true
	elseif slot0.arch == "ppc" or slot0.arch == "mips" then
		slot6 = true
	end

	function slot7(slot0)
		return slot0
	end

	slot8 = slot7
	slot9 = slot7

	if slot3.abi("be") ~= slot6 then
		slot7 = uv1.bswap

		function slot8(slot0)
			return uv0.rshift(uv0.bswap(slot0), 16)
		end

		if slot5 then
			slot10 = slot3.cast("int64_t", 4294967296.0)

			function slot9(slot0)
				return uv0.bswap(slot0) * uv1
			end
		else
			slot9 = slot7
		end
	end

	if slot0.os == "bsd" or slot0.os == "other" then
		slot12 = assert(io.open("/bin/ls", "rb"))

		slot12:close()
		slot3.copy(slot10, slot12:read(9), 9)
		uv2(slot3.new(slot5 and "ELF64obj" or "ELF32obj").hdr.emagic[0] == 127, "no support for writing native object files")
	else
		slot11.emagic = "ELF"
		slot11.eosabi = ({
			freebsd = 9,
			solaris = 6,
			openbsd = 12,
			netbsd = 2
		})[slot0.os] or 0
	end

	slot11.eclass = slot5 and 2 or 1
	slot11.eendian = slot6 and 2 or 1
	slot11.eversion = 1
	slot11.type = slot8(1)
	slot11.machine = slot8(({
		x86 = 3,
		arm = 40,
		mips = 8,
		arm64be = 183,
		x64 = 62,
		arm64 = 183,
		ppc = 20,
		mipsel = 8
	})[slot0.arch])

	if slot0.arch == "mips" or slot0.arch == "mipsel" then
		slot11.flags = slot7(1342181382)
	end

	slot11.version = slot7(1)
	slot11.shofs = slot9(slot3.offsetof(slot10, "sect"))
	slot11.ehsize = slot8(slot3.sizeof(slot11))
	slot11.shentsize = slot8(slot3.sizeof(slot10.sect[0]))
	slot11.shnum = slot8(6)
	slot11.shstridx = slot8(2)
	slot12 = slot3.offsetof(slot10, "space")
	slot13 = 1

	for slot17, slot18 in ipairs({
		".symtab",
		".shstrtab",
		".strtab",
		".rodata",
		".note.GNU-stack"
	}) do
		slot19 = slot10.sect[slot17]
		slot19.align = slot9(1)
		slot19.name = slot7(slot13)

		slot3.copy(slot10.space + slot13, slot18)

		slot13 = slot13 + #slot18 + 1
	end

	slot10.sect[1].type = slot7(2)
	slot10.sect[1].link = slot7(3)
	slot10.sect[1].info = slot7(1)
	slot10.sect[1].align = slot9(8)
	slot10.sect[1].ofs = slot9(slot3.offsetof(slot10, "sym"))
	slot10.sect[1].entsize = slot9(slot3.sizeof(slot10.sym[0]))
	slot10.sect[1].size = slot9(slot3.sizeof(slot10.sym))
	slot10.sym[1].name = slot7(1)
	slot10.sym[1].sectidx = slot8(4)
	slot10.sym[1].size = slot9(#slot2)
	slot10.sym[1].info = 17
	slot10.sect[2].type = slot7(3)
	slot10.sect[2].ofs = slot9(slot12)
	slot10.sect[2].size = slot9(slot13)
	slot10.sect[3].type = slot7(3)
	slot10.sect[3].ofs = slot9(slot12 + slot13)
	slot10.sect[3].size = slot9(#slot4 + 1)

	slot3.copy(slot10.space + slot13 + 1, slot4)

	slot13 = slot13 + #slot4 + 2
	slot10.sect[4].type = slot7(1)
	slot10.sect[4].flags = slot9(2)
	slot10.sect[4].ofs = slot9(slot12 + slot13)
	slot10.sect[4].size = slot9(#slot2)
	slot10.sect[5].type = slot7(1)
	slot10.sect[5].ofs = slot9(slot12 + slot13 + #slot2)
	slot14 = uv3(slot1, "wb")

	slot14:write(slot3.string(slot10, slot3.sizeof(slot10) - 4096 + slot13))
	uv4(slot14, slot1, slot2)
end

function slot18(slot0, slot1, slot2, slot3)
	slot3.cdef([[
typedef struct {
  uint16_t arch, nsects;
  uint32_t time, symtabofs, nsyms;
  uint16_t opthdrsz, flags;
} PEheader;
typedef struct {
  char name[8];
  uint32_t vsize, vaddr, size, ofs, relocofs, lineofs;
  uint16_t nreloc, nline;
  uint32_t flags;
} PEsection;
typedef struct __attribute((packed)) {
  union {
    char name[8];
    uint32_t nameref[2];
  };
  uint32_t value;
  int16_t sect;
  uint16_t type;
  uint8_t scl, naux;
} PEsym;
typedef struct __attribute((packed)) {
  uint32_t size;
  uint16_t nreloc, nline;
  uint32_t cksum;
  uint16_t assoc;
  uint8_t comdatsel, unused[3];
} PEsymaux;
typedef struct {
  PEheader hdr;
  PEsection sect[2];
  // Must be an even number of symbol structs.
  PEsym sym0;
  PEsymaux sym0aux;
  PEsym sym1;
  PEsymaux sym1aux;
  PEsym sym2;
  PEsym sym3;
  uint32_t strtabsize;
  uint8_t space[4096];
} PEobj;
]])

	slot5 = false

	if slot0.arch == "x86" then
		slot4 = "_" .. (uv0 .. slot0.modname)
	elseif slot0.arch == "x64" then
		slot5 = true
	end

	slot6 = "   /EXPORT:" .. slot4 .. ",DATA "

	function slot8(slot0)
		return slot0
	end

	if slot3.abi("be") then
		slot7 = uv1.bswap

		function slot8(slot0)
			return uv0.rshift(uv0.bswap(slot0), 16)
		end
	end

	slot9 = slot3.new("PEobj")
	slot10 = slot9.hdr
	slot10.arch = slot8(({
		arm = 448,
		ppc = 498,
		mips = 870,
		mipsel = 870,
		x64 = 34404,
		x86 = 332
	})[slot0.arch])
	slot10.nsects = slot8(2)
	slot10.symtabofs = slot7(slot3.offsetof(slot9, "sym0"))
	slot10.nsyms = slot7(6)
	slot9.sect[0].name = ".drectve"
	slot9.sect[0].size = slot7(#slot6)
	slot9.sect[0].flags = slot7(1051136)
	slot9.sym0.sect = slot8(1)
	slot9.sym0.scl = 3
	slot9.sym0.name = ".drectve"
	slot9.sym0.naux = 1
	slot9.sym0aux.size = slot7(#slot6)
	slot9.sect[1].name = ".rdata"
	slot9.sect[1].size = slot7(#slot2)
	slot9.sect[1].flags = slot7(1076887616)
	slot9.sym1.sect = slot8(2)
	slot9.sym1.scl = 3
	slot9.sym1.name = ".rdata"
	slot9.sym1.naux = 1
	slot9.sym1aux.size = slot7(#slot2)
	slot9.sym2.sect = slot8(2)
	slot9.sym2.scl = 2
	slot9.sym2.nameref[1] = slot7(4)
	slot9.sym3.sect = slot8(-1)
	slot9.sym3.scl = 2
	slot9.sym3.value = slot7(1)
	slot9.sym3.name = "@feat.00"

	slot3.copy(slot9.space, slot4)

	slot11 = #slot4 + 1
	slot9.strtabsize = slot7(slot11 + 4)
	slot9.sect[0].ofs = slot7(slot3.offsetof(slot9, "space") + slot11)

	slot3.copy(slot9.space + slot11, slot6)

	slot11 = slot11 + #slot6
	slot9.sect[1].ofs = slot7(slot3.offsetof(slot9, "space") + slot11)
	slot12 = uv2(slot1, "wb")

	slot12:write(slot3.string(slot9, slot3.sizeof(slot9) - 4096 + slot11))
	uv3(slot12, slot1, slot2)
end

function slot19(slot0, slot1, slot2, slot3)
	slot3.cdef([[
typedef struct
{
  uint32_t magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags;
} mach_header;
typedef struct
{
  mach_header; uint32_t reserved;
} mach_header_64;
typedef struct {
  uint32_t cmd, cmdsize;
  char segname[16];
  uint32_t vmaddr, vmsize, fileoff, filesize;
  uint32_t maxprot, initprot, nsects, flags;
} mach_segment_command;
typedef struct {
  uint32_t cmd, cmdsize;
  char segname[16];
  uint64_t vmaddr, vmsize, fileoff, filesize;
  uint32_t maxprot, initprot, nsects, flags;
} mach_segment_command_64;
typedef struct {
  char sectname[16], segname[16];
  uint32_t addr, size;
  uint32_t offset, align, reloff, nreloc, flags;
  uint32_t reserved1, reserved2;
} mach_section;
typedef struct {
  char sectname[16], segname[16];
  uint64_t addr, size;
  uint32_t offset, align, reloff, nreloc, flags;
  uint32_t reserved1, reserved2, reserved3;
} mach_section_64;
typedef struct {
  uint32_t cmd, cmdsize, symoff, nsyms, stroff, strsize;
} mach_symtab_command;
typedef struct {
  int32_t strx;
  uint8_t type, sect;
  int16_t desc;
  uint32_t value;
} mach_nlist;
typedef struct {
  uint32_t strx;
  uint8_t type, sect;
  uint16_t desc;
  uint64_t value;
} mach_nlist_64;
typedef struct
{
  uint32_t magic, nfat_arch;
} mach_fat_header;
typedef struct
{
  uint32_t cputype, cpusubtype, offset, size, align;
} mach_fat_arch;
typedef struct {
  struct {
    mach_header hdr;
    mach_segment_command seg;
    mach_section sec;
    mach_symtab_command sym;
  } arch[1];
  mach_nlist sym_entry;
  uint8_t space[4096];
} mach_obj;
typedef struct {
  struct {
    mach_header_64 hdr;
    mach_segment_command_64 seg;
    mach_section_64 sec;
    mach_symtab_command sym;
  } arch[1];
  mach_nlist_64 sym_entry;
  uint8_t space[4096];
} mach_obj_64;
typedef struct {
  mach_fat_header fat;
  mach_fat_arch fat_arch[2];
  struct {
    mach_header hdr;
    mach_segment_command seg;
    mach_section sec;
    mach_symtab_command sym;
  } arch[2];
  mach_nlist sym_entry;
  uint8_t space[4096];
} mach_fat_obj;
]])

	slot4 = "_" .. uv0 .. slot0.modname
	slot5 = false
	slot6 = false
	slot7 = 4
	slot8 = "mach_obj"

	if slot0.arch == "x64" then
		slot8 = "mach_obj_64"
		slot7 = 8
		slot6 = true
	elseif slot0.arch == "arm" then
		slot8 = "mach_fat_obj"
		slot5 = true
	elseif slot0.arch == "arm64" then
		slot8 = "mach_fat_obj"
		slot5 = true
		slot7 = 8
		slot6 = true
	else
		uv1(slot0.arch == "x86", "unsupported architecture for OSX")
	end

	slot10 = uv2.bswap
	slot12 = function (slot0, slot1)
		return uv0.band(slot0 + slot1 - 1, -slot1)
	end(slot3.offsetof(slot3.new(slot8), "space") + #slot4 + 2, slot7)
	slot13 = ({
		x86 = {
			7
		},
		x64 = {
			16777223
		},
		arm = {
			7,
			12
		},
		arm64 = {
			16777223,
			16777228
		}
	})[slot0.arch]
	slot14 = ({
		x86 = {
			3
		},
		x64 = {
			3
		},
		arm = {
			3,
			9
		},
		arm64 = {
			3,
			0
		}
	})[slot0.arch]

	if slot5 then
		slot11.fat.magic = slot10(3405691582.0)
		slot11.fat.nfat_arch = slot10(#slot14)
	end

	for slot18 = 0, #slot14 - 1 do
		slot19 = 0

		if slot5 then
			slot20 = slot11.fat_arch[slot18]
			slot20.cputype = slot10(slot13[slot18 + 1])
			slot20.cpusubtype = slot10(slot14[slot18 + 1])
			slot19 = slot3.offsetof(slot11, "arch") + slot18 * slot3.sizeof(slot11.arch[0])
			slot20.offset = slot10(slot19)
			slot20.size = slot10(slot12 - slot19 + #slot2)
		end

		slot11.arch[slot18].hdr.magic = slot6 and 4277009103.0 or 4277009102.0
		slot20.hdr.cputype = slot13[slot18 + 1]
		slot20.hdr.cpusubtype = slot14[slot18 + 1]
		slot20.hdr.filetype = 1
		slot20.hdr.ncmds = 2
		slot20.hdr.sizeofcmds = slot3.sizeof(slot20.seg) + slot3.sizeof(slot20.sec) + slot3.sizeof(slot20.sym)
		slot20.seg.cmd = slot6 and 25 or 1
		slot20.seg.cmdsize = slot3.sizeof(slot20.seg) + slot3.sizeof(slot20.sec)
		slot20.seg.vmsize = #slot2
		slot20.seg.fileoff = slot12 - slot19
		slot20.seg.filesize = #slot2
		slot20.seg.maxprot = 1
		slot20.seg.initprot = 1
		slot20.seg.nsects = 1

		slot3.copy(slot20.sec.sectname, "__data")
		slot3.copy(slot20.sec.segname, "__DATA")

		slot20.sec.size = #slot2
		slot20.sec.offset = slot12 - slot19
		slot20.sym.cmd = 2
		slot20.sym.cmdsize = slot3.sizeof(slot20.sym)
		slot20.sym.symoff = slot3.offsetof(slot11, "sym_entry") - slot19
		slot20.sym.nsyms = 1
		slot20.sym.stroff = slot3.offsetof(slot11, "sym_entry") + slot3.sizeof(slot11.sym_entry) - slot19
		slot20.sym.strsize = slot9(#slot4 + 2, slot7)
	end

	slot11.sym_entry.type = 15
	slot11.sym_entry.sect = 1
	slot11.sym_entry.strx = 1

	slot3.copy(slot11.space + 1, slot4)

	slot15 = uv3(slot1, "wb")

	slot15:write(slot3.string(slot11, slot12))
	uv4(slot15, slot1, slot2)
end

function slot20(slot0, slot1, slot2)
	slot3, slot4 = pcall(require, "ffi")

	uv0(slot3, "FFI library required to write this file type")

	if slot0.os == "windows" then
		return uv1(slot0, slot1, slot2, slot4)
	elseif slot0.os == "osx" then
		return uv2(slot0, slot1, slot2, slot4)
	else
		return uv3(slot0, slot1, slot2, slot4)
	end
end

function slot21(slot0, slot1)
	require("jit.bc").dump(uv0(slot0), uv1(slot1, "w"), true)
end

function slot22(slot0, slot1, slot2)
	slot4 = string.dump(uv0(slot1), slot0.strip)

	if not slot0.type then
		slot0.type = uv1(slot2)
	end

	if slot5 == "raw" then
		uv2(slot2, slot4)
	else
		if not slot0.modname then
			slot0.modname = uv3(slot1)
		end

		if slot5 == "obj" then
			uv4(slot0, slot2, slot4)
		else
			uv5(slot0, slot2, slot4)
		end
	end
end

return {
	start = function (...)
		slot0 = {
			...
		}
		slot1 = 1
		slot2 = false
		slot3 = {
			modname = false,
			strip = true,
			type = false,
			arch = uv0.arch,
			os = string.lower(uv0.os)
		}

		while slot1 <= #slot0 do
			if type(slot0[slot1]) == "string" and string.sub(slot4, 1, 1) == "-" and slot4 ~= "-" then
				table.remove(slot0, slot1)

				if slot4 == "--" then
					break
				end

				for slot8 = 2, #slot4 do
					if string.sub(slot4, slot8, slot8) == "l" then
						slot2 = true
					elseif slot9 == "s" then
						slot3.strip = true
					elseif slot9 == "g" then
						slot3.strip = false
					else
						if slot0[slot1] == nil or slot8 ~= #slot4 then
							uv1()
						end

						if slot9 == "e" then
							if slot1 ~= 1 then
								uv1()
							end

							slot0[1] = uv2(loadstring(slot0[1]))
						elseif slot9 == "n" then
							slot3.modname = uv3(table.remove(slot0, slot1))
						elseif slot9 == "t" then
							slot3.type = uv4(table.remove(slot0, slot1), uv5, "file type")
						elseif slot9 == "a" then
							slot3.arch = uv4(table.remove(slot0, slot1), uv6, "architecture")
						elseif slot9 == "o" then
							slot3.os = uv4(table.remove(slot0, slot1), uv7, "OS name")
						else
							uv1()
						end
					end
				end
			else
				slot1 = slot1 + 1
			end
		end

		if slot2 then
			if #slot0 == 0 or #slot0 > 2 then
				uv1()
			end

			uv8(slot0[1], slot0[2] or "-")
		else
			if #slot0 ~= 2 then
				uv1()
			end

			uv9(slot3, slot0[1], slot0[2])
		end
	end
}
