libifps: allow Instruction.Create(OpCode, Operand, Operand, Operand) to be used for InlineCmpValueType too

libasm: allow generic operand to be used for InlineCmpValueType op2, as well as type
tests: refactor and fix
tests: add test for InlineCmpValueType on type and variable
This commit is contained in:
zc 2023-03-31 13:19:31 +01:00
parent 8aea82c678
commit b20d5d88b5
9 changed files with 366 additions and 3106 deletions

View File

@ -693,11 +693,20 @@ namespace IFPSAsmLib
var op1 = ParseOperandValue(next, function, types, globals, functions, aliases, defines); var op1 = ParseOperandValue(next, function, types, globals, functions, aliases, defines);
if (next.Next == null) next.ThrowInvalid(); if (next.Next == null) next.ThrowInvalid();
next = next.Next; next = next.Next;
next.ExpectValidName(); Operand op2 = null;
next.EnsureNoNextChild(); try
if (!types.TryGetValue(next.Value, out var typeOp)) next.ThrowInvalid(string.Format("In function \"{0}\": Referenced unknown type", function.Name)); {
op2 = ParseOperandValue(next, function, types, globals, functions, aliases, defines);
}
catch
{
next.ExpectValidName();
next.EnsureNoNextChild();
if (!types.TryGetValue(next.Value, out var typeOp)) next.ThrowInvalid(string.Format("In function \"{0}\": Referenced unknown type", function.Name));
op2 = Operand.Create(typeOp);
}
if (next.Next != null) next.Next.ThrowInvalid(); if (next.Next != null) next.Next.ThrowInvalid();
return Instruction.Create(opcode, op0, op1, typeOp); return Instruction.Create(opcode, op0, op1, op2);
} }
case OperandType.InlineTypeVariable: case OperandType.InlineTypeVariable:
{ {

File diff suppressed because it is too large Load Diff

View File

@ -61,6 +61,7 @@
<None Include="CompiledCode.bin" /> <None Include="CompiledCode.bin" />
<None Include="CompiledCode_v22.bin" /> <None Include="CompiledCode_v22.bin" />
<None Include="packages.config" /> <None Include="packages.config" />
<None Include="TestIsInsn.bin" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Properties\Resources.resx"> <EmbeddedResource Include="Properties\Resources.resx">
@ -85,6 +86,9 @@
<ItemGroup> <ItemGroup>
<None Include="CompiledCode_v22.txt" /> <None Include="CompiledCode_v22.txt" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="TestIsInsn.txt" />
</ItemGroup>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" /> <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">

View File

@ -135,5 +135,39 @@ namespace IFPSLib.Tests.Properties {
return ResourceManager.GetString("CompiledCodeDisasm_v22", resourceCulture); return ResourceManager.GetString("CompiledCodeDisasm_v22", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] TestIsInsn {
get {
object obj = ResourceManager.GetObject("TestIsInsn", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized string similar to .version 23
///
///.type primitive(Pointer) Pointer
///.type primitive(S32) S32
///.type primitive(U32) U32
///.type primitive(U8) U8
///
///.function(export) U8 INITIALIZEUNINSTALL()
/// pushtype U32 ; StackCount = 1
/// pushtype U32 ; StackCount = 2
/// is Var1, Var2, S32
/// is Var1, Var2, Var1
/// ret
///
///
///.
/// </summary>
internal static string TestIsInsnDisasm {
get {
return ResourceManager.GetString("TestIsInsnDisasm", resourceCulture);
}
}
} }
} }

View File

@ -130,4 +130,10 @@
<data name="CompiledCode_v22" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="CompiledCode_v22" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\CompiledCode_v22.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>..\CompiledCode_v22.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data> </data>
<data name="TestIsInsn" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\TestIsInsn.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="TestIsInsnDisasm" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\TestIsInsn.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
</data>
</root> </root>

View File

@ -10,18 +10,16 @@ namespace IFPSLib.Tests
[TestClass] [TestClass]
public class ScriptTest public class ScriptTest
{ {
private static readonly string origB64 = Convert.ToBase64String(Resources.CompiledCode); private void TestLoadSaveImpl(byte[] compiled)
private static readonly string origB64_22 = Convert.ToBase64String(Resources.CompiledCode_v22);
[TestMethod]
public void TestLoadSave()
{ {
// Convert to base64.
string orig = Convert.ToBase64String(compiled);
// Load the script. // Load the script.
var script = Script.Load(Resources.CompiledCode); var script = Script.Load(compiled);
// Ensure it's not null. // Ensure it's not null.
Assert.IsNotNull(script); Assert.IsNotNull(script);
// For an official script (compiled by inno setup), the entrypoint is the first function. // For an official script (compiled by inno setup), the entrypoint is the first function.
Assert.AreEqual(script.EntryPoint, script.Functions[0]); if (script.EntryPoint != null) Assert.AreEqual(script.EntryPoint, script.Functions[0]);
// Save the script. // Save the script.
var savedBytes = script.Save(); var savedBytes = script.Save();
// Convert to base64 for later. // Convert to base64 for later.
@ -33,50 +31,53 @@ namespace IFPSLib.Tests
// Ensure both saved scripts equal each other. // Ensure both saved scripts equal each other.
Assert.AreEqual(saved, savedTwice); Assert.AreEqual(saved, savedTwice);
// Ensure the saved script equals the original. // Ensure the saved script equals the original.
Assert.AreEqual(saved, origB64); Assert.AreEqual(saved, orig);
// Ensure the disassemblies are equal. // Ensure the disassemblies are equal.
Assert.AreEqual(script.Disassemble(), scriptSaved.Disassemble()); Assert.AreEqual(script.Disassemble(), scriptSaved.Disassemble());
} }
private void TestAsmImpl(string disasm, byte[] compiled)
{
string orig = Convert.ToBase64String(compiled);
var script = Assembler.Assemble(disasm);
var savedB64 = Convert.ToBase64String(script.Save());
Assert.AreEqual(savedB64, orig);
}
[TestMethod]
public void TestLoadSave()
{
TestLoadSaveImpl(Resources.CompiledCode);
}
[TestMethod] [TestMethod]
public void TestAsm() public void TestAsm()
{ {
var script = Assembler.Assemble(Resources.CompiledCodeDisasm); TestAsmImpl(Resources.CompiledCodeDisasm, Resources.CompiledCode);
var savedB64 = Convert.ToBase64String(script.Save());
Assert.AreEqual(savedB64, origB64);
} }
[TestMethod] [TestMethod]
public void TestLoadSaveV22() public void TestLoadSaveV22()
{ {
// Load the script. TestLoadSaveImpl(Resources.CompiledCode_v22);
var script = Script.Load(Resources.CompiledCode_v22);
// Ensure it's not null.
Assert.IsNotNull(script);
// For an official script (compiled by inno setup), the entrypoint is the first function.
Assert.AreEqual(script.EntryPoint, script.Functions[0]);
// Save the script.
var savedBytes = script.Save();
// Convert to base64 for later.
var saved = Convert.ToBase64String(savedBytes);
// Load the saved script.
var scriptSaved = Script.Load(savedBytes);
// Save again.
var savedTwice = Convert.ToBase64String(scriptSaved.Save());
// Ensure both saved scripts equal each other.
Assert.AreEqual(saved, savedTwice);
// Ensure the saved script equals the original.
Assert.AreEqual(saved, origB64_22);
// Ensure the disassemblies are equal.
Assert.AreEqual(script.Disassemble(), scriptSaved.Disassemble());
} }
[TestMethod] [TestMethod]
public void TestAsmV22() public void TestAsmV22()
{ {
var script = Assembler.Assemble(Resources.CompiledCodeDisasm_v22); TestAsmImpl(Resources.CompiledCodeDisasm_v22, Resources.CompiledCode_v22);
var savedB64 = Convert.ToBase64String(script.Save()); }
Assert.AreEqual(savedB64, origB64);
[TestMethod]
public void TestLoadSaveIs()
{
TestLoadSaveImpl(Resources.TestIsInsn);
}
[TestMethod]
public void TestAsmIs()
{
TestAsmImpl(Resources.TestIsInsnDisasm, Resources.TestIsInsn);
} }
} }
} }

