Skip to content

Commit 03bf194

Browse files
jpnoirtritao
authored andcommitted
Enhanced ExpressionEvaluator to return implicitly typed uint, long, and ulong values from EvaluateNumber when there is no literal type suffix applied to a numeric expression string.
Changed Library.GenerateEnumFromMacro and friends to track the primitive type of the Enumeration while building an Enumeration's list of items so that we can support negative items within an Enumeration. Fixed bugs in GetUnderlyingTypeForEnumValue where "<" should have been "<=" and Long should have been LongLong. Extended it to support negative values. Added tests.
1 parent 677991b commit 03bf194

File tree

7 files changed

+430
-60
lines changed

7 files changed

+430
-60
lines changed

src/Generator.Tests/Passes/TestPasses.cs

Lines changed: 192 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1-
using CppSharp.AST;
1+
using CodingSeb.ExpressionEvaluator;
2+
using CppSharp.AST;
23
using CppSharp.Generators.CSharp;
34
using CppSharp.Passes;
45
using NUnit.Framework;
6+
using System.Collections;
7+
using System.Collections.Generic;
8+
using System.Globalization;
59
using System.Linq;
10+
using System.Runtime.CompilerServices;
11+
using System.Text.RegularExpressions;
612

713
namespace CppSharp.Generator.Tests.Passes
814
{
@@ -84,7 +90,7 @@ public void TestCleanCommentsPass()
8490
passBuilder.AddPass(new CleanCommentsPass());
8591
passBuilder.RunPasses(pass => pass.VisitDeclaration(c));
8692

87-
var para = (ParagraphComment) c.Comment.FullComment.Blocks[0];
93+
var para = (ParagraphComment)c.Comment.FullComment.Blocks[0];
8894
var textGenerator = new TextGenerator();
8995
textGenerator.Print(para, CommentKind.BCPLSlash);
9096

@@ -135,6 +141,190 @@ public void TestCleanEnumItemNames()
135141
Assert.That(@enum.Items[1].Name, Is.EqualTo("_0"));
136142
}
137143

