Compartmentalize tests
This commit is contained in:
		
							parent
							
								
									e9c48f6cec
								
							
						
					
					
						commit
						20b888bfa0
					
				
							
								
								
									
										169
									
								
								dev-test/tests/luajit_translate.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								dev-test/tests/luajit_translate.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,169 @@ | ||||
| use std::{ | ||||
| 	io::{Result, Write}, | ||||
| 	path::PathBuf, | ||||
| }; | ||||
| 
 | ||||
| use wast::{ | ||||
| 	core::{Expression, Instruction}, | ||||
| 	token::{Float32, Float64}, | ||||
| 	AssertExpression, WastExecute, WastInvoke, | ||||
| }; | ||||
| 
 | ||||
| use target::{Target, TypedModule}; | ||||
| 
 | ||||
| mod target; | ||||
| 
 | ||||
| static ASSERTION: &str = include_str!("assertion.lua"); | ||||
| 
 | ||||
| struct LuaJIT; | ||||
| 
 | ||||
| impl LuaJIT { | ||||
| 	fn write_expression(data: &Expression, w: &mut dyn Write) -> Result<()> { | ||||
| 		let data = &data.instrs; | ||||
| 
 | ||||
| 		assert_eq!(data.len(), 1, "Only one instruction supported"); | ||||
| 
 | ||||
| 		match &data[0] { | ||||
| 			Instruction::I32Const(v) => write!(w, "{v}"), | ||||
| 			Instruction::I64Const(v) => write!(w, "{v}LL"), | ||||
| 			Instruction::F32Const(v) => write!(w, "{}", f32::from_bits(v.bits)), | ||||
| 			Instruction::F64Const(v) => write!(w, "{}", f64::from_bits(v.bits)), | ||||
| 			_ => panic!("Unsupported instruction"), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	write_assert_number!(write_assert_maybe_f32, Float32, f32); | ||||
| 	write_assert_number!(write_assert_maybe_f64, Float64, f64); | ||||
| 
 | ||||
| 	fn write_simple_expression(data: &AssertExpression, w: &mut dyn Write) -> Result<()> { | ||||
| 		match data { | ||||
| 			AssertExpression::I32(v) => write!(w, "{v}"), | ||||
| 			AssertExpression::I64(v) => write!(w, "{v}LL"), | ||||
| 			AssertExpression::F32(v) => Self::write_assert_maybe_f32(v, w), | ||||
| 			AssertExpression::F64(v) => Self::write_assert_maybe_f64(v, w), | ||||
| 			_ => panic!("Unsupported expression"), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_call_of(handler: &str, data: &WastInvoke, w: &mut dyn Write) -> Result<()> { | ||||
| 		let name = TypedModule::resolve_id(data.module); | ||||
| 		let func = data.name; | ||||
| 
 | ||||
| 		write!(w, "{handler}(")?; | ||||
| 		write!(w, r#"loaded["{name}"].func_list["{func}"]"#)?; | ||||
| 
 | ||||
| 		data.args.iter().try_for_each(|v| { | ||||
| 			write!(w, ", ")?; | ||||
| 			Self::write_expression(v, w) | ||||
| 		})?; | ||||
| 
 | ||||
| 		write!(w, ")") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl Target for LuaJIT { | ||||
| 	fn executable() -> String { | ||||
| 		std::env::var("LUAJIT_PATH").unwrap_or_else(|_| "luajit".to_string()) | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_register(post: &str, pre: &str, w: &mut dyn Write) -> Result<()> { | ||||
| 		writeln!(w, r#"linked["{post}"] = loaded["{pre}"]"#) | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_invoke(data: &WastInvoke, w: &mut dyn Write) -> Result<()> { | ||||
| 		Self::write_call_of("raw_invoke", data, w)?; | ||||
| 		writeln!(w) | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_assert_trap(data: &WastExecute, w: &mut dyn Write) -> Result<()> { | ||||
| 		match data { | ||||
| 			WastExecute::Invoke(data) => { | ||||
| 				Self::write_call_of("assert_trap", data, w)?; | ||||
| 				writeln!(w) | ||||
| 			} | ||||
| 			WastExecute::Get { module, global } => { | ||||
| 				let name = TypedModule::resolve_id(*module); | ||||
| 
 | ||||
| 				write!(w, "assert_neq(")?; | ||||
| 				write!(w, r#"loaded["{name}"].global_list["{global}"].value"#)?; | ||||
| 				writeln!(w, ", nil)") | ||||
| 			} | ||||
| 			WastExecute::Wat(_) => panic!("Wat not supported"), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_assert_return( | ||||
| 		data: &WastExecute, | ||||
| 		result: &[AssertExpression], | ||||
| 		w: &mut dyn Write, | ||||
| 	) -> Result<()> { | ||||
| 		match data { | ||||
| 			WastExecute::Invoke(data) => { | ||||
| 				write!(w, "assert_return(")?; | ||||
| 				write!(w, "{{")?; | ||||
| 				Self::write_call_of("raw_invoke", data, w)?; | ||||
| 				write!(w, "}}, {{")?; | ||||
| 
 | ||||
| 				for v in result { | ||||
| 					Self::write_simple_expression(v, w)?; | ||||
| 					write!(w, ", ")?; | ||||
| 				} | ||||
| 
 | ||||
| 				writeln!(w, "}})") | ||||
| 			} | ||||
| 			WastExecute::Get { module, global } => { | ||||
| 				let name = TypedModule::resolve_id(*module); | ||||
| 
 | ||||
| 				write!(w, "assert_eq(")?; | ||||
| 				write!(w, r#"loaded["{name}"].global_list["{global}"].value"#)?; | ||||
| 				write!(w, ", ")?; | ||||
| 				Self::write_simple_expression(&result[0], w)?; | ||||
| 				writeln!(w, ")") | ||||
| 			} | ||||
| 			WastExecute::Wat(_) => panic!("Wat not supported"), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_assert_exhaustion(data: &WastInvoke, w: &mut dyn Write) -> Result<()> { | ||||
| 		Self::write_call_of("assert_exhaustion", data, w)?; | ||||
| 		writeln!(w) | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_runtime(w: &mut dyn Write) -> Result<()> { | ||||
| 		let runtime = codegen_luajit::RUNTIME; | ||||
| 
 | ||||
| 		writeln!(w, "{ASSERTION}")?; | ||||
| 		writeln!(w, "local rt = (function() {runtime} end)()") | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_module(typed: &TypedModule, w: &mut dyn Write) -> Result<()> { | ||||
| 		write!(w, r#"loaded["{}"] = (function() "#, typed.name())?; | ||||
| 		codegen_luajit::from_module_typed(typed.module(), typed.type_info(), w)?; | ||||
| 		writeln!(w, "end)()(nil)") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static DO_NOT_RUN: [&str; 8] = [ | ||||
| 	"binary-leb128.wast", | ||||
| 	"conversions.wast", | ||||
| 	"float_exprs.wast", | ||||
| 	"float_literals.wast", | ||||
| 	"float_memory.wast", | ||||
| 	"float_misc.wast", | ||||
| 	"names.wast", | ||||
| 	"skip-stack-guard-page.wast", | ||||
| ]; | ||||
| 
 | ||||
| #[test_generator::test_resources("dev-test/spec/*.wast")] | ||||
| fn translate_file(path: PathBuf) { | ||||
| 	let path = path.strip_prefix("dev-test/").unwrap(); | ||||
| 	let name = path.file_name().unwrap().to_str().unwrap(); | ||||
| 
 | ||||
| 	if DO_NOT_RUN.contains(&name) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	let source = std::fs::read_to_string(path).unwrap(); | ||||
| 
 | ||||
| 	LuaJIT::test(name, &source).unwrap(); | ||||
| } | ||||
							
								
								
									
										176
									
								
								dev-test/tests/luau_translate.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								dev-test/tests/luau_translate.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,176 @@ | ||||
| use std::{ | ||||
| 	io::{Result, Write}, | ||||
| 	path::PathBuf, | ||||
| }; | ||||
| 
 | ||||
| use wast::{ | ||||
| 	core::{Expression, Instruction}, | ||||
| 	token::{Float32, Float64}, | ||||
| 	AssertExpression, WastExecute, WastInvoke, | ||||
| }; | ||||
| 
 | ||||
| use target::{Target, TypedModule}; | ||||
| 
 | ||||
| mod target; | ||||
| 
 | ||||
| static ASSERTION: &str = include_str!("assertion.lua"); | ||||
| 
 | ||||
| struct Luau; | ||||
| 
 | ||||
| impl Luau { | ||||
| 	fn write_i64(data: i64, w: &mut dyn Write) -> Result<()> { | ||||
| 		let data_1 = (data & 0xFFFFFFFF) as u32; | ||||
| 		let data_2 = (data >> 32 & 0xFFFFFFFF) as u32; | ||||
| 
 | ||||
| 		write!(w, "rt.num.from_u32({data_1}, {data_2})") | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_expression(data: &Expression, w: &mut dyn Write) -> Result<()> { | ||||
| 		let data = &data.instrs; | ||||
| 
 | ||||
| 		assert_eq!(data.len(), 1, "Only one instruction supported"); | ||||
| 
 | ||||
| 		match &data[0] { | ||||
| 			Instruction::I32Const(v) => write!(w, "{v}"), | ||||
| 			Instruction::I64Const(v) => Self::write_i64(*v, w), | ||||
| 			Instruction::F32Const(v) => write!(w, "{}", f32::from_bits(v.bits)), | ||||
| 			Instruction::F64Const(v) => write!(w, "{}", f64::from_bits(v.bits)), | ||||
| 			_ => panic!("Unsupported instruction"), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	write_assert_number!(write_assert_maybe_f32, Float32, f32); | ||||
| 	write_assert_number!(write_assert_maybe_f64, Float64, f64); | ||||
| 
 | ||||
| 	fn write_simple_expression(data: &AssertExpression, w: &mut dyn Write) -> Result<()> { | ||||
| 		match data { | ||||
| 			AssertExpression::I32(v) => write!(w, "{v}"), | ||||
| 			AssertExpression::I64(v) => Self::write_i64(*v, w), | ||||
| 			AssertExpression::F32(v) => Self::write_assert_maybe_f32(v, w), | ||||
| 			AssertExpression::F64(v) => Self::write_assert_maybe_f64(v, w), | ||||
| 			_ => panic!("Unsupported expression"), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_call_of(handler: &str, data: &WastInvoke, w: &mut dyn Write) -> Result<()> { | ||||
| 		let name = TypedModule::resolve_id(data.module); | ||||
| 		let func = data.name; | ||||
| 
 | ||||
| 		write!(w, "{handler}(")?; | ||||
| 		write!(w, r#"loaded["{name}"].func_list["{func}"]"#)?; | ||||
| 
 | ||||
| 		data.args.iter().try_for_each(|v| { | ||||
| 			write!(w, ", ")?; | ||||
| 			Self::write_expression(v, w) | ||||
| 		})?; | ||||
| 
 | ||||
| 		write!(w, ")") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl Target for Luau { | ||||
| 	fn executable() -> String { | ||||
| 		std::env::var("LUAU_PATH").unwrap_or_else(|_| "luau".to_string()) | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_register(post: &str, pre: &str, w: &mut dyn Write) -> Result<()> { | ||||
| 		writeln!(w, r#"linked["{post}"] = loaded["{pre}"]"#) | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_invoke(data: &WastInvoke, w: &mut dyn Write) -> Result<()> { | ||||
| 		Self::write_call_of("raw_invoke", data, w)?; | ||||
| 		writeln!(w) | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_assert_trap(data: &WastExecute, w: &mut dyn Write) -> Result<()> { | ||||
| 		match data { | ||||
| 			WastExecute::Invoke(data) => { | ||||
| 				Self::write_call_of("assert_trap", data, w)?; | ||||
| 				writeln!(w) | ||||
| 			} | ||||
| 			WastExecute::Get { module, global } => { | ||||
| 				let name = TypedModule::resolve_id(*module); | ||||
| 
 | ||||
| 				write!(w, "assert_neq(")?; | ||||
| 				write!(w, r#"loaded["{name}"].global_list["{global}"].value"#)?; | ||||
| 				writeln!(w, ", nil)") | ||||
| 			} | ||||
| 			WastExecute::Wat(_) => panic!("Wat not supported"), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_assert_return( | ||||
| 		data: &WastExecute, | ||||
| 		result: &[AssertExpression], | ||||
| 		w: &mut dyn Write, | ||||
| 	) -> Result<()> { | ||||
| 		match data { | ||||
| 			WastExecute::Invoke(data) => { | ||||
| 				write!(w, "assert_return(")?; | ||||
| 				write!(w, "{{")?; | ||||
| 				Self::write_call_of("raw_invoke", data, w)?; | ||||
| 				write!(w, "}}, {{")?; | ||||
| 
 | ||||
| 				for v in result { | ||||
| 					Self::write_simple_expression(v, w)?; | ||||
| 					write!(w, ", ")?; | ||||
| 				} | ||||
| 
 | ||||
| 				writeln!(w, "}})") | ||||
| 			} | ||||
| 			WastExecute::Get { module, global } => { | ||||
| 				let name = TypedModule::resolve_id(*module); | ||||
| 
 | ||||
| 				write!(w, "assert_eq(")?; | ||||
| 				write!(w, r#"loaded["{name}"].global_list["{global}"].value"#)?; | ||||
| 				write!(w, ", ")?; | ||||
| 				Self::write_simple_expression(&result[0], w)?; | ||||
| 				writeln!(w, ")") | ||||
| 			} | ||||
| 			WastExecute::Wat(_) => panic!("Wat not supported"), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_assert_exhaustion(data: &WastInvoke, w: &mut dyn Write) -> Result<()> { | ||||
| 		Self::write_call_of("assert_exhaustion", data, w)?; | ||||
| 		writeln!(w) | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_runtime(w: &mut dyn Write) -> Result<()> { | ||||
| 		let runtime = codegen_luau::RUNTIME; | ||||
| 
 | ||||
| 		writeln!(w, "{ASSERTION}")?; | ||||
| 		writeln!(w, "local rt = (function() {runtime} end)()") | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_module(typed: &TypedModule, w: &mut dyn Write) -> Result<()> { | ||||
| 		write!(w, r#"loaded["{}"] = (function() "#, typed.name())?; | ||||
| 		codegen_luau::from_module_typed(typed.module(), typed.type_info(), w)?; | ||||
| 		writeln!(w, "end)()(nil)") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static DO_NOT_RUN: [&str; 8] = [ | ||||
| 	"binary-leb128.wast", | ||||
| 	"conversions.wast", | ||||
| 	"float_exprs.wast", | ||||
| 	"float_literals.wast", | ||||
| 	"float_memory.wast", | ||||
| 	"float_misc.wast", | ||||
| 	"names.wast", | ||||
| 	"skip-stack-guard-page.wast", | ||||
| ]; | ||||
| 
 | ||||
| #[test_generator::test_resources("dev-test/spec/*.wast")] | ||||
| fn translate_file(path: PathBuf) { | ||||
| 	let path = path.strip_prefix("dev-test/").unwrap(); | ||||
| 	let name = path.file_name().unwrap().to_str().unwrap(); | ||||
| 
 | ||||
| 	if DO_NOT_RUN.contains(&name) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	let source = std::fs::read_to_string(path).unwrap(); | ||||
| 
 | ||||
| 	Luau::test(name, &source).unwrap(); | ||||
| } | ||||
| @ -1,389 +0,0 @@ | ||||
| #![cfg(test)] | ||||
| 
 | ||||
| use std::{ | ||||
| 	io::{Result as IResult, Write}, | ||||
| 	marker::PhantomData, | ||||
| 	path::{Path, PathBuf}, | ||||
| 	process::Command, | ||||
| }; | ||||
| 
 | ||||
| use parity_wasm::elements::Module as BinModule; | ||||
| use wast::{ | ||||
| 	core::{Expression, Instruction, Module as AstModule}, | ||||
| 	parser::ParseBuffer, | ||||
| 	token::{Float32, Float64, Id}, | ||||
| 	AssertExpression, NanPattern, QuoteWat, Wast, WastDirective, WastExecute, WastInvoke, Wat, | ||||
| }; | ||||
| 
 | ||||
| use wasm_ast::builder::TypeInfo; | ||||
| 
 | ||||
| static ASSERTION: &str = include_str!("assertion.lua"); | ||||
| 
 | ||||
| macro_rules! write_assert_number { | ||||
| 	($name:ident, $generic:ty, $reader:ty) => { | ||||
| 		fn $name(data: &NanPattern<$generic>, w: &mut dyn Write) -> IResult<()> { | ||||
| 			match data { | ||||
| 				NanPattern::CanonicalNan => write!(w, "LUA_NAN_CANONICAL"), | ||||
| 				NanPattern::ArithmeticNan => write!(w, "LUA_NAN_ARITHMETIC"), | ||||
| 				NanPattern::Value(data) => { | ||||
| 					let number = <$reader>::from_bits(data.bits); | ||||
| 					let sign = if number.is_sign_negative() { "-" } else { "" }; | ||||
| 
 | ||||
| 					if number.is_infinite() { | ||||
| 						write!(w, "{sign}LUA_INFINITY") | ||||
| 					} else if number.is_nan() { | ||||
| 						write!(w, "{sign}LUA_NAN_DEFAULT") | ||||
| 					} else { | ||||
| 						write!(w, "{number}") | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	}; | ||||
| } | ||||
| 
 | ||||
| struct TypedModule<'a> { | ||||
| 	name: &'a str, | ||||
| 	module: &'a BinModule, | ||||
| 	type_info: TypeInfo<'a>, | ||||
| } | ||||
| 
 | ||||
| impl<'a> TypedModule<'a> { | ||||
| 	fn resolve_id(id: Option<Id>) -> &str { | ||||
| 		id.map_or("temp", |v| v.name()) | ||||
| 	} | ||||
| 
 | ||||
| 	fn from_id(id: Option<Id<'a>>, module: &'a BinModule) -> Self { | ||||
| 		Self { | ||||
| 			module, | ||||
| 			name: Self::resolve_id(id), | ||||
| 			type_info: TypeInfo::from_module(module), | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| trait Target: Sized { | ||||
| 	fn executable() -> String; | ||||
| 
 | ||||
| 	fn write_register(post: &str, pre: &str, w: &mut dyn Write) -> IResult<()>; | ||||
| 
 | ||||
| 	fn write_invoke(data: &WastInvoke, w: &mut dyn Write) -> IResult<()>; | ||||
| 
 | ||||
| 	fn write_assert_trap(data: &WastExecute, w: &mut dyn Write) -> IResult<()>; | ||||
| 
 | ||||
| 	fn write_assert_return( | ||||
| 		data: &WastExecute, | ||||
| 		result: &[AssertExpression], | ||||
| 		w: &mut dyn Write, | ||||
| 	) -> IResult<()>; | ||||
| 
 | ||||
| 	fn write_assert_exhaustion(data: &WastInvoke, w: &mut dyn Write) -> IResult<()>; | ||||
| 
 | ||||
| 	fn write_runtime(w: &mut dyn Write) -> IResult<()>; | ||||
| 
 | ||||
| 	fn write_module(typed: &TypedModule, w: &mut dyn Write) -> IResult<()>; | ||||
| } | ||||
| 
 | ||||
| struct LuaJIT; | ||||
| 
 | ||||
| impl LuaJIT { | ||||
| 	fn write_expression(data: &Expression, w: &mut dyn Write) -> IResult<()> { | ||||
| 		let data = &data.instrs; | ||||
| 
 | ||||
| 		assert_eq!(data.len(), 1, "Only one instruction supported"); | ||||
| 
 | ||||
| 		match &data[0] { | ||||
| 			Instruction::I32Const(v) => write!(w, "{v}"), | ||||
| 			Instruction::I64Const(v) => write!(w, "{v}LL"), | ||||
| 			Instruction::F32Const(v) => write!(w, "{}", f32::from_bits(v.bits)), | ||||
| 			Instruction::F64Const(v) => write!(w, "{}", f64::from_bits(v.bits)), | ||||
| 			_ => panic!("Unsupported instruction"), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	write_assert_number!(write_assert_maybe_f32, Float32, f32); | ||||
| 	write_assert_number!(write_assert_maybe_f64, Float64, f64); | ||||
| 
 | ||||
| 	fn write_simple_expression(data: &AssertExpression, w: &mut dyn Write) -> IResult<()> { | ||||
| 		match data { | ||||
| 			AssertExpression::I32(v) => write!(w, "{v}"), | ||||
| 			AssertExpression::I64(v) => write!(w, "{v}LL"), | ||||
| 			AssertExpression::F32(v) => Self::write_assert_maybe_f32(v, w), | ||||
| 			AssertExpression::F64(v) => Self::write_assert_maybe_f64(v, w), | ||||
| 			_ => panic!("Unsupported expression"), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_call_of(handler: &str, data: &WastInvoke, w: &mut dyn Write) -> IResult<()> { | ||||
| 		let name = TypedModule::resolve_id(data.module); | ||||
| 		let func = data.name; | ||||
| 
 | ||||
| 		write!(w, "{handler}(")?; | ||||
| 		write!(w, r#"loaded["{name}"].func_list["{func}"]"#)?; | ||||
| 
 | ||||
| 		data.args.iter().try_for_each(|v| { | ||||
| 			write!(w, ", ")?; | ||||
| 			Self::write_expression(v, w) | ||||
| 		})?; | ||||
| 
 | ||||
| 		write!(w, ")") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| impl Target for LuaJIT { | ||||
| 	fn executable() -> String { | ||||
| 		std::env::var("LUAJIT_PATH").unwrap_or_else(|_| "luajit".to_string()) | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_register(post: &str, pre: &str, w: &mut dyn Write) -> IResult<()> { | ||||
| 		writeln!(w, r#"linked["{post}"] = loaded["{pre}"]"#) | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_invoke(data: &WastInvoke, w: &mut dyn Write) -> IResult<()> { | ||||
| 		Self::write_call_of("raw_invoke", data, w)?; | ||||
| 		writeln!(w) | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_assert_trap(data: &WastExecute, w: &mut dyn Write) -> IResult<()> { | ||||
| 		match data { | ||||
| 			WastExecute::Invoke(data) => { | ||||
| 				Self::write_call_of("assert_trap", data, w)?; | ||||
| 				writeln!(w) | ||||
| 			} | ||||
| 			WastExecute::Get { module, global } => { | ||||
| 				let name = TypedModule::resolve_id(*module); | ||||
| 
 | ||||
| 				write!(w, "assert_neq(")?; | ||||
| 				write!(w, r#"loaded["{name}"].global_list["{global}"].value"#)?; | ||||
| 				writeln!(w, ", nil)") | ||||
| 			} | ||||
| 			WastExecute::Wat(_) => panic!("Wat not supported"), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_assert_return( | ||||
| 		data: &WastExecute, | ||||
| 		result: &[AssertExpression], | ||||
| 		w: &mut dyn Write, | ||||
| 	) -> IResult<()> { | ||||
| 		match data { | ||||
| 			WastExecute::Invoke(data) => { | ||||
| 				write!(w, "assert_return(")?; | ||||
| 				write!(w, "{{")?; | ||||
| 				Self::write_call_of("raw_invoke", data, w)?; | ||||
| 				write!(w, "}}, {{")?; | ||||
| 
 | ||||
| 				for v in result { | ||||
| 					Self::write_simple_expression(v, w)?; | ||||
| 					write!(w, ", ")?; | ||||
| 				} | ||||
| 
 | ||||
| 				writeln!(w, "}})") | ||||
| 			} | ||||
| 			WastExecute::Get { module, global } => { | ||||
| 				let name = TypedModule::resolve_id(*module); | ||||
| 
 | ||||
| 				write!(w, "assert_eq(")?; | ||||
| 				write!(w, r#"loaded["{name}"].global_list["{global}"].value"#)?; | ||||
| 				write!(w, ", ")?; | ||||
| 				Self::write_simple_expression(&result[0], w)?; | ||||
| 				writeln!(w, ")") | ||||
| 			} | ||||
| 			WastExecute::Wat(_) => panic!("Wat not supported"), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_assert_exhaustion(data: &WastInvoke, w: &mut dyn Write) -> IResult<()> { | ||||
| 		Self::write_call_of("assert_exhaustion", data, w)?; | ||||
| 		writeln!(w) | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_runtime(w: &mut dyn Write) -> IResult<()> { | ||||
| 		let runtime = codegen_luajit::RUNTIME; | ||||
| 
 | ||||
| 		writeln!(w, "{ASSERTION}")?; | ||||
| 		writeln!(w, "local rt = (function() {runtime} end)()") | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_module(typed: &TypedModule, w: &mut dyn Write) -> IResult<()> { | ||||
| 		write!(w, r#"loaded["{}"] = (function() "#, typed.name)?; | ||||
| 		codegen_luajit::from_module_typed(typed.module, &typed.type_info, w)?; | ||||
| 		writeln!(w, "end)()(nil)") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| struct Luau; | ||||
| 
 | ||||
| impl Target for Luau { | ||||
| 	fn executable() -> String { | ||||
| 		std::env::var("LUAU_PATH").unwrap_or_else(|_| "luajit".to_string()) | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_register(post: &str, pre: &str, w: &mut dyn Write) -> IResult<()> { | ||||
| 		writeln!(w, r#"linked["{post}"] = loaded["{pre}"]"#) | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_invoke(data: &WastInvoke, w: &mut dyn Write) -> IResult<()> { | ||||
| 		todo!(); | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_assert_trap(data: &WastExecute, w: &mut dyn Write) -> IResult<()> { | ||||
| 		todo!(); | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_assert_return( | ||||
| 		data: &WastExecute, | ||||
| 		result: &[AssertExpression], | ||||
| 		w: &mut dyn Write, | ||||
| 	) -> IResult<()> { | ||||
| 		todo!(); | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_assert_exhaustion(data: &WastInvoke, w: &mut dyn Write) -> IResult<()> { | ||||
| 		todo!(); | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_runtime(w: &mut dyn Write) -> IResult<()> { | ||||
| 		let runtime = codegen_luau::RUNTIME; | ||||
| 
 | ||||
| 		writeln!(w, "{ASSERTION}")?; | ||||
| 		writeln!(w, "local rt = (function() {runtime} end)()") | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_module(typed: &TypedModule, w: &mut dyn Write) -> IResult<()> { | ||||
| 		write!(w, r#"loaded["{}"] = (function() "#, typed.name)?; | ||||
| 		codegen_luau::from_module_typed(typed.module, &typed.type_info, w)?; | ||||
| 		writeln!(w, "end)()(nil)") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn try_into_ast_module(data: QuoteWat) -> Option<AstModule> { | ||||
| 	if let QuoteWat::Wat(Wat::Module(data)) = data { | ||||
| 		Some(data) | ||||
| 	} else { | ||||
| 		None | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Only proceed with tests that observe any state.
 | ||||
| fn parse_and_validate<'a>(buffer: &'a ParseBuffer) -> Option<Wast<'a>> { | ||||
| 	let parsed: Wast = wast::parser::parse(buffer).unwrap(); | ||||
| 	let observer = parsed.directives.iter().any(|v| { | ||||
| 		matches!( | ||||
| 			v, | ||||
| 			WastDirective::AssertTrap { .. } | ||||
| 				| WastDirective::AssertReturn { .. } | ||||
| 				| WastDirective::AssertExhaustion { .. } | ||||
| 		) | ||||
| 	}); | ||||
| 
 | ||||
| 	observer.then(|| parsed) | ||||
| } | ||||
| 
 | ||||
| struct Tester<T> { | ||||
| 	_marker: PhantomData<T>, | ||||
| } | ||||
| 
 | ||||
| impl<T: Target> Tester<T> { | ||||
| 	fn test(name: &str, source: &str) -> IResult<()> { | ||||
| 		if let Some(data) = Self::run_generation(source)? { | ||||
| 			let temp = PathBuf::from(env!("CARGO_TARGET_TMPDIR")) | ||||
| 				.join(name) | ||||
| 				.with_extension("wast.lua"); | ||||
| 
 | ||||
| 			std::fs::write(&temp, &data)?; | ||||
| 			Self::run_command(&temp)?; | ||||
| 		} | ||||
| 
 | ||||
| 		Ok(()) | ||||
| 	} | ||||
| 
 | ||||
| 	fn write_variant(variant: WastDirective, w: &mut dyn Write) -> IResult<()> { | ||||
| 		match variant { | ||||
| 			WastDirective::Wat(data) => { | ||||
| 				let mut ast = try_into_ast_module(data).expect("Must be a module"); | ||||
| 				let bytes = ast.encode().unwrap(); | ||||
| 				let temp = parity_wasm::deserialize_buffer(&bytes).unwrap(); | ||||
| 				let typed = TypedModule::from_id(ast.id, &temp); | ||||
| 
 | ||||
| 				T::write_module(&typed, w)?; | ||||
| 			} | ||||
| 			WastDirective::Register { name, module, .. } => { | ||||
| 				let pre = TypedModule::resolve_id(module); | ||||
| 
 | ||||
| 				T::write_register(name, pre, w)?; | ||||
| 			} | ||||
| 			WastDirective::Invoke(data) => { | ||||
| 				T::write_invoke(&data, w)?; | ||||
| 			} | ||||
| 			WastDirective::AssertTrap { exec, .. } => { | ||||
| 				T::write_assert_trap(&exec, w)?; | ||||
| 			} | ||||
| 			WastDirective::AssertReturn { exec, results, .. } => { | ||||
| 				T::write_assert_return(&exec, &results, w)?; | ||||
| 			} | ||||
| 			WastDirective::AssertExhaustion { call, .. } => { | ||||
| 				T::write_assert_exhaustion(&call, w)?; | ||||
| 			} | ||||
| 			_ => {} | ||||
| 		} | ||||
| 
 | ||||
| 		Ok(()) | ||||
| 	} | ||||
| 
 | ||||
| 	fn run_command(file: &Path) -> IResult<()> { | ||||
| 		let result = Command::new(T::executable()).arg(file).output()?; | ||||
| 
 | ||||
| 		if result.status.success() { | ||||
| 			Ok(()) | ||||
| 		} else { | ||||
| 			let data = String::from_utf8_lossy(&result.stderr); | ||||
| 
 | ||||
| 			panic!("{}", data); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fn run_generation(source: &str) -> IResult<Option<Vec<u8>>> { | ||||
| 		let lexed = ParseBuffer::new(source).expect("Failed to tokenize"); | ||||
| 		let parsed = match parse_and_validate(&lexed) { | ||||
| 			Some(v) => v, | ||||
| 			None => return Ok(None), | ||||
| 		}; | ||||
| 
 | ||||
| 		let mut data = Vec::new(); | ||||
| 
 | ||||
| 		T::write_runtime(&mut data)?; | ||||
| 
 | ||||
| 		for variant in parsed.directives { | ||||
| 			Self::write_variant(variant, &mut data)?; | ||||
| 		} | ||||
| 
 | ||||
| 		Ok(Some(data)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static DO_NOT_RUN: [&str; 8] = [ | ||||
| 	"binary-leb128.wast", | ||||
| 	"conversions.wast", | ||||
| 	"float_exprs.wast", | ||||
| 	"float_literals.wast", | ||||
| 	"float_memory.wast", | ||||
| 	"float_misc.wast", | ||||
| 	"names.wast", | ||||
| 	"skip-stack-guard-page.wast", | ||||
| ]; | ||||
| 
 | ||||
| #[test_generator::test_resources("dev-test/spec/*.wast")] | ||||
| fn translate_file(path: PathBuf) { | ||||
| 	let path = path.strip_prefix("dev-test/").unwrap(); | ||||
| 	let name = path.file_name().unwrap().to_str().unwrap(); | ||||
| 
 | ||||
| 	if DO_NOT_RUN.contains(&name) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	let source = std::fs::read_to_string(path).unwrap(); | ||||
| 
 | ||||
| 	Tester::<LuaJIT>::test(name, &source).unwrap(); | ||||
| 	// Tester::<Luau>::test(name, &source).unwrap();
 | ||||
| } | ||||
							
								
								
									
										192
									
								
								dev-test/tests/target.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								dev-test/tests/target.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,192 @@ | ||||
| use std::{ | ||||
| 	io::{Result, Write}, | ||||
| 	path::{Path, PathBuf}, | ||||
| 	process::Command, | ||||
| }; | ||||
| 
 | ||||
| use parity_wasm::elements::Module as BinModule; | ||||
| use wast::{ | ||||
| 	core::Module as AstModule, parser::ParseBuffer, token::Id, AssertExpression, QuoteWat, Wast, | ||||
| 	WastDirective, WastExecute, WastInvoke, Wat, | ||||
| }; | ||||
| 
 | ||||
| use wasm_ast::builder::TypeInfo; | ||||
| 
 | ||||
| #[macro_export] | ||||
| macro_rules! write_assert_number { | ||||
| 	($name:ident, $generic:ty, $reader:ty) => { | ||||
| 		fn $name(data: &wast::NanPattern<$generic>, w: &mut dyn Write) -> Result<()> { | ||||
| 			use wast::NanPattern; | ||||
| 
 | ||||
| 			match data { | ||||
| 				NanPattern::CanonicalNan => write!(w, "LUA_NAN_CANONICAL"), | ||||
| 				NanPattern::ArithmeticNan => write!(w, "LUA_NAN_ARITHMETIC"), | ||||
| 				NanPattern::Value(data) => { | ||||
| 					let number = <$reader>::from_bits(data.bits); | ||||
| 					let sign = if number.is_sign_negative() { "-" } else { "" }; | ||||
| 
 | ||||
| 					if number.is_infinite() { | ||||
| 						write!(w, "{sign}LUA_INFINITY") | ||||
| 					} else if number.is_nan() { | ||||
| 						write!(w, "{sign}LUA_NAN_DEFAULT") | ||||
| 					} else { | ||||
| 						write!(w, "{number}") | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	}; | ||||
| } | ||||
| 
 | ||||
| pub struct TypedModule<'a> { | ||||
| 	name: &'a str, | ||||
| 	module: &'a BinModule, | ||||
| 	type_info: TypeInfo<'a>, | ||||
| } | ||||
| 
 | ||||
| impl<'a> TypedModule<'a> { | ||||
| 	pub fn resolve_id(id: Option<Id>) -> &str { | ||||
| 		id.map_or("temp", |v| v.name()) | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn name(&self) -> &str { | ||||
| 		self.name | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn module(&self) -> &BinModule { | ||||
| 		self.module | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn type_info(&self) -> &TypeInfo<'a> { | ||||
| 		&self.type_info | ||||
| 	} | ||||
| 
 | ||||
| 	fn from_id(id: Option<Id<'a>>, module: &'a BinModule) -> Self { | ||||
| 		Self { | ||||
| 			module, | ||||
| 			name: Self::resolve_id(id), | ||||
| 			type_info: TypeInfo::from_module(module), | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| fn try_into_ast_module(data: QuoteWat) -> Option<AstModule> { | ||||
| 	if let QuoteWat::Wat(Wat::Module(data)) = data { | ||||
| 		Some(data) | ||||
| 	} else { | ||||
| 		None | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Only proceed with tests that observe any state.
 | ||||
| fn parse_and_validate<'a>(buffer: &'a ParseBuffer) -> Option<Wast<'a>> { | ||||
| 	let parsed: Wast = wast::parser::parse(buffer).unwrap(); | ||||
| 	let observer = parsed.directives.iter().any(|v| { | ||||
| 		matches!( | ||||
| 			v, | ||||
| 			WastDirective::AssertTrap { .. } | ||||
| 				| WastDirective::AssertReturn { .. } | ||||
| 				| WastDirective::AssertExhaustion { .. } | ||||
| 		) | ||||
| 	}); | ||||
| 
 | ||||
| 	observer.then(|| parsed) | ||||
| } | ||||
| 
 | ||||
| pub trait Target: Sized { | ||||
| 	fn executable() -> String; | ||||
| 
 | ||||
| 	fn write_register(post: &str, pre: &str, w: &mut dyn Write) -> Result<()>; | ||||
| 
 | ||||
| 	fn write_invoke(data: &WastInvoke, w: &mut dyn Write) -> Result<()>; | ||||
| 
 | ||||
| 	fn write_assert_trap(data: &WastExecute, w: &mut dyn Write) -> Result<()>; | ||||
| 
 | ||||
| 	fn write_assert_return( | ||||
| 		data: &WastExecute, | ||||
| 		result: &[AssertExpression], | ||||
| 		w: &mut dyn Write, | ||||
| 	) -> Result<()>; | ||||
| 
 | ||||
| 	fn write_assert_exhaustion(data: &WastInvoke, w: &mut dyn Write) -> Result<()>; | ||||
| 
 | ||||
| 	fn write_runtime(w: &mut dyn Write) -> Result<()>; | ||||
| 
 | ||||
| 	fn write_module(typed: &TypedModule, w: &mut dyn Write) -> Result<()>; | ||||
| 
 | ||||
| 	fn write_variant(variant: WastDirective, w: &mut dyn Write) -> Result<()> { | ||||
| 		match variant { | ||||
| 			WastDirective::Wat(data) => { | ||||
| 				let mut ast = try_into_ast_module(data).expect("Must be a module"); | ||||
| 				let bytes = ast.encode().unwrap(); | ||||
| 				let temp = parity_wasm::deserialize_buffer(&bytes).unwrap(); | ||||
| 				let typed = TypedModule::from_id(ast.id, &temp); | ||||
| 
 | ||||
| 				Self::write_module(&typed, w)?; | ||||
| 			} | ||||
| 			WastDirective::Register { name, module, .. } => { | ||||
| 				let pre = TypedModule::resolve_id(module); | ||||
| 
 | ||||
| 				Self::write_register(name, pre, w)?; | ||||
| 			} | ||||
| 			WastDirective::Invoke(data) => { | ||||
| 				Self::write_invoke(&data, w)?; | ||||
| 			} | ||||
| 			WastDirective::AssertTrap { exec, .. } => { | ||||
| 				Self::write_assert_trap(&exec, w)?; | ||||
| 			} | ||||
| 			WastDirective::AssertReturn { exec, results, .. } => { | ||||
| 				Self::write_assert_return(&exec, &results, w)?; | ||||
| 			} | ||||
| 			WastDirective::AssertExhaustion { call, .. } => { | ||||
| 				Self::write_assert_exhaustion(&call, w)?; | ||||
| 			} | ||||
| 			_ => {} | ||||
| 		} | ||||
| 
 | ||||
| 		Ok(()) | ||||
| 	} | ||||
| 
 | ||||
| 	fn run_command(file: &Path) -> Result<()> { | ||||
| 		let result = Command::new(Self::executable()).arg(file).output()?; | ||||
| 
 | ||||
| 		if result.status.success() { | ||||
| 			Ok(()) | ||||
| 		} else { | ||||
| 			let data = String::from_utf8_lossy(&result.stderr); | ||||
| 
 | ||||
| 			panic!("{}", data); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	fn run_generation(source: &str) -> Result<Option<Vec<u8>>> { | ||||
| 		let lexed = ParseBuffer::new(source).expect("Failed to tokenize"); | ||||
| 		let parsed = match parse_and_validate(&lexed) { | ||||
| 			Some(v) => v, | ||||
| 			None => return Ok(None), | ||||
| 		}; | ||||
| 
 | ||||
| 		let mut data = Vec::new(); | ||||
| 
 | ||||
| 		Self::write_runtime(&mut data)?; | ||||
| 
 | ||||
| 		for variant in parsed.directives { | ||||
| 			Self::write_variant(variant, &mut data)?; | ||||
| 		} | ||||
| 
 | ||||
| 		Ok(Some(data)) | ||||
| 	} | ||||
| 
 | ||||
| 	fn test(name: &str, source: &str) -> Result<()> { | ||||
| 		if let Some(data) = Self::run_generation(source)? { | ||||
| 			let temp = PathBuf::from(env!("CARGO_TARGET_TMPDIR")) | ||||
| 				.join(name) | ||||
| 				.with_extension("wast.lua"); | ||||
| 
 | ||||
| 			std::fs::write(&temp, &data)?; | ||||
| 			Self::run_command(&temp)?; | ||||
| 		} | ||||
| 
 | ||||
| 		Ok(()) | ||||
| 	} | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user