From 900fd84b25d6fbf3a0d187318c534c91089f73e9 Mon Sep 17 00:00:00 2001 From: Orden4 Date: Thu, 28 Sep 2023 22:51:09 +0200 Subject: [PATCH 1/3] Add support for @CSharpLua.Get and @CSharpLua.Set. Add support for templating implicit/explicit operators. --- .../LuaAst/LuaMemberAccessExpressionSyntax.cs | 29 +++ CSharp.lua/LuaAst/LuaStatementSyntax.cs | 2 + CSharp.lua/LuaSyntaxGenerator.cs | 15 +- CSharp.lua/LuaSyntaxNodeTransform.Helper.cs | 25 +- CSharp.lua/LuaSyntaxNodeTransform.Object.cs | 5 + CSharp.lua/LuaSyntaxNodeTransform.cs | 36 ++- CSharp.lua/Utils.cs | 28 ++- test/BridgeNetTests/Batch1/src/Constants.cs | 2 + .../src/Templates/ConversionTemplateTests.cs | 43 ++++ .../src/Templates/PropertyTemplateTests.cs | 224 ++++++++++++++++++ 10 files changed, 396 insertions(+), 13 deletions(-) create mode 100644 test/BridgeNetTests/Batch1/src/Templates/ConversionTemplateTests.cs create mode 100644 test/BridgeNetTests/Batch1/src/Templates/PropertyTemplateTests.cs diff --git a/CSharp.lua/LuaAst/LuaMemberAccessExpressionSyntax.cs b/CSharp.lua/LuaAst/LuaMemberAccessExpressionSyntax.cs index 23823cb7..570bba91 100644 --- a/CSharp.lua/LuaAst/LuaMemberAccessExpressionSyntax.cs +++ b/CSharp.lua/LuaAst/LuaMemberAccessExpressionSyntax.cs @@ -15,7 +15,9 @@ limitations under the License. */ using System; +using System.Collections.Generic; using System.Diagnostics.Contracts; +using System.Linq; namespace CSharpLua.LuaAst { public sealed class LuaMemberAccessExpressionSyntax : LuaExpressionSyntax { @@ -110,4 +112,31 @@ internal override void Render(LuaRenderer renderer) { renderer.Render(this); } } + + public sealed class LuaPropertyTemplateExpressionSyntax : LuaExpressionSyntax { + public string GetTemplate { get; private set; } + public string SetTemplate { get; private set; } + public LuaCodeTemplateExpressionSyntax GetExpression { get; private set; } + public readonly LuaArgumentListSyntax ArgumentList = new(); + + public LuaPropertyTemplateExpressionSyntax(string getExpression, string setExpression) { + GetTemplate = getExpression; + SetTemplate = setExpression; + } + + public void Update(LuaExpressionSyntax expression, LuaCodeTemplateExpressionSyntax getExpression, bool isStatic) { + if (!isStatic) { + ArgumentList.AddArgument(expression); + } + GetExpression = getExpression; + } + + internal override void Render(LuaRenderer renderer) { + renderer.Render(GetExpression); + } + + internal IEnumerable GetArguments(params LuaExpressionSyntax[] luaBinaryExpressionSyntax) { + return ArgumentList.Arguments.Concat(luaBinaryExpressionSyntax); + } + } } diff --git a/CSharp.lua/LuaAst/LuaStatementSyntax.cs b/CSharp.lua/LuaAst/LuaStatementSyntax.cs index 0f1cbeb5..ffd5a30d 100644 --- a/CSharp.lua/LuaAst/LuaStatementSyntax.cs +++ b/CSharp.lua/LuaAst/LuaStatementSyntax.cs @@ -220,6 +220,8 @@ public enum AttributeFlags { MetadataAll = 1 << 3, Template = 1 << 4, Params = 1 << 5, + Get = 1 << 6, + Set = 1 << 7 } public readonly List Statements = new(); diff --git a/CSharp.lua/LuaSyntaxGenerator.cs b/CSharp.lua/LuaSyntaxGenerator.cs index a2c80091..296c1f6f 100644 --- a/CSharp.lua/LuaSyntaxGenerator.cs +++ b/CSharp.lua/LuaSyntaxGenerator.cs @@ -1012,7 +1012,7 @@ private List GetSymbolNames(ISymbol symbol) { switch (symbol.Kind) { case SymbolKind.Property:{ var propertySymbol = (IPropertySymbol)symbol; - if (IsPropertyField(propertySymbol)) { + if (IsPropertyField(propertySymbol) && !IsPropertyTemplate(propertySymbol)) { names.Add(symbol.Name); } else { string baseName = GetSymbolBaseName(symbol); @@ -1409,6 +1409,7 @@ public bool IsMonoBehaviourSpecialMethod(IMethodSymbol symbol) { private readonly Dictionary> implicitInterfaceTypes_ = new(SymbolEqualityComparer.Default); private readonly HashSet typesOfExtendSelf_ = new(SymbolEqualityComparer.Default); + private readonly ConcurrentDictionary isPropertyTemplates_ = new(SymbolEqualityComparer.Default); private readonly ConcurrentDictionary isFieldProperties_ = new(SymbolEqualityComparer.Default); private readonly ConcurrentDictionary isFieldEvents_ = new(SymbolEqualityComparer.Default); private readonly ConcurrentDictionary isMoreThanLocalVariables_ = new(SymbolEqualityComparer.Default); @@ -1630,6 +1631,18 @@ private bool IsPropertyFieldInternal(IPropertySymbol symbol) { return symbol.IsAutoProperty(); } + internal bool IsPropertyTemplate(IPropertySymbol symbol) { + return isPropertyTemplates_.GetOrAdd(symbol, symbol => { + var node = symbol.GetDeclaringSyntaxNode(); + if (node == null) { + return false; + } else { + return node.HasCSharpLuaAttribute(LuaDocumentStatement.AttributeFlags.Get) + || symbol.GetDeclaringSyntaxNode().HasCSharpLuaAttribute(LuaDocumentStatement.AttributeFlags.Set); + } + }); + } + internal bool IsPropertyField(IPropertySymbol symbol) { return isFieldProperties_.GetOrAdd(symbol, symbol => { bool? isMateField = XmlMetaProvider.IsPropertyField(symbol); diff --git a/CSharp.lua/LuaSyntaxNodeTransform.Helper.cs b/CSharp.lua/LuaSyntaxNodeTransform.Helper.cs index 319db614..db522f05 100644 --- a/CSharp.lua/LuaSyntaxNodeTransform.Helper.cs +++ b/CSharp.lua/LuaSyntaxNodeTransform.Helper.cs @@ -301,6 +301,10 @@ private LuaExpressionSyntax BuildCodeTemplateExpression(string codeTemplate, Lua return InternalBuildCodeTemplateExpression(codeTemplate, null, null, null, targetExpression); } + private LuaExpressionSyntax BuildCodeTemplateExpression(string codeTemplate, IEnumerable targetExpressions) { + return InternalBuildCodeTemplateExpression(codeTemplate, null, targetExpressions.Select>(i => () => i), null); + } + private LuaExpressionSyntax BuildCodeTemplateExpression(string codeTemplate, ExpressionSyntax targetExpression, IEnumerable arguments, IList typeArguments) { return InternalBuildCodeTemplateExpression(codeTemplate, targetExpression, arguments.Select>(i => () => i), typeArguments); } @@ -417,6 +421,10 @@ private void AddExportEnum(ITypeSymbol enumType) { generator_.AddExportEnum(enumType); } + private bool IsPropertyTemplate(IPropertySymbol symbol) { + return generator_.IsPropertyTemplate(symbol); + } + private bool IsPropertyField(IPropertySymbol symbol) { return generator_.IsPropertyField(symbol); } @@ -1108,6 +1116,21 @@ private LuaExpressionSyntax BuildFieldOrPropertyMemberAccessExpression(LuaExpres return name; }*/ + if (name is LuaPropertyTemplateExpressionSyntax propertyTemplate) { + if (isStatic) { + var getExpression = propertyTemplate.GetTemplate != null + ? new LuaCodeTemplateExpressionSyntax(propertyTemplate.GetTemplate) + : null; + propertyTemplate.Update(expression, getExpression, isStatic); + } else { + var getExpression = propertyTemplate.GetTemplate != null + ? (LuaCodeTemplateExpressionSyntax)BuildCodeTemplateExpression(propertyTemplate.GetTemplate, new[] { expression }) + : null; + propertyTemplate.Update(expression, getExpression, isStatic); + } + return propertyTemplate; + } + if (name is LuaPropertyAdapterExpressionSyntax propertyMethod) { var arguments = propertyMethod.ArgumentList.Arguments; if (arguments.Count == 1) { @@ -1233,7 +1256,7 @@ private static IParameterSymbol GetParameterSymbol(IMethodSymbol symbol, Argumen } private void CheckValueTypeClone(ITypeSymbol typeSymbol, IdentifierNameSyntax node, ref LuaExpressionSyntax expression, bool isPropertyField = false) { - if (typeSymbol.IsCustomValueType() && !generator_.IsReadOnlyStruct(typeSymbol) && !typeSymbol.IsNullableWithBasicElementType()) { + if (typeSymbol.IsCustomValueType() && !generator_.IsReadOnlyStruct(typeSymbol) && !typeSymbol.IsNullableWithBasicElementType() && expression is not LuaPropertyTemplateExpressionSyntax) { bool need = false; if (isPropertyField) { need = true; diff --git a/CSharp.lua/LuaSyntaxNodeTransform.Object.cs b/CSharp.lua/LuaSyntaxNodeTransform.Object.cs index be642ed5..74dcd204 100644 --- a/CSharp.lua/LuaSyntaxNodeTransform.Object.cs +++ b/CSharp.lua/LuaSyntaxNodeTransform.Object.cs @@ -1240,6 +1240,11 @@ private void BuildOperatorMethodDeclaration(BaseMethodDeclarationSyntax node) { } public override LuaSyntaxNode VisitConversionOperatorDeclaration(ConversionOperatorDeclarationSyntax node) { + var symbol = semanticModel_.GetDeclaredSymbol(node); + var codeTemplate = XmlMetaProvider.GetMethodCodeTemplate(symbol); + if (codeTemplate != null) { + return BuildCodeTemplateExpression(codeTemplate, node.ParameterList.Accept(this).Parameters); + } BuildOperatorMethodDeclaration(node); return base.VisitConversionOperatorDeclaration(node); } diff --git a/CSharp.lua/LuaSyntaxNodeTransform.cs b/CSharp.lua/LuaSyntaxNodeTransform.cs index ff29cab0..cdd96ddf 100644 --- a/CSharp.lua/LuaSyntaxNodeTransform.cs +++ b/CSharp.lua/LuaSyntaxNodeTransform.cs @@ -900,7 +900,7 @@ private void VisitBaseFieldDeclarationSyntax(BaseFieldDeclarationSyntax node) { bool isImmutable = typeSymbol.IsImmutable(); foreach (var variable in node.Declaration.Variables) { var variableSymbol = semanticModel_.GetDeclaredSymbol(variable); - if (variableSymbol.IsAbstract) { + if (variableSymbol.IsAbstract || variableSymbol.IsExtern || variableSymbol.GetDeclaringSyntaxNode().HasCSharpLuaAttribute(LuaDocumentStatement.AttributeFlags.Ignore)) { continue; } @@ -1062,7 +1062,7 @@ private void AddPropertyOrEventMetaData(ISymbol symbol, PropertyMethodResult get public override LuaSyntaxNode VisitPropertyDeclaration(PropertyDeclarationSyntax node) { var symbol = semanticModel_.GetDeclaredSymbol(node); - if (!symbol.IsAbstract) { + if (!symbol.IsAbstract && !symbol.IsExtern && !symbol.GetDeclaringSyntaxNode().HasCSharpLuaAttribute(LuaDocumentStatement.AttributeFlags.Ignore)) { bool isStatic = symbol.IsStatic; bool isPrivate = generator_.IsPrivate(symbol) && symbol.ExplicitInterfaceImplementations.IsEmpty; bool hasGet = false; @@ -1174,7 +1174,7 @@ private bool IsReadOnlyProperty(PropertyDeclarationSyntax node) { public override LuaSyntaxNode VisitEventDeclaration(EventDeclarationSyntax node) { var symbol = semanticModel_.GetDeclaredSymbol(node); - if (!symbol.IsAbstract) { + if (!symbol.IsAbstract && !symbol.IsExtern && !symbol.GetDeclaringSyntaxNode().HasCSharpLuaAttribute(LuaDocumentStatement.AttributeFlags.Ignore)) { var attributes = BuildAttributes(node.AttributeLists); bool isStatic = symbol.IsStatic; bool isPrivate = symbol.IsPrivate() && symbol.ExplicitInterfaceImplementations.IsEmpty; @@ -1233,7 +1233,7 @@ public override LuaSyntaxNode VisitEnumMemberDeclaration(EnumMemberDeclarationSy public override LuaSyntaxNode VisitIndexerDeclaration(IndexerDeclarationSyntax node) { var symbol = semanticModel_.GetDeclaredSymbol(node); - if (!symbol.IsAbstract) { + if (!symbol.IsAbstract && !symbol.IsExtern && !symbol.GetDeclaringSyntaxNode().HasCSharpLuaAttribute(LuaDocumentStatement.AttributeFlags.Ignore)) { bool isPrivate = symbol.IsPrivate(); var indexName = GetMemberName(symbol); var parameterList = node.ParameterList.Accept(this); @@ -1564,7 +1564,10 @@ public override LuaSyntaxNode VisitExpressionStatement(ExpressionStatementSyntax private LuaExpressionSyntax BuildCommonAssignmentExpression(LuaExpressionSyntax left, LuaExpressionSyntax right, string operatorToken, ExpressionSyntax rightNode, ExpressionSyntax parent) { bool isPreventDebugConcatenation = IsPreventDebug && operatorToken == LuaSyntaxNode.Tokens.Concatenation; - if (left is LuaPropertyAdapterExpressionSyntax propertyAdapter) { + if (left is LuaPropertyTemplateExpressionSyntax propertyTemplate) { + var arguments = propertyTemplate.GetArguments(propertyTemplate.GetExpression.Binary(operatorToken, right)); + return BuildCodeTemplateExpression(propertyTemplate.SetTemplate, arguments); + } else if (left is LuaPropertyAdapterExpressionSyntax propertyAdapter) { LuaExpressionSyntax expression = null; var getter = propertyAdapter.GetCloneOfGet(); LuaExpressionSyntax leftExpression = getter; @@ -1614,6 +1617,13 @@ private LuaExpressionSyntax BuildDelegateBinaryExpression(LuaExpressionSyntax le } private LuaExpressionSyntax BuildDelegateAssignmentExpression(LuaExpressionSyntax left, LuaExpressionSyntax right, bool isPlus) { + if (left is LuaPropertyTemplateExpressionSyntax propertyTemplate) { + var expr = isPlus + ? propertyTemplate.GetExpression.Plus(right) + : propertyTemplate.GetExpression.Sub(right); + return BuildCodeTemplateExpression(propertyTemplate.SetTemplate, propertyTemplate.GetArguments(expr)); + } + if (left is LuaPropertyAdapterExpressionSyntax propertyAdapter) { if (propertyAdapter.IsProperty) { propertyAdapter.ArgumentList.AddArgument(BuildDelegateBinaryExpression(propertyAdapter.GetCloneOfGet(), right, isPlus)); @@ -1629,7 +1639,11 @@ private LuaExpressionSyntax BuildDelegateAssignmentExpression(LuaExpressionSynta } private LuaExpressionSyntax BuildBinaryInvokeAssignmentExpression(LuaExpressionSyntax left, LuaExpressionSyntax right, LuaExpressionSyntax methodName) { - if (left is LuaPropertyAdapterExpressionSyntax propertyAdapter) { + if (left is LuaPropertyTemplateExpressionSyntax propertyTemplate) { + var invocation = new LuaInvocationExpressionSyntax(methodName, propertyTemplate.GetExpression, right); + var arguments = propertyTemplate.GetArguments(invocation); + return BuildCodeTemplateExpression(propertyTemplate.SetTemplate, arguments); + } else if (left is LuaPropertyAdapterExpressionSyntax propertyAdapter) { var invocation = new LuaInvocationExpressionSyntax(methodName, propertyAdapter.GetCloneOfGet(), right); propertyAdapter.ArgumentList.AddArgument(invocation); return propertyAdapter; @@ -1846,7 +1860,10 @@ private LuaExpressionSyntax BuildLuaAssignmentExpression(AssignmentExpressionSyn case SyntaxKind.SimpleAssignmentExpression: { var left = leftNode.AcceptExpression(this); var right = VisitExpression(rightNode); - if (leftNode.Kind().IsTupleDeclaration()) { + if (left is LuaPropertyTemplateExpressionSyntax propertyTemplate) { + var arguments = propertyTemplate.GetArguments(right); + return BuildCodeTemplateExpression(propertyTemplate.SetTemplate, arguments); + } else if (leftNode.Kind().IsTupleDeclaration()) { if (!rightNode.IsKind(SyntaxKind.TupleExpression)) { right = BuildDeconstructExpression(rightNode, right); } @@ -2855,6 +2872,11 @@ private LuaExpressionSyntax VisitPropertyOrEventIdentifierName(IdentifierNameSyn var propertySymbol = (IPropertySymbol)symbol; isField = IsPropertyField(node, propertySymbol); isReadOnly = propertySymbol.IsReadOnly; + if (IsPropertyTemplate(propertySymbol)) { + var (get, set) = Utility.GetPropertyTemplateFromAttribute(symbol); + LuaIdentifierNameSyntax fieldName = GetMemberName(symbol); + return new LuaPropertyTemplateExpressionSyntax(get, set); + } } else { var eventSymbol = (IEventSymbol)symbol; isField = IsEventField(eventSymbol); diff --git a/CSharp.lua/Utils.cs b/CSharp.lua/Utils.cs index 4606f89c..adb68829 100644 --- a/CSharp.lua/Utils.cs +++ b/CSharp.lua/Utils.cs @@ -594,21 +594,41 @@ public static string GetCodeTemplateFromAttribute(this ISymbol symbol) { node = node.Parent.Parent; } if (node.HasCSharpLuaAttribute(LuaDocumentStatement.AttributeFlags.Template, out string text)) { - return GetCodeTemplateFromAttributeText(text); + return GetCodeTemplateFromAttributeText(text, codeTemplateAttributeRegex_); } } else { string xml = symbol.GetDocumentationCommentXml(); if (xml != null) { - return GetCodeTemplateFromAttributeText(xml); + return GetCodeTemplateFromAttributeText(xml, codeTemplateAttributeRegex_); } } return null; } + public static (string get, string set) GetPropertyTemplateFromAttribute(this ISymbol symbol) { + var node = symbol.GetDeclaringSyntaxNode(); + string get = null; + string set = null; + if (node != null) { + if (symbol.Kind == SymbolKind.Field) { + node = node.Parent.Parent; + } + if (node.HasCSharpLuaAttribute(LuaDocumentStatement.AttributeFlags.Get, out string getText)) { + get = GetCodeTemplateFromAttributeText(getText, codeGetAttributeRegex_); + } + if (node.HasCSharpLuaAttribute(LuaDocumentStatement.AttributeFlags.Set, out string setText)) { + set = GetCodeTemplateFromAttributeText(setText, codeSetAttributeRegex_); + } + } + return (get, set); + } + private static readonly Regex codeTemplateAttributeRegex_ = new(@"@CSharpLua.Template\s*=\s*(.+)\s*", RegexOptions.Compiled); + private static readonly Regex codeGetAttributeRegex_ = new(@"@CSharpLua.Get\s*=\s*(.+)\s*", RegexOptions.Compiled); + private static readonly Regex codeSetAttributeRegex_ = new(@"@CSharpLua.Set\s*=\s*(.+)\s*", RegexOptions.Compiled); - private static string GetCodeTemplateFromAttributeText(string document) { - var matches = codeTemplateAttributeRegex_.Matches(document); + private static string GetCodeTemplateFromAttributeText(string document, Regex regex) { + var matches = regex.Matches(document); if (matches.Count > 0) { string text = matches[0].Groups[1].Value; return text.Trim().Trim('"'); diff --git a/test/BridgeNetTests/Batch1/src/Constants.cs b/test/BridgeNetTests/Batch1/src/Constants.cs index 763a6144..21b2b012 100644 --- a/test/BridgeNetTests/Batch1/src/Constants.cs +++ b/test/BridgeNetTests/Batch1/src/Constants.cs @@ -112,6 +112,8 @@ public static class Constants public const string MODULE_RUNTIME = "Runtime helpers"; // + "Array"; public const string MODULE_IO = "IO"; + public const string MODULE_TEMPLATES = "@CSharpLua templates"; + public const string IGNORE_DATE = null; } } diff --git a/test/BridgeNetTests/Batch1/src/Templates/ConversionTemplateTests.cs b/test/BridgeNetTests/Batch1/src/Templates/ConversionTemplateTests.cs new file mode 100644 index 00000000..6f08b212 --- /dev/null +++ b/test/BridgeNetTests/Batch1/src/Templates/ConversionTemplateTests.cs @@ -0,0 +1,43 @@ +using Bridge.ClientTest; +using Bridge.Test.NUnit; + +namespace Batch1.src.Templates +{ + [Category(Constants.MODULE_TEMPLATES)] + [TestFixture(TestNameFormat = "Conversion templates - {0}")] + public class ConversionTemplateTests + { + [Test] + public void TestImplicitCast() + { + var type1 = new Type1(); + Type2 type2 = type1; + + // Type didn't actually change due to the template + Assert.AreEqual($"{nameof(ConversionTemplateTests)}+{nameof(Type1)}", type2.GetType().Name); + } + + [Test] + public void TestExplicitCast() + { + var type2 = new Type2(); + var type1 = (Type1)type2; + + // Type didn't actually change due to the template + Assert.AreEqual($"{nameof(ConversionTemplateTests)}+{nameof(Type2)}", type1.GetType().Name); + } + +#pragma warning disable CS0626 // Method, operator, or accessor is marked external and has no attributes on it + private class Type1 + { + /// @CSharpLua.Template = "{0}" + public static extern implicit operator Type2(Type1 t); + } + + private class Type2 + { + /// @CSharpLua.Template = "{0}" + public static extern explicit operator Type1(Type2 t); + } + } +} diff --git a/test/BridgeNetTests/Batch1/src/Templates/PropertyTemplateTests.cs b/test/BridgeNetTests/Batch1/src/Templates/PropertyTemplateTests.cs new file mode 100644 index 00000000..4d088b7a --- /dev/null +++ b/test/BridgeNetTests/Batch1/src/Templates/PropertyTemplateTests.cs @@ -0,0 +1,224 @@ +using Bridge.ClientTest; +using Bridge.Test.NUnit; + +namespace Batch1.src.Templates +{ + [Category(Constants.MODULE_TEMPLATES)] + [TestFixture(TestNameFormat = "Property templates - {0}")] + public class TemplatePropertyTest + { + [Test] + public static void PropertyTemplateGetSetTest() + { + var test = new TestType(); + for (var i = 0; i < 10; i++) + { + test.FullProp = i; + Assert.AreEqual(i, test.FullProp); + } + } + + [Test] + public static void PropertyTemplateGetOnlySetOnlyTest() + { + var test = new TestType(); + for (var i = 0; i < 10; i++) + { + test.SetProp = i; + Assert.AreEqual(i, test.GetProp); + } + } + + [Test] + public static void PropertyTemplateGetOnlyTest() + { + var test = new TestType(); + Assert.AreEqual(4, test.GetOnlyProp); + } + + [Test] + public static void StaticPropertyTemplateGetSetTest() + { + var a = 5; + Assert.AreEqual(5, a); + TestType.StaticProp = 7; + Assert.AreEqual(7, TestType.StaticProp); + } + + [Test] + public static void PropertyTemplateIncrementTest() + { + var test = new TestType + { + FullProp = 4 + }; + test.FullProp++; + Assert.AreEqual(5, test.FullProp); + } + + [Test] + public static void PropertyTemplateDecrementTest() + { + var test = new TestType + { + FullProp = 4 + }; + test.FullProp--; + Assert.AreEqual(3, test.FullProp); + } + + [Test] + public static void PropertyTemplateAddTest() + { + var test = new TestType + { + FullProp = 4 + }; + test.FullProp += 4; + Assert.AreEqual(8, test.FullProp); + } + + [Test] + public static void PropertyTemplateSubtractTest() + { + var test = new TestType + { + FullProp = 4 + }; + test.FullProp -= 9; + Assert.AreEqual(-5, test.FullProp); + } + + [Test] + public static void PropertyTemplateMultiplyTest() + { + var test = new TestType + { + FullProp = 4 + }; + test.FullProp *= 5; + Assert.AreEqual(20, test.FullProp); + } + + [Test] + public static void PropertyTemplateDivideTest() + { + var test = new TestType + { + FullProp = 4 + }; + test.FullProp /= 3; + Assert.AreEqual(1, test.FullProp); + } + + [Test] + public static void PropertyTemplateModuloTest() + { + var test = new TestType + { + FullProp = 13 + }; + test.FullProp %= 10; + Assert.AreEqual(3, test.FullProp); + } + + [Test] + public static void PropertyTemplateBitwiseAndTest() + { + var test = new TestType + { + FullProp = 3 + }; + test.FullProp &= 5; + Assert.AreEqual(1, test.FullProp); + } + + [Test] + public static void PropertyTemplateBitwiseOrTest() + { + var test = new TestType + { + FullProp = 3 + }; + test.FullProp |= 5; + Assert.AreEqual(7, test.FullProp); + } + + [Test] + public static void PropertyTemplateLeftShiftTest() + { + var test = new TestType + { + FullProp = 5 + }; + test.FullProp <<= 4; + Assert.AreEqual(80, test.FullProp); + } + + [Test] + public static void PropertyTemplateRightShiftTest() + { + var test = new TestType + { + FullProp = 800 + }; + test.FullProp >>= 4; + Assert.AreEqual(50, test.FullProp); + } + + [Test] + public static void PropertyTemplateConcatenateTest() + { + var test = new TestType + { + StringProp = "foo" + }; + test.StringProp += "bar"; + Assert.AreEqual("foobar", test.StringProp); + } + + [Test] + public static void PropertyTemplateNullCoallesceTest() + { + var test = new TestType(); + test.StringProp ??= "hello world"; + Assert.AreEqual("hello world", test.StringProp); + } + + [Test] + public static void PropertyTemplateNullableTest() + { + var test = new TestType(); + Assert.False(test.NullableProp.HasValue); + test.NullableProp = 5; + Assert.True(test.NullableProp.HasValue); + Assert.AreEqual(5, test.NullableProp.Value); + } + +#pragma warning disable CS0626 // Method, operator, or accessor is marked external and has no attributes on it + private class TestType + { + /// @CSharpLua.Get = "a" + /// @CSharpLua.Set = "a = {0}" + public static extern int StaticProp { get; set; } + + /// @CSharpLua.Get = "{0}.CustomProp1" + /// @CSharpLua.Set = "{0}.CustomProp1 = {1}" + public extern int FullProp { get; set; } + /// @CSharpLua.Get = "{0}.CustomProp2" + public extern int GetProp { get; set; } + /// @CSharpLua.Set = "{0}.CustomProp2 = {1}" + public extern int SetProp { get; set; } + /// @CSharpLua.Get = "4" + public extern int GetOnlyProp { get; } + + /// @CSharpLua.Get = "{0}.StringProp" + /// @CSharpLua.Set = "{0}.StringProp = {1}" + public extern string StringProp { get; set; } + + /// @CSharpLua.Get = "{0}.NullableProp" + /// @CSharpLua.Set = "{0}.NullableProp = {1}" + public extern int? NullableProp { get; set; } + } + } +} From f6fd8ccde67625df45e04f5f2f1063180dd3f73f Mon Sep 17 00:00:00 2001 From: Orden4 Date: Fri, 29 Sep 2023 14:11:56 +0200 Subject: [PATCH 2/3] Change property templates to use {this} instead of {0}. --- .../LuaAst/LuaMemberAccessExpressionSyntax.cs | 7 +++---- CSharp.lua/LuaSyntaxNodeTransform.Helper.cs | 19 ++++++------------- CSharp.lua/LuaSyntaxNodeTransform.Object.cs | 2 +- CSharp.lua/LuaSyntaxNodeTransform.cs | 9 ++++----- .../src/Templates/PropertyTemplateTests.cs | 16 ++++++++-------- 5 files changed, 22 insertions(+), 31 deletions(-) diff --git a/CSharp.lua/LuaAst/LuaMemberAccessExpressionSyntax.cs b/CSharp.lua/LuaAst/LuaMemberAccessExpressionSyntax.cs index 570bba91..d8d24938 100644 --- a/CSharp.lua/LuaAst/LuaMemberAccessExpressionSyntax.cs +++ b/CSharp.lua/LuaAst/LuaMemberAccessExpressionSyntax.cs @@ -117,6 +117,7 @@ public sealed class LuaPropertyTemplateExpressionSyntax : LuaExpressionSyntax { public string GetTemplate { get; private set; } public string SetTemplate { get; private set; } public LuaCodeTemplateExpressionSyntax GetExpression { get; private set; } + public LuaIdentifierNameSyntax Name { get; private set; } public readonly LuaArgumentListSyntax ArgumentList = new(); public LuaPropertyTemplateExpressionSyntax(string getExpression, string setExpression) { @@ -124,10 +125,8 @@ public LuaPropertyTemplateExpressionSyntax(string getExpression, string setExpre SetTemplate = setExpression; } - public void Update(LuaExpressionSyntax expression, LuaCodeTemplateExpressionSyntax getExpression, bool isStatic) { - if (!isStatic) { - ArgumentList.AddArgument(expression); - } + public void Update(LuaExpressionSyntax expression, LuaCodeTemplateExpressionSyntax getExpression) { + Name = new LuaSymbolNameSyntax(expression); GetExpression = getExpression; } diff --git a/CSharp.lua/LuaSyntaxNodeTransform.Helper.cs b/CSharp.lua/LuaSyntaxNodeTransform.Helper.cs index db522f05..b7e24b76 100644 --- a/CSharp.lua/LuaSyntaxNodeTransform.Helper.cs +++ b/CSharp.lua/LuaSyntaxNodeTransform.Helper.cs @@ -301,8 +301,8 @@ private LuaExpressionSyntax BuildCodeTemplateExpression(string codeTemplate, Lua return InternalBuildCodeTemplateExpression(codeTemplate, null, null, null, targetExpression); } - private LuaExpressionSyntax BuildCodeTemplateExpression(string codeTemplate, IEnumerable targetExpressions) { - return InternalBuildCodeTemplateExpression(codeTemplate, null, targetExpressions.Select>(i => () => i), null); + private LuaExpressionSyntax BuildCodeTemplateExpression(string codeTemplate, IEnumerable arguments, LuaIdentifierNameSyntax memberBindingIdentifier) { + return InternalBuildCodeTemplateExpression(codeTemplate, null, arguments.Select>(i => () => i), null, memberBindingIdentifier); } private LuaExpressionSyntax BuildCodeTemplateExpression(string codeTemplate, ExpressionSyntax targetExpression, IEnumerable arguments, IList typeArguments) { @@ -1117,17 +1117,10 @@ private LuaExpressionSyntax BuildFieldOrPropertyMemberAccessExpression(LuaExpres }*/ if (name is LuaPropertyTemplateExpressionSyntax propertyTemplate) { - if (isStatic) { - var getExpression = propertyTemplate.GetTemplate != null - ? new LuaCodeTemplateExpressionSyntax(propertyTemplate.GetTemplate) - : null; - propertyTemplate.Update(expression, getExpression, isStatic); - } else { - var getExpression = propertyTemplate.GetTemplate != null - ? (LuaCodeTemplateExpressionSyntax)BuildCodeTemplateExpression(propertyTemplate.GetTemplate, new[] { expression }) - : null; - propertyTemplate.Update(expression, getExpression, isStatic); - } + var getExpression = propertyTemplate.GetTemplate != null + ? (LuaCodeTemplateExpressionSyntax)BuildCodeTemplateExpression(propertyTemplate.GetTemplate, new LuaSymbolNameSyntax(expression)) + : null; + propertyTemplate.Update(expression, getExpression); return propertyTemplate; } diff --git a/CSharp.lua/LuaSyntaxNodeTransform.Object.cs b/CSharp.lua/LuaSyntaxNodeTransform.Object.cs index 74dcd204..ce64c8d0 100644 --- a/CSharp.lua/LuaSyntaxNodeTransform.Object.cs +++ b/CSharp.lua/LuaSyntaxNodeTransform.Object.cs @@ -1243,7 +1243,7 @@ public override LuaSyntaxNode VisitConversionOperatorDeclaration(ConversionOpera var symbol = semanticModel_.GetDeclaredSymbol(node); var codeTemplate = XmlMetaProvider.GetMethodCodeTemplate(symbol); if (codeTemplate != null) { - return BuildCodeTemplateExpression(codeTemplate, node.ParameterList.Accept(this).Parameters); + return BuildCodeTemplateExpression(codeTemplate, node.ParameterList.Accept(this).Parameters, null); } BuildOperatorMethodDeclaration(node); return base.VisitConversionOperatorDeclaration(node); diff --git a/CSharp.lua/LuaSyntaxNodeTransform.cs b/CSharp.lua/LuaSyntaxNodeTransform.cs index cdd96ddf..970dc6fc 100644 --- a/CSharp.lua/LuaSyntaxNodeTransform.cs +++ b/CSharp.lua/LuaSyntaxNodeTransform.cs @@ -1566,7 +1566,7 @@ private LuaExpressionSyntax BuildCommonAssignmentExpression(LuaExpressionSyntax bool isPreventDebugConcatenation = IsPreventDebug && operatorToken == LuaSyntaxNode.Tokens.Concatenation; if (left is LuaPropertyTemplateExpressionSyntax propertyTemplate) { var arguments = propertyTemplate.GetArguments(propertyTemplate.GetExpression.Binary(operatorToken, right)); - return BuildCodeTemplateExpression(propertyTemplate.SetTemplate, arguments); + return BuildCodeTemplateExpression(propertyTemplate.SetTemplate, arguments, propertyTemplate.Name); } else if (left is LuaPropertyAdapterExpressionSyntax propertyAdapter) { LuaExpressionSyntax expression = null; var getter = propertyAdapter.GetCloneOfGet(); @@ -1621,7 +1621,7 @@ private LuaExpressionSyntax BuildDelegateAssignmentExpression(LuaExpressionSynta var expr = isPlus ? propertyTemplate.GetExpression.Plus(right) : propertyTemplate.GetExpression.Sub(right); - return BuildCodeTemplateExpression(propertyTemplate.SetTemplate, propertyTemplate.GetArguments(expr)); + return BuildCodeTemplateExpression(propertyTemplate.SetTemplate, propertyTemplate.GetArguments(expr), propertyTemplate.Name); } if (left is LuaPropertyAdapterExpressionSyntax propertyAdapter) { @@ -1642,7 +1642,7 @@ private LuaExpressionSyntax BuildBinaryInvokeAssignmentExpression(LuaExpressionS if (left is LuaPropertyTemplateExpressionSyntax propertyTemplate) { var invocation = new LuaInvocationExpressionSyntax(methodName, propertyTemplate.GetExpression, right); var arguments = propertyTemplate.GetArguments(invocation); - return BuildCodeTemplateExpression(propertyTemplate.SetTemplate, arguments); + return BuildCodeTemplateExpression(propertyTemplate.SetTemplate, arguments, propertyTemplate.Name); } else if (left is LuaPropertyAdapterExpressionSyntax propertyAdapter) { var invocation = new LuaInvocationExpressionSyntax(methodName, propertyAdapter.GetCloneOfGet(), right); propertyAdapter.ArgumentList.AddArgument(invocation); @@ -1862,7 +1862,7 @@ private LuaExpressionSyntax BuildLuaAssignmentExpression(AssignmentExpressionSyn var right = VisitExpression(rightNode); if (left is LuaPropertyTemplateExpressionSyntax propertyTemplate) { var arguments = propertyTemplate.GetArguments(right); - return BuildCodeTemplateExpression(propertyTemplate.SetTemplate, arguments); + return BuildCodeTemplateExpression(propertyTemplate.SetTemplate, arguments, propertyTemplate.Name); } else if (leftNode.Kind().IsTupleDeclaration()) { if (!rightNode.IsKind(SyntaxKind.TupleExpression)) { right = BuildDeconstructExpression(rightNode, right); @@ -2874,7 +2874,6 @@ private LuaExpressionSyntax VisitPropertyOrEventIdentifierName(IdentifierNameSyn isReadOnly = propertySymbol.IsReadOnly; if (IsPropertyTemplate(propertySymbol)) { var (get, set) = Utility.GetPropertyTemplateFromAttribute(symbol); - LuaIdentifierNameSyntax fieldName = GetMemberName(symbol); return new LuaPropertyTemplateExpressionSyntax(get, set); } } else { diff --git a/test/BridgeNetTests/Batch1/src/Templates/PropertyTemplateTests.cs b/test/BridgeNetTests/Batch1/src/Templates/PropertyTemplateTests.cs index 4d088b7a..e4691818 100644 --- a/test/BridgeNetTests/Batch1/src/Templates/PropertyTemplateTests.cs +++ b/test/BridgeNetTests/Batch1/src/Templates/PropertyTemplateTests.cs @@ -202,22 +202,22 @@ private class TestType /// @CSharpLua.Set = "a = {0}" public static extern int StaticProp { get; set; } - /// @CSharpLua.Get = "{0}.CustomProp1" - /// @CSharpLua.Set = "{0}.CustomProp1 = {1}" + /// @CSharpLua.Get = "{this}.CustomProp1" + /// @CSharpLua.Set = "{this}.CustomProp1 = {0}" public extern int FullProp { get; set; } - /// @CSharpLua.Get = "{0}.CustomProp2" + /// @CSharpLua.Get = "{this}.CustomProp2" public extern int GetProp { get; set; } - /// @CSharpLua.Set = "{0}.CustomProp2 = {1}" + /// @CSharpLua.Set = "{this}.CustomProp2 = {0}" public extern int SetProp { get; set; } /// @CSharpLua.Get = "4" public extern int GetOnlyProp { get; } - /// @CSharpLua.Get = "{0}.StringProp" - /// @CSharpLua.Set = "{0}.StringProp = {1}" + /// @CSharpLua.Get = "{this}.CustomProp3" + /// @CSharpLua.Set = "{this}.CustomProp3 = {0}" public extern string StringProp { get; set; } - /// @CSharpLua.Get = "{0}.NullableProp" - /// @CSharpLua.Set = "{0}.NullableProp = {1}" + /// @CSharpLua.Get = "{this}.CustomProp4" + /// @CSharpLua.Set = "{this}.CustomProp4 = {0}" public extern int? NullableProp { get; set; } } } From ac3628ff2adf9ef9191c8749a68d2c310e776584 Mon Sep 17 00:00:00 2001 From: Orden4 Date: Fri, 29 Sep 2023 15:12:03 +0200 Subject: [PATCH 3/3] Add most missing Math methods. Fix rounding between -1 and -0.5. Add missing MidpointRounding modes. Add MathF as a redirect to Math. --- CSharp.lua/CoreSystem.Lua/CoreSystem/Math.lua | 116 +++++++- test/BridgeNetTests/Batch1/src/MathTests.cs | 255 ++++++++++++++++++ 2 files changed, 365 insertions(+), 6 deletions(-) diff --git a/CSharp.lua/CoreSystem.Lua/CoreSystem/Math.lua b/CSharp.lua/CoreSystem.Lua/CoreSystem/Math.lua index 9a502efd..f00f09ec 100644 --- a/CSharp.lua/CoreSystem.Lua/CoreSystem/Math.lua +++ b/CSharp.lua/CoreSystem.Lua/CoreSystem/Math.lua @@ -19,10 +19,93 @@ local trunc = System.trunc local math = math local floor = math.floor +local ceil = math.ceil local min = math.min local max = math.max local abs = math.abs local log = math.log +local sqrt = math.sqrt +local ln2 = log(2) + +local function acosh(a) + return log(a + sqrt(a ^ 2 - 1)) +end + +local function asinh(a) + return log(a + sqrt(a ^ 2 + 1)) +end + +local function atanh(a) + return 0.5 * log((1 + a) / (1 - a)) +end + +local function cbrt(a) + if a >= 0 then + return a ^ (1 / 3) + else + return -abs(a) ^ (1 / 3) + end +end + +local function copySign(a, b) + if b >= 0 then + return a >= 0 and a or -a + else + return a >= 0 and -a or a + end +end + +local function fusedMultiplyAdd(a, b, c) + return a * b + c +end + +local function ilogB(a) + return a == 0 and -2147483648 or floor(log(abs(a)) / ln2) +end + +local function log2(a) + return log(a) / ln2 +end + +local function maxMagnitude(a, b) + local x = abs(a) + local y = abs(b) + if x > y then + return a + elseif x < y then + return b + else + return a > b and a or b + end +end + +local function minMagnitude(a, b) + local x = abs(a) + local y = abs(b) + if x < y then + return a + elseif x > y then + return b + else + return a < b and a or b + end +end + +local function reciprocalEstimate(a) + return 1 / a +end + +local function reciprocalSqrtEstimate(a) + return sqrt(1 / a) +end + +local function scaleB(a, b) + return a * 2 ^ b +end + +local function sinCos(a) + return System.ValueTuple(math.sin(a), math.cos(a)) +end local function bigMul(a, b) return a * b @@ -38,11 +121,17 @@ local function round(value, digits, mode) local i = value * mult if mode == 1 then value = trunc(i + (value >= 0 and 0.5 or -0.5)) + elseif mode == 2 then + value = i >= 0 and floor(i) or ceil(i) + elseif mode == 3 then + value = floor(i) + elseif mode == 4 then + value = ceil(i) else value = trunc(i) if value ~= i then local dif = i - value - if value >= 0 then + if i >= 0 then if dif > 0.5 or (dif == 0.5 and value % 2 ~= 0) then value = value + 1 end @@ -115,30 +204,45 @@ local tanh = math.tanh or function (x) return sinh(x) / cosh(x) end local Math = math Math.Abs = abs Math.Acos = math.acos +Math.Acosh = acosh Math.Asin = math.asin +Math.Asinh = asinh Math.Atan = math.atan +Math.Atanh = atanh Math.Atan2 = math.atan2 or math.atan Math.BigMul = bigMul -Math.Ceiling = math.ceil +Math.Cbrt = cbrt +Math.Ceiling = ceil Math.Clamp = clamp +Math.CopySign = copySign Math.Cos = math.cos Math.Cosh = cosh Math.DivRem = divRem Math.Exp = exp -Math.Floor = math.floor +Math.Floor = floor +Math.FusedMultiplyAdd = fusedMultiplyAdd Math.IEEERemainder = IEEERemainder +Math.ILogB = ilogB Math.Log = log Math.Log10 = log10 -Math.Max = math.max -Math.Min = math.min +Math.Log2 = log2 +Math.Max = max +Math.MaxMagnitude = maxMagnitude +Math.Min = min +Math.MinMagnitude = minMagnitude Math.Pow = pow +Math.ReciprocalEstimate = reciprocalEstimate +Math.ReciprocalSqrtEstimate = reciprocalSqrtEstimate Math.Round = round +Math.ScaleB = scaleB Math.Sign = sign Math.Sin = math.sin +Math.SinCos = sinCos Math.Sinh = sinh -Math.Sqrt = math.sqrt +Math.Sqrt = sqrt Math.Tan = math.tan Math.Tanh = tanh Math.Truncate = truncate System.define("System.Math", Math) +System.define("System.MathF", Math) diff --git a/test/BridgeNetTests/Batch1/src/MathTests.cs b/test/BridgeNetTests/Batch1/src/MathTests.cs index 852cacd6..01ea5916 100644 --- a/test/BridgeNetTests/Batch1/src/MathTests.cs +++ b/test/BridgeNetTests/Batch1/src/MathTests.cs @@ -813,5 +813,260 @@ public void TruncateWithDecimalWorks() // // #1629 // Assert.AreEqual(Math.BigMul(214748364, 214748364), 46116859840676496L); //} + + [Test] + public void RoundDoubleAround0Works() + { + // Test around 0 for all rounding modes + NumberHelper.AssertDouble(-1, Math.Round(-0.7, 0, MidpointRounding.ToEven)); + NumberHelper.AssertDouble(0, Math.Round(-0.5, 0, MidpointRounding.ToEven)); + NumberHelper.AssertDouble(0, Math.Round(-0.3, 0, MidpointRounding.ToEven)); + NumberHelper.AssertDouble(0, Math.Round(0.3, 0, MidpointRounding.ToEven)); + NumberHelper.AssertDouble(0, Math.Round(0.5, 0, MidpointRounding.ToEven)); + NumberHelper.AssertDouble(1, Math.Round(0.7, 0, MidpointRounding.ToEven)); + + NumberHelper.AssertDouble(-1, Math.Round(-0.7, 0, MidpointRounding.AwayFromZero)); + NumberHelper.AssertDouble(-1, Math.Round(-0.5, 0, MidpointRounding.AwayFromZero)); + NumberHelper.AssertDouble(0, Math.Round(-0.3, 0, MidpointRounding.AwayFromZero)); + NumberHelper.AssertDouble(0, Math.Round(0.3, 0, MidpointRounding.AwayFromZero)); + NumberHelper.AssertDouble(1, Math.Round(0.5, 0, MidpointRounding.AwayFromZero)); + NumberHelper.AssertDouble(1, Math.Round(0.7, 0, MidpointRounding.AwayFromZero)); + + NumberHelper.AssertDouble(0, Math.Round(-0.7, 0, MidpointRounding.ToZero)); + NumberHelper.AssertDouble(0, Math.Round(-0.5, 0, MidpointRounding.ToZero)); + NumberHelper.AssertDouble(0, Math.Round(-0.3, 0, MidpointRounding.ToZero)); + NumberHelper.AssertDouble(0, Math.Round(0.3, 0, MidpointRounding.ToZero)); + NumberHelper.AssertDouble(0, Math.Round(0.5, 0, MidpointRounding.ToZero)); + NumberHelper.AssertDouble(0, Math.Round(0.7, 0, MidpointRounding.ToZero)); + + NumberHelper.AssertDouble(-1, Math.Round(-0.7, 0, MidpointRounding.ToNegativeInfinity)); + NumberHelper.AssertDouble(-1, Math.Round(-0.5, 0, MidpointRounding.ToNegativeInfinity)); + NumberHelper.AssertDouble(-1, Math.Round(-0.3, 0, MidpointRounding.ToNegativeInfinity)); + NumberHelper.AssertDouble(0, Math.Round(0.3, 0, MidpointRounding.ToNegativeInfinity)); + NumberHelper.AssertDouble(0, Math.Round(0.5, 0, MidpointRounding.ToNegativeInfinity)); + NumberHelper.AssertDouble(0, Math.Round(0.7, 0, MidpointRounding.ToNegativeInfinity)); + + NumberHelper.AssertDouble(0, Math.Round(-0.7, 0, MidpointRounding.ToPositiveInfinity)); + NumberHelper.AssertDouble(0, Math.Round(-0.5, 0, MidpointRounding.ToPositiveInfinity)); + NumberHelper.AssertDouble(0, Math.Round(-0.3, 0, MidpointRounding.ToPositiveInfinity)); + NumberHelper.AssertDouble(1, Math.Round(0.3, 0, MidpointRounding.ToPositiveInfinity)); + NumberHelper.AssertDouble(1, Math.Round(0.5, 0, MidpointRounding.ToPositiveInfinity)); + NumberHelper.AssertDouble(1, Math.Round(0.7, 0, MidpointRounding.ToPositiveInfinity)); + + // Test around the first even number as well + NumberHelper.AssertDouble(-2, Math.Round(-1.7, 0, MidpointRounding.ToEven)); + NumberHelper.AssertDouble(-2, Math.Round(-1.5, 0, MidpointRounding.ToEven)); + NumberHelper.AssertDouble(-1, Math.Round(-1.3, 0, MidpointRounding.ToEven)); + NumberHelper.AssertDouble(1, Math.Round(1.3, 0, MidpointRounding.ToEven)); + NumberHelper.AssertDouble(2, Math.Round(1.5, 0, MidpointRounding.ToEven)); + NumberHelper.AssertDouble(2, Math.Round(1.7, 0, MidpointRounding.ToEven)); + + NumberHelper.AssertDouble(-2, Math.Round(-1.7, 0, MidpointRounding.AwayFromZero)); + NumberHelper.AssertDouble(-2, Math.Round(-1.5, 0, MidpointRounding.AwayFromZero)); + NumberHelper.AssertDouble(-1, Math.Round(-1.3, 0, MidpointRounding.AwayFromZero)); + NumberHelper.AssertDouble(1, Math.Round(1.3, 0, MidpointRounding.AwayFromZero)); + NumberHelper.AssertDouble(2, Math.Round(1.5, 0, MidpointRounding.AwayFromZero)); + NumberHelper.AssertDouble(2, Math.Round(1.7, 0, MidpointRounding.AwayFromZero)); + + NumberHelper.AssertDouble(-1, Math.Round(-1.7, 0, MidpointRounding.ToZero)); + NumberHelper.AssertDouble(-1, Math.Round(-1.5, 0, MidpointRounding.ToZero)); + NumberHelper.AssertDouble(-1, Math.Round(-1.3, 0, MidpointRounding.ToZero)); + NumberHelper.AssertDouble(1, Math.Round(1.3, 0, MidpointRounding.ToZero)); + NumberHelper.AssertDouble(1, Math.Round(1.5, 0, MidpointRounding.ToZero)); + NumberHelper.AssertDouble(1, Math.Round(1.7, 0, MidpointRounding.ToZero)); + + NumberHelper.AssertDouble(-2, Math.Round(-1.7, 0, MidpointRounding.ToNegativeInfinity)); + NumberHelper.AssertDouble(-2, Math.Round(-1.5, 0, MidpointRounding.ToNegativeInfinity)); + NumberHelper.AssertDouble(-2, Math.Round(-1.3, 0, MidpointRounding.ToNegativeInfinity)); + NumberHelper.AssertDouble(1, Math.Round(1.3, 0, MidpointRounding.ToNegativeInfinity)); + NumberHelper.AssertDouble(1, Math.Round(1.5, 0, MidpointRounding.ToNegativeInfinity)); + NumberHelper.AssertDouble(1, Math.Round(1.7, 0, MidpointRounding.ToNegativeInfinity)); + + NumberHelper.AssertDouble(-1, Math.Round(-1.7, 0, MidpointRounding.ToPositiveInfinity)); + NumberHelper.AssertDouble(-1, Math.Round(-1.5, 0, MidpointRounding.ToPositiveInfinity)); + NumberHelper.AssertDouble(-1, Math.Round(-1.3, 0, MidpointRounding.ToPositiveInfinity)); + NumberHelper.AssertDouble(2, Math.Round(1.3, 0, MidpointRounding.ToPositiveInfinity)); + NumberHelper.AssertDouble(2, Math.Round(1.5, 0, MidpointRounding.ToPositiveInfinity)); + NumberHelper.AssertDouble(2, Math.Round(1.7, 0, MidpointRounding.ToPositiveInfinity)); + } + + [Test] + public void AcoshWorks() + { + Assert.AreEqual(double.NaN, Math.Acosh(0)); + Assert.AreEqual(double.NaN, Math.Acosh(0.5)); + NumberHelper.AssertDouble(0, Math.Acosh(1)); + NumberHelper.AssertDoubleWithEpsilon8(1.3169578969248166, Math.Acosh(2)); + NumberHelper.AssertDoubleWithEpsilon8(1.762747174039086, Math.Acosh(3)); + NumberHelper.AssertDoubleWithEpsilon8(2.0634370688955608, Math.Acosh(4)); + } + + [Test] + public void AsinhWorks() + { + NumberHelper.AssertDoubleWithEpsilon8(-1.4436354751788103, Math.Asinh(-2)); + NumberHelper.AssertDoubleWithEpsilon8(-0.881373587019543, Math.Asinh(-1)); + NumberHelper.AssertDouble(0, Math.Asinh(0)); + NumberHelper.AssertDoubleWithEpsilon8(0.881373587019543, Math.Asinh(1)); + NumberHelper.AssertDoubleWithEpsilon8(1.4436354751788103, Math.Asinh(2)); + NumberHelper.AssertDoubleWithEpsilon8(1.8184464592320668, Math.Asinh(3)); + NumberHelper.AssertDoubleWithEpsilon8(2.0947125472611012, Math.Asinh(4)); + } + + [Test] + public void AtanhWorks() + { + Assert.AreEqual(double.NegativeInfinity, Math.Atanh(-1)); + NumberHelper.AssertDoubleWithEpsilon8(-0.9729550745276566, Math.Atanh(-0.75)); + NumberHelper.AssertDouble(0, Math.Atanh(0)); + NumberHelper.AssertDoubleWithEpsilon8(0.5493061443340548, Math.Atanh(0.5)); + Assert.AreEqual(double.PositiveInfinity, Math.Atanh(1)); + } + + [Test] + public void CbrtWorks() + { + NumberHelper.AssertDoubleWithEpsilon8(-1.7099759466766968, Math.Cbrt(-5)); + NumberHelper.AssertDouble(0, Math.Cbrt(0)); + NumberHelper.AssertDoubleWithEpsilon8(0.7937005259840998, Math.Cbrt(0.5)); + NumberHelper.AssertDouble(1, Math.Cbrt(1)); + NumberHelper.AssertDoubleWithEpsilon8(2.1544346900318834, Math.Cbrt(10)); + NumberHelper.AssertDouble(3, Math.Cbrt(27)); + } + + [Test] + public void CopySignWorks() + { + NumberHelper.AssertDouble(5, Math.CopySign(5, 7)); + NumberHelper.AssertDouble(-5, Math.CopySign(5, -7)); + NumberHelper.AssertDouble(5, Math.CopySign(-5, 7)); + NumberHelper.AssertDouble(-5, Math.CopySign(-5, -7)); + + NumberHelper.AssertDouble(8, Math.CopySign(8, 4)); + NumberHelper.AssertDouble(-8, Math.CopySign(8, -4)); + NumberHelper.AssertDouble(8, Math.CopySign(-8, 4)); + NumberHelper.AssertDouble(-8, Math.CopySign(-8, -4)); + } + + [Test] + public void FusedMultipleAddWorks() + { + NumberHelper.AssertDouble(26, Math.FusedMultiplyAdd(4, 5, 6)); + NumberHelper.AssertDoubleWithEpsilon8(9.85, Math.FusedMultiplyAdd(2.5, 2.7, 3.1)); + NumberHelper.AssertDoubleWithEpsilon8(35.507, Math.FusedMultiplyAdd(6.18, 4.15, 9.86)); + } + + [Test] + public void ILogBWorks() + { + NumberHelper.AssertDouble(4, Math.ILogB(-27)); + NumberHelper.AssertDouble(1, Math.ILogB(-3)); + NumberHelper.AssertDouble(-14, Math.ILogB(-0.0001)); + NumberHelper.AssertDouble(int.MinValue, Math.ILogB(0)); + NumberHelper.AssertDouble(-8, Math.ILogB(0.005)); + NumberHelper.AssertDouble(2, Math.ILogB(6)); + NumberHelper.AssertDouble(9, Math.ILogB(581)); + } + + [Test] + public void Log2Works() + { + Assert.AreEqual(double.NaN, Math.Log2(-27)); + Assert.AreEqual(double.NaN, Math.Log2(-3)); + Assert.AreEqual(double.NaN, Math.Log2(-0.0001)); + Assert.AreEqual(double.NegativeInfinity, Math.Log2(0)); + NumberHelper.AssertDoubleWithEpsilon8(-7.643856189774724, Math.Log2(0.005)); + NumberHelper.AssertDoubleWithEpsilon8(2.584962500721156, Math.Log2(6)); + NumberHelper.AssertDoubleWithEpsilon8(9.18239435340453, Math.Log2(581)); + } + + [Test] + public void MaxMagnitudeWorks() + { + NumberHelper.AssertDouble(40, Math.MaxMagnitude(40, 40)); + NumberHelper.AssertDouble(40, Math.MaxMagnitude(40, -40)); + NumberHelper.AssertDouble(40, Math.MaxMagnitude(-40, 40)); + NumberHelper.AssertDouble(-40, Math.MaxMagnitude(-40, -40)); + + NumberHelper.AssertDouble(41, Math.MaxMagnitude(40, 41)); + NumberHelper.AssertDouble(-41, Math.MaxMagnitude(40, -41)); + NumberHelper.AssertDouble(41, Math.MaxMagnitude(-40, 41)); + NumberHelper.AssertDouble(-41, Math.MaxMagnitude(-40, -41)); + + NumberHelper.AssertDouble(41, Math.MaxMagnitude(41, 40)); + NumberHelper.AssertDouble(41, Math.MaxMagnitude(41, -40)); + NumberHelper.AssertDouble(-41, Math.MaxMagnitude(-41, 40)); + NumberHelper.AssertDouble(-41, Math.MaxMagnitude(-41, -40)); + } + + [Test] + public void MinMagnitudeWorks() + { + NumberHelper.AssertDouble(40, Math.MinMagnitude(40, 40)); + NumberHelper.AssertDouble(-40, Math.MinMagnitude(40, -40)); + NumberHelper.AssertDouble(-40, Math.MinMagnitude(-40, 40)); + NumberHelper.AssertDouble(-40, Math.MinMagnitude(-40, -40)); + + NumberHelper.AssertDouble(39, Math.MinMagnitude(40, 39)); + NumberHelper.AssertDouble(-39, Math.MinMagnitude(40, -39)); + NumberHelper.AssertDouble(39, Math.MinMagnitude(-40, 39)); + NumberHelper.AssertDouble(-39, Math.MinMagnitude(-40, -39)); + + NumberHelper.AssertDouble(39, Math.MinMagnitude(39, 40)); + NumberHelper.AssertDouble(39, Math.MinMagnitude(39, -40)); + NumberHelper.AssertDouble(-39, Math.MinMagnitude(-39, 40)); + NumberHelper.AssertDouble(-39, Math.MinMagnitude(-39, -40)); + } + + [Test] + public void ReciprocalEstimateWorks() + { + NumberHelper.AssertDoubleWithEpsilon8(-0.14285714285714285, Math.ReciprocalEstimate(-7)); + NumberHelper.AssertDouble(-2, Math.ReciprocalEstimate(-0.5)); + Assert.AreEqual(double.PositiveInfinity, Math.ReciprocalEstimate(0)); + NumberHelper.AssertDoubleWithEpsilon8(4.761904761904762, Math.ReciprocalEstimate(0.21)); + NumberHelper.AssertDouble(1, Math.ReciprocalEstimate(1)); + NumberHelper.AssertDoubleWithEpsilon8(0.3333333333333333, Math.ReciprocalEstimate(3)); + NumberHelper.AssertDoubleWithEpsilon8(0.0022172949002217295, Math.ReciprocalEstimate(451)); + } + + [Test] + public void ReciprocalSqrtEstimateWorks() + { + Assert.AreEqual(double.NaN, Math.ReciprocalSqrtEstimate(-7)); + Assert.AreEqual(double.NaN, Math.ReciprocalSqrtEstimate(-0.5)); + Assert.AreEqual(double.PositiveInfinity, Math.ReciprocalSqrtEstimate(0)); + NumberHelper.AssertDoubleWithEpsilon8(2.1821789023599236, Math.ReciprocalSqrtEstimate(0.21)); + NumberHelper.AssertDouble(1, Math.ReciprocalSqrtEstimate(1)); + NumberHelper.AssertDoubleWithEpsilon8(0.5773502691896258, Math.ReciprocalSqrtEstimate(3)); + NumberHelper.AssertDoubleWithEpsilon8(0.04708816093480111, Math.ReciprocalSqrtEstimate(451)); + } + + [Test] + public void ScaleBWorks() + { + NumberHelper.AssertDouble(56, Math.ScaleB(7, 3)); + NumberHelper.AssertDouble(9, Math.ScaleB(72, -3)); + NumberHelper.AssertDouble(-139264, Math.ScaleB(-17, 13)); + NumberHelper.AssertDoubleWithEpsilon8(-0.0032958984375, Math.ScaleB(-27, -13)); + } + + [Test] + public void SinCosWorks() + { + var (sin, cos) = Math.SinCos(5); + NumberHelper.AssertDoubleWithEpsilon8(-0.9589242746631385, sin); + NumberHelper.AssertDoubleWithEpsilon8(0.28366218546322625, cos); + + (sin, cos) = Math.SinCos(-3); + NumberHelper.AssertDoubleWithEpsilon8(-0.1411200080598672, sin); + NumberHelper.AssertDoubleWithEpsilon8(-0.9899924966004454, cos); + } + + [Test] + public void MathFWorks() + { + Assert.AreEqual(5, MathF.Abs(-5)); + } } }