144+
[Test]
145+
public void TestEnumBaseTypesFromMacroValues()
146+
{
147+
AstContext.GenerateEnumFromMacros("TestEnumMaxSbyte", "TEST_ENUM_MAX_SBYTE");
148+
var @enum = AstContext.Enum("TestEnumMaxSbyte");
149+
Assert.IsNotNull(@enum);
150+
Assert.AreEqual(sbyte.MaxValue, @enum.Items[0].Value);
151+
Assert.AreEqual(PrimitiveType.UChar, @enum.BuiltinType.Type);
152+
153+
AstContext.GenerateEnumFromMacros("TestEnumMaxByte", "TEST_ENUM_MAX_BYTE");
154+
@enum = AstContext.Enum("TestEnumMaxByte");
155+
Assert.IsNotNull(@enum);
156+
Assert.AreEqual(byte.MaxValue, @enum.Items[0].Value);
157+
Assert.AreEqual(PrimitiveType.UChar, @enum.BuiltinType.Type);
158+
159+
AstContext.GenerateEnumFromMacros("TestEnumMaxShort", "TEST_ENUM_MAX_SHORT");
160+
@enum = AstContext.Enum("TestEnumMaxShort");
161+
Assert.IsNotNull(@enum);
162+
Assert.AreEqual(short.MaxValue, @enum.Items[0].Value);
163+
Assert.AreEqual(PrimitiveType.Short, @enum.BuiltinType.Type);
164+
165+
AstContext.GenerateEnumFromMacros("TestEnumMaxUshort", "TEST_ENUM_MAX_USHORT");
166+
@enum = AstContext.Enum("TestEnumMaxUshort");
167+
Assert.IsNotNull(@enum);
168+
Assert.AreEqual(ushort.MaxValue, @enum.Items[0].Value);
169+
Assert.AreEqual(PrimitiveType.UShort, @enum.BuiltinType.Type);
170+
171+
AstContext.GenerateEnumFromMacros("TestEnumMaxInt", "TEST_ENUM_MAX_INT");
172+
@enum = AstContext.Enum("TestEnumMaxInt");
173+
Assert.IsNotNull(@enum);
174+
Assert.AreEqual(int.MaxValue, @enum.Items[0].Value);
175+
Assert.AreEqual(PrimitiveType.Int, @enum.BuiltinType.Type);
176+
177+
AstContext.GenerateEnumFromMacros("TestEnumMaxLong", "TEST_ENUM_MAX_LONG");
178+
@enum = AstContext.Enum("TestEnumMaxLong");
179+
Assert.IsNotNull(@enum);
180+
Assert.AreEqual(long.MaxValue, @enum.Items[0].Value);
181+
Assert.AreEqual(PrimitiveType.LongLong, @enum.BuiltinType.Type);
182+
183+
AstContext.GenerateEnumFromMacros("TestEnumMaxUint", "TEST_ENUM_MAX_UINT");
184+
@enum = AstContext.Enum("TestEnumMaxUint");
185+
Assert.IsNotNull(@enum);
186+
Assert.AreEqual(uint.MaxValue, @enum.Items[0].Value);
187+
Assert.AreEqual(PrimitiveType.UInt, @enum.BuiltinType.Type);
188+
189+
AstContext.GenerateEnumFromMacros("TestEnumMaxUlong", "TEST_ENUM_MAX_ULONG");
190+
@enum = AstContext.Enum("TestEnumMaxUlong");
191+
Assert.IsNotNull(@enum);
192+
Assert.AreEqual(ulong.MaxValue, @enum.Items[0].Value);
193+
Assert.AreEqual(PrimitiveType.ULongLong, @enum.BuiltinType.Type);
194+
195+
AstContext.GenerateEnumFromMacros("TestEnumMinInt", "TEST_ENUM_MIN_INT");
196+
@enum = AstContext.Enum("TestEnumMinInt");
197+
Assert.IsNotNull(@enum);
198+
Assert.AreEqual(int.MinValue, (long)@enum.Items[0].Value);
199+
Assert.AreEqual(PrimitiveType.Int, @enum.BuiltinType.Type);
200+
201+
AstContext.GenerateEnumFromMacros("TestEnumMinLong", "TEST_ENUM_MIN_LONG");
202+
@enum = AstContext.Enum("TestEnumMinLong");
203+
Assert.IsNotNull(@enum);
204+
Assert.AreEqual(long.MinValue, (long)@enum.Items[0].Value);
205+
Assert.AreEqual(PrimitiveType.LongLong, @enum.BuiltinType.Type);
206+
}
207+
208+
// These tests have nothing to do with testing passes except that the expression evaluator
209+
// is only used by GenerateEnumFromMacros. Add a separate TestFixture?
210+
static ExprEvalTestParams[] TestExpressionEvaluatorTestCases =
211+
{
212+
new ExprEvalTestParams("2147483647", int.MaxValue),
213+
new ExprEvalTestParams("-2147483648", int.MinValue),
214+
new ExprEvalTestParams("4294967295", uint.MaxValue),
215+
new ExprEvalTestParams("9223372036854775807", long.MaxValue),
216+
new ExprEvalTestParams("-9223372036854775808", long.MinValue),
217+
new ExprEvalTestParams("18446744073709551615", ulong.MaxValue),
218+
219+
// Must use "L" suffix here or the expression evaluator will overflow with no warning. Note
220+
// that it won't compile in C# without it.
221+
new ExprEvalTestParams("-2147483648L - 1", -2147483648L - 1),
222+
223+
// Note that the dynamic subsystem used by the expression evaluator types this result as
224+
// long, but the compiler types the expression a uint. Adding the casts to dynamic in
225+
// the expressions below matches the result of the expression evaluator. Not sure we
226+
// care that they're different.
227+
new ExprEvalTestParams("2147483648 + 5", (dynamic)2147483648 + (dynamic)5),
228+
new ExprEvalTestParams("5 + 2147483648", (dynamic)5 + (dynamic)2147483648),
229+
230+
new ExprEvalTestParams("0x2A828670572C << 1", 0x2A828670572C << 1),
231+
new ExprEvalTestParams("-0xFC84D76B0482", -0xFC84D76B0482),
232+
new ExprEvalTestParams("27 - 9223372036854775807", 27 - 9223372036854775807),
233+
new ExprEvalTestParams("9223372036854775807", 9223372036854775807),
234+
new ExprEvalTestParams("18446744073709551615", 18446744073709551615),
235+
new ExprEvalTestParams("1 << 5", 1 << 5),
236+
new ExprEvalTestParams("1 << 32", 1 << 32),
237+
new ExprEvalTestParams("1L << 32", 1L << 32),
238+
new ExprEvalTestParams("5u", 5u),
239+
new ExprEvalTestParams("5ul", 5ul),
240+
new ExprEvalTestParams("\"This is\" + \" a string expression\"", "This is" + " a string expression"),
241+
new ExprEvalTestParams("(17 - 48 + 80 * 81 - 88) + (74 + 79 - 50) - ((76 - 51 - 88 + (9 + 98 - 47)))", (17 - 48 + 80 * 81 - 88) + (74 + 79 - 50) - ((76 - 51 - 88 + (9 + 98 - 47)))),
242+
new ExprEvalTestParams("3.14159265", 3.14159265),
243+
new ExprEvalTestParams("16 - 47 * (75 / 91) + 44", 16 - 47 * (75 / 91) + 44), // Does C# truncate the same way?
244+
new ExprEvalTestParams("16 - 47 * (75d / 91d) + 44", 16 - 47 * (75d / 91d) + 44),
245+
new ExprEvalTestParams("69d / 5 - 48 - 47 / (82d - 71 + 2 + 6 / 39d) - 56", 69d / 5 - 48 - 47 / (82d - 71 + 2 + 6 / 39d) - 56),
246+
new ExprEvalTestParams("55.59m", 55.59m),
247+
new ExprEvalTestParams("55.59m + 23", 55.59m + 23),
248+
new ExprEvalTestParams("'A'", 'A'),
249+
new ExprEvalTestParams("'A'+'B'", 'A' + 'B'),
250+
new ExprEvalTestParams("(int)'A'", (int)'A'),
251+
252+
// C++ specific sufixes not supported. C++ octal not supported.
253+
//new EETestParams("5ll", 5L),
254+
//new EETestParams("5ull", 5UL),
255+
//new EETestParams("016", 8 + 6),
256+
257+
new ExprEvalTestParams("V1 | V2", 3, ("V1", 1), ("V2", 2)),
258+
new ExprEvalTestParams("TRUE && FALSE", false, ("TRUE", true), ("FALSE", false)),
259+
};
260+
261+
class ExprEvalTestParams
262+
{
263+
public readonly string Expression;
264+
public readonly object ExpectedResult;
265+
public readonly (string Name, object Value)[] Symbols;
266+
public ExprEvalTestParams(string expression, object expectedResult, params (string Name, object Value)[] symbols)
267+
{
268+
Expression = expression;
269+
ExpectedResult = expectedResult;
270+
Symbols = symbols;
271+
}
272+
}
273+
274+
static IEnumerable GetExpressionEvaluatorTestCases()
275+
{
276+
var testNumber = 100;
277+
foreach (var tc in TestExpressionEvaluatorTestCases)
278+
{
279+
// "testNumber" is just used to cause the test runner to show the tests in the order
280+
// we've listed them which is simply a development time convenience.
281+
yield return new TestCaseData(testNumber++, tc.Expression, tc.ExpectedResult, tc.Symbols);
282+
}
283+
}
284+
285+
[TestCaseSource(nameof(GetExpressionEvaluatorTestCases))]
286+
public void TestExpressionEvaluator(int testNumber, string expression, object expectedResult, (string SymbolName, object SymbolValue)[] symbols)
287+
{
288+
var evaluator = symbols == null || symbols.Length == 0
289+
? new ExpressionEvaluator()
290+
: new ExpressionEvaluator(symbols.ToDictionary(s => s.SymbolName, s => s.SymbolValue));
291+
292+
var result = evaluator.Evaluate(expression);
293+
Assert.AreEqual(expectedResult, result);
294+
Assert.AreEqual(expectedResult.GetType(), result.GetType());
295+
}
296+
297+
[Test]
298+
public void TestEnumsWithBitwiseExpressionMacroValues()
299+
{
300+
var @enum = AstContext.GenerateEnumFromMacros("TestBitwiseShift", "TEST_BITWISE_SHIFT_(.*)");
301+
Assert.IsNotNull(@enum);
302+
Assert.AreEqual(PrimitiveType.LongLong, @enum.BuiltinType.Type);
303+
Assert.AreEqual(0x2A828670572C << 1, (long)@enum.Items[0].Value);
304+
305+
@enum = AstContext.GenerateEnumFromMacros("TestNegativeHex", "TEST_NEGATIVE_HEX_(.*)");
306+
Assert.IsNotNull(@enum);
307+
Assert.AreEqual(PrimitiveType.LongLong, @enum.BuiltinType.Type);
308+
Assert.AreEqual(-0xFC84D76B0482, (long)@enum.Items[0].Value);
309+
310+
@enum = AstContext.GenerateEnumFromMacros("TestBitwiseOr", "TEST_BITWISE_OR_1");
311+
Assert.IsNotNull(@enum);
312+
Assert.AreEqual(PrimitiveType.UChar, @enum.BuiltinType.Type);
313+
Assert.AreEqual(0x7F | 0x80, @enum.Items[0].Value);
314+
315+
@enum = AstContext.GenerateEnumFromMacros("TestBitwiseAnd", "TEST_BITWISE_AND_(.*)");
316+
Assert.IsNotNull(@enum);
317+
Assert.AreEqual(PrimitiveType.UChar, @enum.BuiltinType.Type);
318+
Assert.AreEqual(0x7F & 0xFF, @enum.Items[0].Value);
319+
Assert.AreEqual(0x73 & -1, @enum.Items[1].Value);
320+
Assert.AreEqual(0x42 & ~0x2, @enum.Items[2].Value);
321+
322+
@enum = AstContext.GenerateEnumFromMacros("TestBitwiseXor", "TEST_BITWISE_XOR_(.*)");
323+
Assert.IsNotNull(@enum);
324+
Assert.AreEqual(PrimitiveType.UChar, @enum.BuiltinType.Type);
325+
Assert.AreEqual(0x7F ^ 0x03, @enum.Items[0].Value);
326+
}
327+
138328
[Test]
139329
public void TestUnnamedEnumSupport()
140330
{

0 commit comments

Comments
 (0)