Binary file not shown.

View File

@ -0,0 +1,15 @@
.version 23
.type primitive(Pointer) Pointer
.type primitive(S32) S32
.type primitive(U32) U32
.type primitive(U8) U8
.function(export) U8 INITIALIZEUNINSTALL()
pushtype U32 ; StackCount = 1
pushtype U32 ; StackCount = 2
is Var1, Var2, S32
is Var1, Var2, Var1
ret

View File

@ -250,7 +250,8 @@ namespace IFPSLib.Emit
/// <returns>New instruction</returns> /// <returns>New instruction</returns>
public static Instruction Create(OpCode opcode, Operand op0, Operand op1, Operand op2) public static Instruction Create(OpCode opcode, Operand op0, Operand op1, Operand op2)
{ {
if (opcode.OperandType != OperandType.InlineCmpValue) throw new ArgumentOutOfRangeException(nameof(opcode), "Opcode does not have three value operands"); if (opcode.OperandType != OperandType.InlineCmpValue && opcode.OperandType != OperandType.InlineCmpValueType)
throw new ArgumentOutOfRangeException(nameof(opcode), "Opcode does not have three value operands");
return new Instruction(opcode, new List<Operand>(3) { op0, op1, op2 }); return new Instruction(opcode, new List<Operand>(3) { op0, op1, op2 });
} }