From 6ef0ff8b7fe4352d11d186699ae3f8e51f21ce14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Sun, 18 Oct 2026 22:20:08 -0400 Subject: [PATCH 1/5] Support IsTrue/IsFalse in LightCompiler and fall back to Compile in LightBind --- .../Interpreter/LightCompiler.cs | 24 +++++++++++++++++++ .../Microsoft.Dynamic/Utils/DynamicUtils.cs | 13 +++++++--- .../Microsoft.Dynamic.Test/InterpreterTest.cs | 24 +++++++++++++++++++ 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/core/Microsoft.Dynamic/Interpreter/LightCompiler.cs b/src/core/Microsoft.Dynamic/Interpreter/LightCompiler.cs index 457ec851..f2ed6e5f 100644 --- a/src/core/Microsoft.Dynamic/Interpreter/LightCompiler.cs +++ b/src/core/Microsoft.Dynamic/Interpreter/LightCompiler.cs @@ -647,6 +647,26 @@ private void CompileNotExpression(UnaryExpression node) { } } + private void CompileUnaryBoolCheck(UnaryExpression node) { + Compile(node.Operand); + if (node.IsLifted) { + BranchLabel notNull = Instructions.MakeLabel(); + BranchLabel computed = Instructions.MakeLabel(); + + Instructions.EmitCoalescingBranch(notNull); + Instructions.EmitBranch(computed); + + Instructions.MarkLabel(notNull); + Instructions.EmitLoad(node.NodeType == ExpressionType.IsTrue); + Instructions.EmitEqual(typeof(bool)); + + Instructions.MarkLabel(computed); + } else { + Instructions.EmitLoad(node.NodeType == ExpressionType.IsTrue); + Instructions.EmitEqual(typeof(bool)); + } + } + private void CompileUnaryExpression(Expression expr) { var node = (UnaryExpression)expr; @@ -658,6 +678,10 @@ private void CompileUnaryExpression(Expression expr) { case ExpressionType.Not: CompileNotExpression(node); return; + case ExpressionType.IsTrue: + case ExpressionType.IsFalse: + CompileUnaryBoolCheck(node); + return; case ExpressionType.TypeAs: CompileTypeAsExpression(node); return; diff --git a/src/core/Microsoft.Dynamic/Utils/DynamicUtils.cs b/src/core/Microsoft.Dynamic/Utils/DynamicUtils.cs index 42f33617..bc61e9bd 100644 --- a/src/core/Microsoft.Dynamic/Utils/DynamicUtils.cs +++ b/src/core/Microsoft.Dynamic/Utils/DynamicUtils.cs @@ -127,11 +127,18 @@ private GenericInterpretedBinder() { } var binding = binder.Bind(targetMO, argsMO); - return CreateDelegate(binding, bindingInfo); + return CreateDelegate(binder, binding, bindingInfo); } - private T/*!*/ CreateDelegate(DynamicMetaObject/*!*/ binding, CachedBindingInfo/*!*/ bindingInfo) { - return Compile(binding, bindingInfo).LightCompile(Int32.MaxValue); + private T/*!*/ CreateDelegate(DynamicMetaObjectBinder/*!*/ binder, DynamicMetaObject/*!*/ binding, CachedBindingInfo/*!*/ bindingInfo) { + var lambda = Compile(binding, bindingInfo); + try { + return lambda.LightCompile(Int32.MaxValue); + } catch (NotImplementedException ex) { + // the interpreter doesn't support every expression, fall back to compiling the lambda + PerfTrack.NoteEvent(PerfTrack.Categories.BindingSlow, $"LightBind fallback {binder.GetType().Name}: {ex.Message}"); + return lambda.Compile(); + } } private Expression/*!*/ Compile(DynamicMetaObject/*!*/ obj, CachedBindingInfo/*!*/ bindingInfo) { diff --git a/tests/Microsoft.Dynamic.Test/InterpreterTest.cs b/tests/Microsoft.Dynamic.Test/InterpreterTest.cs index e258106f..f3d5e2a4 100644 --- a/tests/Microsoft.Dynamic.Test/InterpreterTest.cs +++ b/tests/Microsoft.Dynamic.Test/InterpreterTest.cs @@ -35,5 +35,29 @@ public void PopTest() { Scripting.Generation.CompilerHelpers.LightCompile(lambda).DynamicInvoke(); } + + [TestCase(ExpressionType.IsTrue, true)] + [TestCase(ExpressionType.IsTrue, false)] + [TestCase(ExpressionType.IsFalse, true)] + [TestCase(ExpressionType.IsFalse, false)] + public void UnaryBoolCheckTest(ExpressionType nodeType, bool value) { + var x = Expression.Parameter(typeof(bool), "x"); + var lambda = Expression.Lambda>(Expression.MakeUnary(nodeType, x, null), x); + + Assert.That(Scripting.Generation.CompilerHelpers.LightCompile(lambda)(value), Is.EqualTo(lambda.Compile()(value))); + } + + [TestCase(ExpressionType.IsTrue, true)] + [TestCase(ExpressionType.IsTrue, false)] + [TestCase(ExpressionType.IsTrue, null)] + [TestCase(ExpressionType.IsFalse, true)] + [TestCase(ExpressionType.IsFalse, false)] + [TestCase(ExpressionType.IsFalse, null)] + public void LiftedUnaryBoolCheckTest(ExpressionType nodeType, bool? value) { + var x = Expression.Parameter(typeof(bool?), "x"); + var lambda = Expression.Lambda>(Expression.MakeUnary(nodeType, x, null), x); + + Assert.That(Scripting.Generation.CompilerHelpers.LightCompile(lambda)(value), Is.EqualTo(lambda.Compile()(value))); + } } } From ac372e03e7b4487bde2c63eae5d830ba5886fce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Sun, 18 Oct 2026 23:31:23 -0400 Subject: [PATCH 2/5] Use DotNet source for some instructions --- .../Instructions/AddInstruction.cs | 283 --------- .../Instructions/DivInstruction.cs | 162 ----- .../Instructions/DotNet/.editorconfig | 9 + .../Instructions/DotNet/AddInstruction.cs | 316 ++++++++++ .../Instructions/DotNet/AndInstruction.cs | 191 ++++++ .../DotNet/DecrementInstruction.cs | 174 ++++++ .../Instructions/DotNet/DivInstruction.cs | 182 ++++++ .../Instructions/DotNet/EqualInstruction.cs | 558 ++++++++++++++++++ .../DotNet/ExclusiveOrInstruction.cs | 179 ++++++ .../DotNet/GreaterThanInstruction.cs | 320 ++++++++++ .../DotNet/GreaterThanOrEqualInstruction.cs | 320 ++++++++++ .../DotNet/IncrementInstruction.cs | 174 ++++++ .../Instructions/DotNet/LICENSE.TXT | 23 + .../DotNet/LeftShiftInstruction.cs | 178 ++++++ .../DotNet/LessThanInstruction.cs | 319 ++++++++++ .../DotNet/LessThanOrEqualInstruction.cs | 320 ++++++++++ .../Instructions/DotNet/ModuloInstruction.cs | 182 ++++++ .../Instructions/DotNet/MulInstruction.cs | 317 ++++++++++ .../Instructions/DotNet/NegateInstruction.cs | 194 ++++++ .../DotNet/NotEqualInstruction.cs | 540 +++++++++++++++++ .../Instructions/DotNet/NotInstruction.cs | 188 ++++++ .../Instructions/DotNet/OrInstruction.cs | 195 ++++++ .../Interpreter/Instructions/DotNet/README.md | 38 ++ .../DotNet/RightShiftInstruction.cs | 180 ++++++ .../Instructions/DotNet/SubInstruction.cs | 316 ++++++++++ .../Instructions/EqualInstruction.cs | 146 ----- .../Instructions/GreaterThanInstruction.cs | 132 ----- .../Interpreter/Instructions/Instruction.cs | 15 - .../Instructions/InstructionList.cs | 108 ++-- .../Instructions/LessThanInstruction.cs | 251 -------- .../Instructions/NotEqualInstruction.cs | 146 ----- .../Interpreter/Instructions/Utils.cs | 17 + .../Interpreter/InterpretedFrame.cs | 16 + .../Interpreter/LightCompiler.cs | 113 +++- .../Microsoft.Dynamic.csproj | 6 + .../Microsoft.Dynamic/Utils/ContractUtils.cs | 2 + src/core/Microsoft.Dynamic/Utils/TypeUtils.cs | 14 +- .../Microsoft.Dynamic.Test/InterpreterTest.cs | 209 +++++++ 38 files changed, 5833 insertions(+), 1200 deletions(-) delete mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/AddInstruction.cs delete mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DivInstruction.cs create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/.editorconfig create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/AddInstruction.cs create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/AndInstruction.cs create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/DecrementInstruction.cs create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/DivInstruction.cs create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/EqualInstruction.cs create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/ExclusiveOrInstruction.cs create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/GreaterThanInstruction.cs create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/GreaterThanOrEqualInstruction.cs create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/IncrementInstruction.cs create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/LICENSE.TXT create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/LeftShiftInstruction.cs create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/LessThanInstruction.cs create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/LessThanOrEqualInstruction.cs create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/ModuloInstruction.cs create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/MulInstruction.cs create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/NegateInstruction.cs create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/NotEqualInstruction.cs create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/NotInstruction.cs create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/OrInstruction.cs create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/README.md create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/RightShiftInstruction.cs create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/SubInstruction.cs delete mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/EqualInstruction.cs delete mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/GreaterThanInstruction.cs delete mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/LessThanInstruction.cs delete mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/NotEqualInstruction.cs create mode 100644 src/core/Microsoft.Dynamic/Interpreter/Instructions/Utils.cs diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/AddInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/AddInstruction.cs deleted file mode 100644 index e04d409f..00000000 --- a/src/core/Microsoft.Dynamic/Interpreter/Instructions/AddInstruction.cs +++ /dev/null @@ -1,283 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics; - -using Microsoft.Scripting.Runtime; -using Microsoft.Scripting.Utils; - -namespace Microsoft.Scripting.Interpreter { - internal abstract class AddInstruction : Instruction { - private static Instruction _Int16, _Int32, _Int64, _UInt16, _UInt32, _UInt64, _Single, _Double; - - public override int ConsumedStack => 2; - public override int ProducedStack => 1; - - private AddInstruction() { - } - - private sealed class AddInt16 : AddInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = right is null ? null : (object)unchecked((short)((short)left + (short)right)); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - private sealed class AddInt32 : AddInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = right is null - ? null - : ScriptingRuntimeHelpers.Int32ToObject(unchecked((int)left + (int)right)); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - private sealed class AddInt64 : AddInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = right is null ? null : (object)unchecked((long)left + (long)right); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - private sealed class AddUInt16 : AddInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = right is null ? null : (object)unchecked((ushort)((ushort)left + (ushort)right)); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - private sealed class AddUInt32 : AddInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = right is null ? null : (object)unchecked((uint)left + (uint)right); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - private sealed class AddUInt64 : AddInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = right is null ? null : (object)unchecked((ulong)left + (ulong)right); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - private sealed class AddSingle : AddInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = right is null ? null : (object)((float)left + (float)right); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - private sealed class AddDouble : AddInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = right is null ? null : (object)((double)left + (double)right); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - public static Instruction Create(Type type) { - Debug.Assert(type.IsArithmetic()); - switch (type.GetNonNullableType().GetTypeCode()) { - case TypeCode.Int16: return _Int16 ?? (_Int16 = new AddInt16()); - case TypeCode.Int32: return _Int32 ?? (_Int32 = new AddInt32()); - case TypeCode.Int64: return _Int64 ?? (_Int64 = new AddInt64()); - case TypeCode.UInt16: return _UInt16 ?? (_UInt16 = new AddUInt16()); - case TypeCode.UInt32: return _UInt32 ?? (_UInt32 = new AddUInt32()); - case TypeCode.UInt64: return _UInt64 ?? (_UInt64 = new AddUInt64()); - case TypeCode.Single: return _Single ?? (_Single = new AddSingle()); - case TypeCode.Double: return _Double ?? (_Double = new AddDouble()); - default: - throw Assert.Unreachable; - } - } - - public override string ToString() { - return "Add()"; - } - } - - internal abstract class AddOvfInstruction : Instruction { - private static Instruction _Int16, _Int32, _Int64, _UInt16, _UInt32, _UInt64; - - public override int ConsumedStack => 2; - public override int ProducedStack => 1; - - private AddOvfInstruction() { - } - - private sealed class AddOvfInt16 : AddOvfInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = right is null ? null : (object)checked((short)((short)left + (short)right)); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - private sealed class AddOvfInt32 : AddOvfInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = right is null - ? null - : ScriptingRuntimeHelpers.Int32ToObject(checked((int)left + (int)right)); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - private sealed class AddOvfInt64 : AddOvfInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = right is null ? null : (object)checked((long)left + (long)right); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - private sealed class AddOvfUInt16 : AddOvfInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = right is null ? null : (object)checked((ushort)((ushort)left + (ushort)right)); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - private sealed class AddOvfUInt32 : AddOvfInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = right is null ? null : (object)checked((uint)left + (uint)right); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - private sealed class AddOvfUInt64 : AddOvfInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = right is null ? null : (object)checked((ulong)left + (ulong)right); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - public static Instruction Create(Type type) { - Debug.Assert(type.IsArithmetic()); - switch (type.GetNonNullableType().GetTypeCode()) { - case TypeCode.Int16: return _Int16 ?? (_Int16 = new AddOvfInt16()); - case TypeCode.Int32: return _Int32 ?? (_Int32 = new AddOvfInt32()); - case TypeCode.Int64: return _Int64 ?? (_Int64 = new AddOvfInt64()); - case TypeCode.UInt16: return _UInt16 ?? (_UInt16 = new AddOvfUInt16()); - case TypeCode.UInt32: return _UInt32 ?? (_UInt32 = new AddOvfUInt32()); - case TypeCode.UInt64: return _UInt64 ?? (_UInt64 = new AddOvfUInt64()); - default: - return AddInstruction.Create(type); - } - } - - public override string ToString() { - return "AddOvf()"; - } - } -} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DivInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DivInstruction.cs deleted file mode 100644 index dc3f8aa1..00000000 --- a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DivInstruction.cs +++ /dev/null @@ -1,162 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics; - -using Microsoft.Scripting.Runtime; -using Microsoft.Scripting.Utils; - -namespace Microsoft.Scripting.Interpreter { - internal abstract class DivInstruction : Instruction { - private static Instruction _Int16, _Int32, _Int64, _UInt16, _UInt32, _UInt64, _Single, _Double; - - public override int ConsumedStack => 2; - public override int ProducedStack => 1; - - private DivInstruction() { - } - - private sealed class DivInt16 : DivInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = right is null ? null : (object)unchecked((short)((short)left / (short)right)); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - private sealed class DivInt32 : DivInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = - right is null ? null : ScriptingRuntimeHelpers.Int32ToObject((int)left / (int)right); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - private sealed class DivInt64 : DivInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = right is null ? null : (object)((long)left / (long)right); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - private sealed class DivUInt16 : DivInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = right is null ? null : (object)unchecked((ushort)((ushort)left / (ushort)right)); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - private sealed class DivUInt32 : DivInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = right is null ? null : (object)((uint)left / (uint)right); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - private sealed class DivUInt64 : DivInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = right is null ? null : (object)((ulong)left / (ulong)right); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - private sealed class DivSingle : DivInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = right is null ? null : (object)((float)left / (float)right); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - private sealed class DivDouble : DivInstruction { - public override int Run(InterpretedFrame frame) { - int index = frame.StackIndex; - object[] stack = frame.Data; - object left = stack[index - 2]; - if (left is not null) { - object right = stack[index - 1]; - stack[index - 2] = right is null ? null : (object)((double)left / (double)right); - } - - frame.StackIndex = index - 1; - return 1; - } - } - - public static Instruction Create(Type type) { - Debug.Assert(!type.IsEnum); - switch (type.GetNonNullableType().GetTypeCode()) { - case TypeCode.Int16: return _Int16 ?? (_Int16 = new DivInt16()); - case TypeCode.Int32: return _Int32 ?? (_Int32 = new DivInt32()); - case TypeCode.Int64: return _Int64 ?? (_Int64 = new DivInt64()); - case TypeCode.UInt16: return _UInt16 ?? (_UInt16 = new DivUInt16()); - case TypeCode.UInt32: return _UInt32 ?? (_UInt32 = new DivUInt32()); - case TypeCode.UInt64: return _UInt64 ?? (_UInt64 = new DivUInt64()); - case TypeCode.Single: return _Single ?? (_Single = new DivSingle()); - case TypeCode.Double: return _Double ?? (_Double = new DivDouble()); - default: - throw Assert.Unreachable; - } - } - - public override string ToString() { - return "Div()"; - } - } -} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/.editorconfig b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/.editorconfig new file mode 100644 index 00000000..7ec4aff4 --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +trim_trailing_whitespace = false +insert_final_newline = false + +[*.cs] +# leave upstream code formatting and style intact +generated_code = true diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/AddInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/AddInstruction.cs new file mode 100644 index 00000000..79d1b8e0 --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/AddInstruction.cs @@ -0,0 +1,316 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.Diagnostics; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpreter +{ + internal abstract class AddInstruction : Instruction + { + private static Instruction? s_Int16, s_Int32, s_Int64, s_UInt16, s_UInt32, s_UInt64, s_Single, s_Double; + + public override int ConsumedStack => 2; + public override int ProducedStack => 1; + public override string InstructionName => "Add"; + + private AddInstruction() { } + + private sealed class AddInt16 : AddInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)unchecked((short)((short)left + (short)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class AddInt32 : AddInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : ScriptingRuntimeHelpers.Int32ToObject(unchecked((int)left + (int)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class AddInt64 : AddInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)unchecked((long)left + (long)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class AddUInt16 : AddInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)unchecked((ushort)((ushort)left + (ushort)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class AddUInt32 : AddInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)unchecked((uint)left + (uint)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class AddUInt64 : AddInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)unchecked((ulong)left + (ulong)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class AddSingle : AddInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)((float)left + (float)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class AddDouble : AddInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)((double)left + (double)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + public static Instruction Create(Type type) + { + Debug.Assert(type.IsArithmetic()); + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.Int16 => s_Int16 ??= new AddInt16(), + TypeCode.Int32 => s_Int32 ??= new AddInt32(), + TypeCode.Int64 => s_Int64 ??= new AddInt64(), + TypeCode.UInt16 => s_UInt16 ??= new AddUInt16(), + TypeCode.UInt32 => s_UInt32 ??= new AddUInt32(), + TypeCode.UInt64 => s_UInt64 ??= new AddUInt64(), + TypeCode.Single => s_Single ??= new AddSingle(), + TypeCode.Double => s_Double ??= new AddDouble(), + _ => throw ContractUtils.Unreachable, + }; + } + } + + internal abstract class AddOvfInstruction : Instruction + { + private static Instruction? s_Int16, s_Int32, s_Int64, s_UInt16, s_UInt32, s_UInt64; + + public override int ConsumedStack => 2; + public override int ProducedStack => 1; + public override string InstructionName => "AddOvf"; + + private AddOvfInstruction() { } + + private sealed class AddOvfInt16 : AddOvfInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)checked((short)((short)left + (short)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class AddOvfInt32 : AddOvfInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : ScriptingRuntimeHelpers.Int32ToObject(checked((int)left + (int)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class AddOvfInt64 : AddOvfInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)checked((long)left + (long)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class AddOvfUInt16 : AddOvfInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)checked((ushort)((ushort)left + (ushort)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class AddOvfUInt32 : AddOvfInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)checked((uint)left + (uint)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class AddOvfUInt64 : AddOvfInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)checked((ulong)left + (ulong)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + public static Instruction Create(Type type) + { + Debug.Assert(type.IsArithmetic()); + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.Int16 => s_Int16 ??= new AddOvfInt16(), + TypeCode.Int32 => s_Int32 ??= new AddOvfInt32(), + TypeCode.Int64 => s_Int64 ??= new AddOvfInt64(), + TypeCode.UInt16 => s_UInt16 ??= new AddOvfUInt16(), + TypeCode.UInt32 => s_UInt32 ??= new AddOvfUInt32(), + TypeCode.UInt64 => s_UInt64 ??= new AddOvfUInt64(), + _ => AddInstruction.Create(type), + }; + } + } +} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/AndInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/AndInstruction.cs new file mode 100644 index 00000000..37b0f013 --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/AndInstruction.cs @@ -0,0 +1,191 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpreter +{ + internal abstract class AndInstruction : Instruction + { + private static Instruction? s_SByte, s_Int16, s_Int32, s_Int64, s_Byte, s_UInt16, s_UInt32, s_UInt64, s_Boolean; + + public override int ConsumedStack => 2; + public override int ProducedStack => 1; + public override string InstructionName => "And"; + + private AndInstruction() { } + + private sealed class AndSByte : AndInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((sbyte)((sbyte)left & (sbyte)right)); + return 1; + } + } + + private sealed class AndInt16 : AndInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((short)((short)left & (short)right)); + return 1; + } + } + + private sealed class AndInt32 : AndInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((int)left & (int)right); + return 1; + } + } + + private sealed class AndInt64 : AndInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((long)left & (long)right); + return 1; + } + } + + private sealed class AndByte : AndInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((byte)((byte)left & (byte)right)); + return 1; + } + } + + private sealed class AndUInt16 : AndInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((ushort)((ushort)left & (ushort)right)); + return 1; + } + } + + private sealed class AndUInt32 : AndInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((uint)left & (uint)right); + return 1; + } + } + + private sealed class AndUInt64 : AndInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((ulong)left & (ulong)right); + return 1; + } + } + + private sealed class AndBoolean : AndInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + if (right == null) + { + frame.Push(null); + } + else + { + frame.Push((bool)right ? null : Utils.BoxedFalse); + } + return 1; + } + else if (right == null) + { + frame.Push((bool)left ? null : Utils.BoxedFalse); + return 1; + } + frame.Push((bool)left & (bool)right); + return 1; + } + } + + public static Instruction Create(Type type) => + type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.SByte => s_SByte ??= new AndSByte(), + TypeCode.Int16 => s_Int16 ??= new AndInt16(), + TypeCode.Int32 => s_Int32 ??= new AndInt32(), + TypeCode.Int64 => s_Int64 ??= new AndInt64(), + TypeCode.Byte => s_Byte ??= new AndByte(), + TypeCode.UInt16 => s_UInt16 ??= new AndUInt16(), + TypeCode.UInt32 => s_UInt32 ??= new AndUInt32(), + TypeCode.UInt64 => s_UInt64 ??= new AndUInt64(), + TypeCode.Boolean => s_Boolean ??= new AndBoolean(), + _ => throw ContractUtils.Unreachable, + }; + } +} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/DecrementInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/DecrementInstruction.cs new file mode 100644 index 00000000..52ff671c --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/DecrementInstruction.cs @@ -0,0 +1,174 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.Diagnostics; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpreter +{ + internal abstract class DecrementInstruction : Instruction + { + private static Instruction? s_Int16, s_Int32, s_Int64, s_UInt16, s_UInt32, s_UInt64, s_Single, s_Double; + + public override int ConsumedStack => 1; + public override int ProducedStack => 1; + public override string InstructionName => "Decrement"; + + private DecrementInstruction() { } + + private sealed class DecrementInt16 : DecrementInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked((short)((short)obj - 1))); + } + return 1; + } + } + + private sealed class DecrementInt32 : DecrementInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked((int)obj - 1)); + } + return 1; + } + } + + private sealed class DecrementInt64 : DecrementInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked((long)obj - 1)); + } + return 1; + } + } + + private sealed class DecrementUInt16 : DecrementInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked((ushort)((ushort)obj - 1))); + } + return 1; + } + } + + private sealed class DecrementUInt32 : DecrementInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked((uint)obj - 1)); + } + return 1; + } + } + + private sealed class DecrementUInt64 : DecrementInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked((ulong)obj - 1)); + } + return 1; + } + } + + private sealed class DecrementSingle : DecrementInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked((float)obj - 1)); + } + return 1; + } + } + + private sealed class DecrementDouble : DecrementInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked((double)obj - 1)); + } + return 1; + } + } + + public static Instruction Create(Type type) + { + Debug.Assert(!type.IsEnum); + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.Int16 => s_Int16 ??= new DecrementInt16(), + TypeCode.Int32 => s_Int32 ??= new DecrementInt32(), + TypeCode.Int64 => s_Int64 ??= new DecrementInt64(), + TypeCode.UInt16 => s_UInt16 ??= new DecrementUInt16(), + TypeCode.UInt32 => s_UInt32 ??= new DecrementUInt32(), + TypeCode.UInt64 => s_UInt64 ??= new DecrementUInt64(), + TypeCode.Single => s_Single ??= new DecrementSingle(), + TypeCode.Double => s_Double ??= new DecrementDouble(), + _ => throw ContractUtils.Unreachable, + }; + } + } +} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/DivInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/DivInstruction.cs new file mode 100644 index 00000000..1662dfc1 --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/DivInstruction.cs @@ -0,0 +1,182 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.Diagnostics; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpreter +{ + internal abstract class DivInstruction : Instruction + { + private static Instruction? s_Int16, s_Int32, s_Int64, s_UInt16, s_UInt32, s_UInt64, s_Single, s_Double; + + public override int ConsumedStack => 2; + public override int ProducedStack => 1; + public override string InstructionName => "Div"; + + private DivInstruction() { } + + private sealed class DivInt16 : DivInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)unchecked((short)((short)left / (short)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class DivInt32 : DivInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : ScriptingRuntimeHelpers.Int32ToObject((int)left / (int)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class DivInt64 : DivInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)((long)left / (long)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class DivUInt16 : DivInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)unchecked((ushort)((ushort)left / (ushort)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class DivUInt32 : DivInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)((uint)left / (uint)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class DivUInt64 : DivInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)((ulong)left / (ulong)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class DivSingle : DivInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)((float)left / (float)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class DivDouble : DivInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)((double)left / (double)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + public static Instruction Create(Type type) + { + Debug.Assert(!type.IsEnum); + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.Int16 => s_Int16 ??= new DivInt16(), + TypeCode.Int32 => s_Int32 ??= new DivInt32(), + TypeCode.Int64 => s_Int64 ??= new DivInt64(), + TypeCode.UInt16 => s_UInt16 ??= new DivUInt16(), + TypeCode.UInt32 => s_UInt32 ??= new DivUInt32(), + TypeCode.UInt64 => s_UInt64 ??= new DivUInt64(), + TypeCode.Single => s_Single ??= new DivSingle(), + TypeCode.Double => s_Double ??= new DivDouble(), + _ => throw ContractUtils.Unreachable, + }; + } + } +} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/EqualInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/EqualInstruction.cs new file mode 100644 index 00000000..9d448705 --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/EqualInstruction.cs @@ -0,0 +1,558 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.Diagnostics; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpreter +{ + internal abstract class EqualInstruction : Instruction + { + // Perf: EqualityComparer but is 3/2 to 2 times slower. + private static Instruction? s_reference, s_Boolean, s_SByte, s_Int16, s_Char, s_Int32, s_Int64, s_Byte, s_UInt16, s_UInt32, s_UInt64, s_Single, s_Double; + private static Instruction? s_BooleanLiftedToNull, s_SByteLiftedToNull, s_Int16LiftedToNull, s_CharLiftedToNull, s_Int32LiftedToNull, s_Int64LiftedToNull, s_ByteLiftedToNull, s_UInt16LiftedToNull, s_UInt32LiftedToNull, s_UInt64LiftedToNull, s_SingleLiftedToNull, s_DoubleLiftedToNull; + + public override int ConsumedStack => 2; + public override int ProducedStack => 1; + public override string InstructionName => "Equal"; + + private EqualInstruction() { } + + private sealed class EqualBoolean : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right == null); + } + else if (right == null) + { + frame.Push(false); + } + else + { + frame.Push((bool)left == (bool)right); + } + return 1; + } + } + + private sealed class EqualSByte : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right == null); + } + else if (right == null) + { + frame.Push(false); + } + else + { + frame.Push((sbyte)left == (sbyte)right); + } + return 1; + } + } + + private sealed class EqualInt16 : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right == null); + } + else if (right == null) + { + frame.Push(false); + } + else + { + frame.Push((short)left == (short)right); + } + return 1; + } + } + + private sealed class EqualChar : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right == null); + } + else if (right == null) + { + frame.Push(false); + } + else + { + frame.Push((char)left == (char)right); + } + return 1; + } + } + + private sealed class EqualInt32 : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right == null); + } + else if (right == null) + { + frame.Push(false); + } + else + { + frame.Push((int)left == (int)right); + } + return 1; + } + } + + private sealed class EqualInt64 : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right == null); + } + else if (right == null) + { + frame.Push(false); + } + else + { + frame.Push((long)left == (long)right); + } + return 1; + } + } + + private sealed class EqualByte : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right == null); + } + else if (right == null) + { + frame.Push(false); + } + else + { + frame.Push((byte)left == (byte)right); + } + return 1; + } + } + + private sealed class EqualUInt16 : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right == null); + } + else if (right == null) + { + frame.Push(false); + } + else + { + frame.Push((ushort)left == (ushort)right); + } + return 1; + } + } + + private sealed class EqualUInt32 : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right == null); + } + else if (right == null) + { + frame.Push(false); + } + else + { + frame.Push((uint)left == (uint)right); + } + return 1; + } + } + + private sealed class EqualUInt64 : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right == null); + } + else if (right == null) + { + frame.Push(false); + } + else + { + frame.Push((ulong)left == (ulong)right); + } + return 1; + } + } + + private sealed class EqualSingle : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right == null); + } + else if (right == null) + { + frame.Push(false); + } + else + { + frame.Push((float)left == (float)right); + } + return 1; + } + } + + private sealed class EqualDouble : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right == null); + } + else if (right == null) + { + frame.Push(false); + } + else + { + frame.Push((double)left == (double)right); + } + return 1; + } + } + + private sealed class EqualReference : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + frame.Push(frame.Pop() == frame.Pop()); + return 1; + } + } + + private sealed class EqualBooleanLiftedToNull : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((bool)left == (bool)right); + } + return 1; + } + } + + private sealed class EqualSByteLiftedToNull : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((sbyte)left == (sbyte)right); + } + return 1; + } + } + + private sealed class EqualInt16LiftedToNull : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((short)left == (short)right); + } + return 1; + } + } + + private sealed class EqualCharLiftedToNull : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((char)left == (char)right); + } + return 1; + } + } + + private sealed class EqualInt32LiftedToNull : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((int)left == (int)right); + } + return 1; + } + } + + private sealed class EqualInt64LiftedToNull : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((long)left == (long)right); + } + return 1; + } + } + + private sealed class EqualByteLiftedToNull : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((byte)left == (byte)right); + } + return 1; + } + } + + private sealed class EqualUInt16LiftedToNull : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((ushort)left == (ushort)right); + } + return 1; + } + } + + private sealed class EqualUInt32LiftedToNull : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((uint)left == (uint)right); + } + return 1; + } + } + + private sealed class EqualUInt64LiftedToNull : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((ulong)left == (ulong)right); + } + return 1; + } + } + + private sealed class EqualSingleLiftedToNull : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((float)left == (float)right); + } + return 1; + } + } + + private sealed class EqualDoubleLiftedToNull : EqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((double)left == (double)right); + } + return 1; + } + } + + public static Instruction Create(Type type, bool liftedToNull) + { + if (liftedToNull) + { + switch (type.GetNonNullableType().GetTypeCode()) + { + case TypeCode.Boolean: return s_BooleanLiftedToNull ??= new EqualBooleanLiftedToNull(); + case TypeCode.SByte: return s_SByteLiftedToNull ??= new EqualSByteLiftedToNull(); + case TypeCode.Int16: return s_Int16LiftedToNull ??= new EqualInt16LiftedToNull(); + case TypeCode.Char: return s_CharLiftedToNull ??= new EqualCharLiftedToNull(); + case TypeCode.Int32: return s_Int32LiftedToNull ??= new EqualInt32LiftedToNull(); + case TypeCode.Int64: return s_Int64LiftedToNull ??= new EqualInt64LiftedToNull(); + case TypeCode.Byte: return s_ByteLiftedToNull ??= new EqualByteLiftedToNull(); + case TypeCode.UInt16: return s_UInt16LiftedToNull ??= new EqualUInt16LiftedToNull(); + case TypeCode.UInt32: return s_UInt32LiftedToNull ??= new EqualUInt32LiftedToNull(); + case TypeCode.UInt64: return s_UInt64LiftedToNull ??= new EqualUInt64LiftedToNull(); + case TypeCode.Single: return s_SingleLiftedToNull ??= new EqualSingleLiftedToNull(); + default: + Debug.Assert(type.GetNonNullableType().GetTypeCode() == TypeCode.Double); + return s_DoubleLiftedToNull ??= new EqualDoubleLiftedToNull(); + } + } + else + { + switch (type.GetNonNullableType().GetTypeCode()) + { + case TypeCode.Boolean: return s_Boolean ??= new EqualBoolean(); + case TypeCode.SByte: return s_SByte ??= new EqualSByte(); + case TypeCode.Int16: return s_Int16 ??= new EqualInt16(); + case TypeCode.Char: return s_Char ??= new EqualChar(); + case TypeCode.Int32: return s_Int32 ??= new EqualInt32(); + case TypeCode.Int64: return s_Int64 ??= new EqualInt64(); + case TypeCode.Byte: return s_Byte ??= new EqualByte(); + case TypeCode.UInt16: return s_UInt16 ??= new EqualUInt16(); + case TypeCode.UInt32: return s_UInt32 ??= new EqualUInt32(); + case TypeCode.UInt64: return s_UInt64 ??= new EqualUInt64(); + case TypeCode.Single: return s_Single ??= new EqualSingle(); + case TypeCode.Double: return s_Double ??= new EqualDouble(); + default: + // Nullable only valid if one operand is constant null, so this assert is slightly too broad. + Debug.Assert(type.IsNullableOrReferenceType()); + return s_reference ??= new EqualReference(); + } + } + } + } +} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/ExclusiveOrInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/ExclusiveOrInstruction.cs new file mode 100644 index 00000000..15400dd6 --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/ExclusiveOrInstruction.cs @@ -0,0 +1,179 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpreter +{ + internal abstract class ExclusiveOrInstruction : Instruction + { + private static Instruction? s_SByte, s_Int16, s_Int32, s_Int64, s_Byte, s_UInt16, s_UInt32, s_UInt64, s_Boolean; + + public override int ConsumedStack => 2; + public override int ProducedStack => 1; + public override string InstructionName => "ExclusiveOr"; + + private ExclusiveOrInstruction() { } + + private sealed class ExclusiveOrSByte : ExclusiveOrInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((sbyte)((sbyte)left ^ (sbyte)right)); + return 1; + } + } + + private sealed class ExclusiveOrInt16 : ExclusiveOrInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((short)((short)left ^ (short)right)); + return 1; + } + } + + private sealed class ExclusiveOrInt32 : ExclusiveOrInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((int)left ^ (int)right); + return 1; + } + } + + private sealed class ExclusiveOrInt64 : ExclusiveOrInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((long)left ^ (long)right); + return 1; + } + } + + private sealed class ExclusiveOrByte : ExclusiveOrInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((byte)((byte)left ^ (byte)right)); + return 1; + } + } + + private sealed class ExclusiveOrUInt16 : ExclusiveOrInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((ushort)((ushort)left ^ (ushort)right)); + return 1; + } + } + + private sealed class ExclusiveOrUInt32 : ExclusiveOrInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((uint)left ^ (uint)right); + return 1; + } + } + + private sealed class ExclusiveOrUInt64 : ExclusiveOrInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((ulong)left ^ (ulong)right); + return 1; + } + } + + private sealed class ExclusiveOrBoolean : ExclusiveOrInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((bool)left ^ (bool)right); + return 1; + } + } + + public static Instruction Create(Type type) => + type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.SByte => s_SByte ??= new ExclusiveOrSByte(), + TypeCode.Int16 => s_Int16 ??= new ExclusiveOrInt16(), + TypeCode.Int32 => s_Int32 ??= new ExclusiveOrInt32(), + TypeCode.Int64 => s_Int64 ??= new ExclusiveOrInt64(), + TypeCode.Byte => s_Byte ??= new ExclusiveOrByte(), + TypeCode.UInt16 => s_UInt16 ??= new ExclusiveOrUInt16(), + TypeCode.UInt32 => s_UInt32 ??= new ExclusiveOrUInt32(), + TypeCode.UInt64 => s_UInt64 ??= new ExclusiveOrUInt64(), + TypeCode.Boolean => s_Boolean ??= new ExclusiveOrBoolean(), + _ => throw ContractUtils.Unreachable, + }; + } +} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/GreaterThanInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/GreaterThanInstruction.cs new file mode 100644 index 00000000..ae4235ae --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/GreaterThanInstruction.cs @@ -0,0 +1,320 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.Diagnostics; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpreter +{ + internal abstract class GreaterThanInstruction : Instruction + { + private readonly object? _nullValue; + private static Instruction? s_SByte, s_Int16, s_Char, s_Int32, s_Int64, s_Byte, s_UInt16, s_UInt32, s_UInt64, s_Single, s_Double; + private static Instruction? s_liftedToNullSByte, s_liftedToNullInt16, s_liftedToNullChar, s_liftedToNullInt32, s_liftedToNullInt64, s_liftedToNullByte, s_liftedToNullUInt16, s_liftedToNullUInt32, s_liftedToNullUInt64, s_liftedToNullSingle, s_liftedToNullDouble; + + public override int ConsumedStack => 2; + public override int ProducedStack => 1; + public override string InstructionName => "GreaterThan"; + + private GreaterThanInstruction(object? nullValue) + { + _nullValue = nullValue; + } + + private sealed class GreaterThanSByte : GreaterThanInstruction + { + public GreaterThanSByte(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((sbyte)left > (sbyte)right); + } + return 1; + } + } + + private sealed class GreaterThanInt16 : GreaterThanInstruction + { + public GreaterThanInt16(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((short)left > (short)right); + } + return 1; + } + } + + private sealed class GreaterThanChar : GreaterThanInstruction + { + public GreaterThanChar(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((char)left > (char)right); + } + return 1; + } + } + + private sealed class GreaterThanInt32 : GreaterThanInstruction + { + public GreaterThanInt32(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((int)left > (int)right); + } + return 1; + } + } + + private sealed class GreaterThanInt64 : GreaterThanInstruction + { + public GreaterThanInt64(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((long)left > (long)right); + } + return 1; + } + } + + private sealed class GreaterThanByte : GreaterThanInstruction + { + public GreaterThanByte(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((byte)left > (byte)right); + } + return 1; + } + } + + private sealed class GreaterThanUInt16 : GreaterThanInstruction + { + public GreaterThanUInt16(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((ushort)left > (ushort)right); + } + return 1; + } + } + + private sealed class GreaterThanUInt32 : GreaterThanInstruction + { + public GreaterThanUInt32(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((uint)left > (uint)right); + } + return 1; + } + } + + private sealed class GreaterThanUInt64 : GreaterThanInstruction + { + public GreaterThanUInt64(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((ulong)left > (ulong)right); + } + return 1; + } + } + + private sealed class GreaterThanSingle : GreaterThanInstruction + { + public GreaterThanSingle(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((float)left > (float)right); + } + return 1; + } + } + + private sealed class GreaterThanDouble : GreaterThanInstruction + { + public GreaterThanDouble(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((double)left > (double)right); + } + return 1; + } + } + + public static Instruction Create(Type type, bool liftedToNull = false) + { + Debug.Assert(!type.IsEnum); + if (liftedToNull) + { + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.SByte => s_liftedToNullSByte ??= new GreaterThanSByte(null), + TypeCode.Int16 => s_liftedToNullInt16 ??= new GreaterThanInt16(null), + TypeCode.Char => s_liftedToNullChar ??= new GreaterThanChar(null), + TypeCode.Int32 => s_liftedToNullInt32 ??= new GreaterThanInt32(null), + TypeCode.Int64 => s_liftedToNullInt64 ??= new GreaterThanInt64(null), + TypeCode.Byte => s_liftedToNullByte ??= new GreaterThanByte(null), + TypeCode.UInt16 => s_liftedToNullUInt16 ??= new GreaterThanUInt16(null), + TypeCode.UInt32 => s_liftedToNullUInt32 ??= new GreaterThanUInt32(null), + TypeCode.UInt64 => s_liftedToNullUInt64 ??= new GreaterThanUInt64(null), + TypeCode.Single => s_liftedToNullSingle ??= new GreaterThanSingle(null), + TypeCode.Double => s_liftedToNullDouble ??= new GreaterThanDouble(null), + _ => throw ContractUtils.Unreachable, + }; + } + else + { + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.SByte => s_SByte ??= new GreaterThanSByte(Utils.BoxedFalse), + TypeCode.Int16 => s_Int16 ??= new GreaterThanInt16(Utils.BoxedFalse), + TypeCode.Char => s_Char ??= new GreaterThanChar(Utils.BoxedFalse), + TypeCode.Int32 => s_Int32 ??= new GreaterThanInt32(Utils.BoxedFalse), + TypeCode.Int64 => s_Int64 ??= new GreaterThanInt64(Utils.BoxedFalse), + TypeCode.Byte => s_Byte ??= new GreaterThanByte(Utils.BoxedFalse), + TypeCode.UInt16 => s_UInt16 ??= new GreaterThanUInt16(Utils.BoxedFalse), + TypeCode.UInt32 => s_UInt32 ??= new GreaterThanUInt32(Utils.BoxedFalse), + TypeCode.UInt64 => s_UInt64 ??= new GreaterThanUInt64(Utils.BoxedFalse), + TypeCode.Single => s_Single ??= new GreaterThanSingle(Utils.BoxedFalse), + TypeCode.Double => s_Double ??= new GreaterThanDouble(Utils.BoxedFalse), + _ => throw ContractUtils.Unreachable, + }; + } + } + } +} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/GreaterThanOrEqualInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/GreaterThanOrEqualInstruction.cs new file mode 100644 index 00000000..5efd3943 --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/GreaterThanOrEqualInstruction.cs @@ -0,0 +1,320 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.Diagnostics; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpreter +{ + internal abstract class GreaterThanOrEqualInstruction : Instruction + { + private readonly object? _nullValue; + private static Instruction? s_SByte, s_Int16, s_Char, s_Int32, s_Int64, s_Byte, s_UInt16, s_UInt32, s_UInt64, s_Single, s_Double; + private static Instruction? s_liftedToNullSByte, s_liftedToNullInt16, s_liftedToNullChar, s_liftedToNullInt32, s_liftedToNullInt64, s_liftedToNullByte, s_liftedToNullUInt16, s_liftedToNullUInt32, s_liftedToNullUInt64, s_liftedToNullSingle, s_liftedToNullDouble; + + public override int ConsumedStack => 2; + public override int ProducedStack => 1; + public override string InstructionName => "GreaterThanOrEqual"; + + private GreaterThanOrEqualInstruction(object? nullValue) + { + _nullValue = nullValue; + } + + private sealed class GreaterThanOrEqualSByte : GreaterThanOrEqualInstruction + { + public GreaterThanOrEqualSByte(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((sbyte)left >= (sbyte)right); + } + return 1; + } + } + + private sealed class GreaterThanOrEqualInt16 : GreaterThanOrEqualInstruction + { + public GreaterThanOrEqualInt16(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((short)left >= (short)right); + } + return 1; + } + } + + private sealed class GreaterThanOrEqualChar : GreaterThanOrEqualInstruction + { + public GreaterThanOrEqualChar(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((char)left >= (char)right); + } + return 1; + } + } + + private sealed class GreaterThanOrEqualInt32 : GreaterThanOrEqualInstruction + { + public GreaterThanOrEqualInt32(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((int)left >= (int)right); + } + return 1; + } + } + + private sealed class GreaterThanOrEqualInt64 : GreaterThanOrEqualInstruction + { + public GreaterThanOrEqualInt64(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((long)left >= (long)right); + } + return 1; + } + } + + private sealed class GreaterThanOrEqualByte : GreaterThanOrEqualInstruction + { + public GreaterThanOrEqualByte(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((byte)left >= (byte)right); + } + return 1; + } + } + + private sealed class GreaterThanOrEqualUInt16 : GreaterThanOrEqualInstruction + { + public GreaterThanOrEqualUInt16(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((ushort)left >= (ushort)right); + } + return 1; + } + } + + private sealed class GreaterThanOrEqualUInt32 : GreaterThanOrEqualInstruction + { + public GreaterThanOrEqualUInt32(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((uint)left >= (uint)right); + } + return 1; + } + } + + private sealed class GreaterThanOrEqualUInt64 : GreaterThanOrEqualInstruction + { + public GreaterThanOrEqualUInt64(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((ulong)left >= (ulong)right); + } + return 1; + } + } + + private sealed class GreaterThanOrEqualSingle : GreaterThanOrEqualInstruction + { + public GreaterThanOrEqualSingle(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((float)left >= (float)right); + } + return 1; + } + } + + private sealed class GreaterThanOrEqualDouble : GreaterThanOrEqualInstruction + { + public GreaterThanOrEqualDouble(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((double)left >= (double)right); + } + return 1; + } + } + + public static Instruction Create(Type type, bool liftedToNull = false) + { + Debug.Assert(!type.IsEnum); + if (liftedToNull) + { + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.SByte => s_liftedToNullSByte ??= new GreaterThanOrEqualSByte(null), + TypeCode.Int16 => s_liftedToNullInt16 ??= new GreaterThanOrEqualInt16(null), + TypeCode.Char => s_liftedToNullChar ??= new GreaterThanOrEqualChar(null), + TypeCode.Int32 => s_liftedToNullInt32 ??= new GreaterThanOrEqualInt32(null), + TypeCode.Int64 => s_liftedToNullInt64 ??= new GreaterThanOrEqualInt64(null), + TypeCode.Byte => s_liftedToNullByte ??= new GreaterThanOrEqualByte(null), + TypeCode.UInt16 => s_liftedToNullUInt16 ??= new GreaterThanOrEqualUInt16(null), + TypeCode.UInt32 => s_liftedToNullUInt32 ??= new GreaterThanOrEqualUInt32(null), + TypeCode.UInt64 => s_liftedToNullUInt64 ??= new GreaterThanOrEqualUInt64(null), + TypeCode.Single => s_liftedToNullSingle ??= new GreaterThanOrEqualSingle(null), + TypeCode.Double => s_liftedToNullDouble ??= new GreaterThanOrEqualDouble(null), + _ => throw ContractUtils.Unreachable, + }; + } + else + { + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.SByte => s_SByte ??= new GreaterThanOrEqualSByte(Utils.BoxedFalse), + TypeCode.Int16 => s_Int16 ??= new GreaterThanOrEqualInt16(Utils.BoxedFalse), + TypeCode.Char => s_Char ??= new GreaterThanOrEqualChar(Utils.BoxedFalse), + TypeCode.Int32 => s_Int32 ??= new GreaterThanOrEqualInt32(Utils.BoxedFalse), + TypeCode.Int64 => s_Int64 ??= new GreaterThanOrEqualInt64(Utils.BoxedFalse), + TypeCode.Byte => s_Byte ??= new GreaterThanOrEqualByte(Utils.BoxedFalse), + TypeCode.UInt16 => s_UInt16 ??= new GreaterThanOrEqualUInt16(Utils.BoxedFalse), + TypeCode.UInt32 => s_UInt32 ??= new GreaterThanOrEqualUInt32(Utils.BoxedFalse), + TypeCode.UInt64 => s_UInt64 ??= new GreaterThanOrEqualUInt64(Utils.BoxedFalse), + TypeCode.Single => s_Single ??= new GreaterThanOrEqualSingle(Utils.BoxedFalse), + TypeCode.Double => s_Double ??= new GreaterThanOrEqualDouble(Utils.BoxedFalse), + _ => throw ContractUtils.Unreachable, + }; + } + } + } +} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/IncrementInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/IncrementInstruction.cs new file mode 100644 index 00000000..8af79637 --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/IncrementInstruction.cs @@ -0,0 +1,174 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.Diagnostics; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpreter +{ + internal abstract class IncrementInstruction : Instruction + { + private static Instruction? s_Int16, s_Int32, s_Int64, s_UInt16, s_UInt32, s_UInt64, s_Single, s_Double; + + public override int ConsumedStack => 1; + public override int ProducedStack => 1; + public override string InstructionName => "Increment"; + + private IncrementInstruction() { } + + private sealed class IncrementInt16 : IncrementInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked((short)(1 + (short)obj))); + } + return 1; + } + } + + private sealed class IncrementInt32 : IncrementInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked(1 + (int)obj)); + } + return 1; + } + } + + private sealed class IncrementInt64 : IncrementInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked(1 + (long)obj)); + } + return 1; + } + } + + private sealed class IncrementUInt16 : IncrementInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked((ushort)(1 + (ushort)obj))); + } + return 1; + } + } + + private sealed class IncrementUInt32 : IncrementInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked(1 + (uint)obj)); + } + return 1; + } + } + + private sealed class IncrementUInt64 : IncrementInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked(1 + (ulong)obj)); + } + return 1; + } + } + + private sealed class IncrementSingle : IncrementInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked(1 + (float)obj)); + } + return 1; + } + } + + private sealed class IncrementDouble : IncrementInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked(1 + (double)obj)); + } + return 1; + } + } + + public static Instruction Create(Type type) + { + Debug.Assert(!type.IsEnum); + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.Int16 => s_Int16 ??= new IncrementInt16(), + TypeCode.Int32 => s_Int32 ??= new IncrementInt32(), + TypeCode.Int64 => s_Int64 ??= new IncrementInt64(), + TypeCode.UInt16 => s_UInt16 ??= new IncrementUInt16(), + TypeCode.UInt32 => s_UInt32 ??= new IncrementUInt32(), + TypeCode.UInt64 => s_UInt64 ??= new IncrementUInt64(), + TypeCode.Single => s_Single ??= new IncrementSingle(), + TypeCode.Double => s_Double ??= new IncrementDouble(), + _ => throw ContractUtils.Unreachable, + }; + } + } +} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/LICENSE.TXT b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/LICENSE.TXT new file mode 100644 index 00000000..984713a4 --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/LICENSE.TXT @@ -0,0 +1,23 @@ +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/LeftShiftInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/LeftShiftInstruction.cs new file mode 100644 index 00000000..e4c70e6e --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/LeftShiftInstruction.cs @@ -0,0 +1,178 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpreter +{ + internal abstract class LeftShiftInstruction : Instruction + { + private static Instruction? s_SByte, s_Int16, s_Int32, s_Int64, s_Byte, s_UInt16, s_UInt32, s_UInt64; + + public override int ConsumedStack => 2; + public override int ProducedStack => 1; + public override string InstructionName => "LeftShift"; + + private LeftShiftInstruction() { } + + private sealed class LeftShiftSByte : LeftShiftInstruction + { + public override int Run(InterpretedFrame frame) + { + object? shift = frame.Pop(); + object? value = frame.Pop(); + if (value == null || shift == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked((sbyte)((sbyte)value << (int)shift))); + } + return 1; + } + } + + private sealed class LeftShiftInt16 : LeftShiftInstruction + { + public override int Run(InterpretedFrame frame) + { + object? shift = frame.Pop(); + object? value = frame.Pop(); + if (value == null || shift == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked((short)((short)value << (int)shift))); + } + return 1; + } + } + + private sealed class LeftShiftInt32 : LeftShiftInstruction + { + public override int Run(InterpretedFrame frame) + { + object? shift = frame.Pop(); + object? value = frame.Pop(); + if (value == null || shift == null) + { + frame.Push(null); + } + else + { + frame.Push((int)value << (int)shift); + } + return 1; + } + } + + private sealed class LeftShiftInt64 : LeftShiftInstruction + { + public override int Run(InterpretedFrame frame) + { + object? shift = frame.Pop(); + object? value = frame.Pop(); + if (value == null || shift == null) + { + frame.Push(null); + } + else + { + frame.Push((long)value << (int)shift); + } + return 1; + } + } + + private sealed class LeftShiftByte : LeftShiftInstruction + { + public override int Run(InterpretedFrame frame) + { + object? shift = frame.Pop(); + object? value = frame.Pop(); + if (value == null || shift == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked((byte)((byte)value << (int)shift))); + } + return 1; + } + } + + private sealed class LeftShiftUInt16 : LeftShiftInstruction + { + public override int Run(InterpretedFrame frame) + { + object? shift = frame.Pop(); + object? value = frame.Pop(); + if (value == null || shift == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked((ushort)((ushort)value << (int)shift))); + } + return 1; + } + } + + private sealed class LeftShiftUInt32 : LeftShiftInstruction + { + public override int Run(InterpretedFrame frame) + { + object? shift = frame.Pop(); + object? value = frame.Pop(); + if (value == null || shift == null) + { + frame.Push(null); + } + else + { + frame.Push((uint)value << (int)shift); + } + return 1; + } + } + + private sealed class LeftShiftUInt64 : LeftShiftInstruction + { + public override int Run(InterpretedFrame frame) + { + object? shift = frame.Pop(); + object? value = frame.Pop(); + if (value == null || shift == null) + { + frame.Push(null); + } + else + { + frame.Push((ulong)value << (int)shift); + } + return 1; + } + } + + public static Instruction Create(Type type) => + type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.SByte => s_SByte ??= new LeftShiftSByte(), + TypeCode.Int16 => s_Int16 ??= new LeftShiftInt16(), + TypeCode.Int32 => s_Int32 ??= new LeftShiftInt32(), + TypeCode.Int64 => s_Int64 ??= new LeftShiftInt64(), + TypeCode.Byte => s_Byte ??= new LeftShiftByte(), + TypeCode.UInt16 => s_UInt16 ??= new LeftShiftUInt16(), + TypeCode.UInt32 => s_UInt32 ??= new LeftShiftUInt32(), + TypeCode.UInt64 => s_UInt64 ??= new LeftShiftUInt64(), + _ => throw ContractUtils.Unreachable, + }; + } +} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/LessThanInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/LessThanInstruction.cs new file mode 100644 index 00000000..f7c7724b --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/LessThanInstruction.cs @@ -0,0 +1,319 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.Diagnostics; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpreter +{ + internal abstract class LessThanInstruction : Instruction + { + private readonly object? _nullValue; + private static Instruction? s_SByte, s_Int16, s_Char, s_Int32, s_Int64, s_Byte, s_UInt16, s_UInt32, s_UInt64, s_Single, s_Double; + private static Instruction? s_liftedToNullSByte, s_liftedToNullInt16, s_liftedToNullChar, s_liftedToNullInt32, s_liftedToNullInt64, s_liftedToNullByte, s_liftedToNullUInt16, s_liftedToNullUInt32, s_liftedToNullUInt64, s_liftedToNullSingle, s_liftedToNullDouble; + + public override int ConsumedStack => 2; + public override int ProducedStack => 1; + public override string InstructionName => "LessThan"; + + private LessThanInstruction(object? nullValue) + { + _nullValue = nullValue; + } + + private sealed class LessThanSByte : LessThanInstruction + { + public LessThanSByte(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((sbyte)left < (sbyte)right); + } + return 1; + } + } + + private sealed class LessThanInt16 : LessThanInstruction + { + public LessThanInt16(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((short)left < (short)right); + } + return 1; + } + } + + private sealed class LessThanChar : LessThanInstruction + { + public LessThanChar(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((char)left < (char)right); + } + return 1; + } + } + + private sealed class LessThanInt32 : LessThanInstruction + { + public LessThanInt32(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((int)left < (int)right); + } + return 1; + } + } + + private sealed class LessThanInt64 : LessThanInstruction + { + public LessThanInt64(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((long)left < (long)right); + } + return 1; + } + } + + private sealed class LessThanByte : LessThanInstruction + { + public LessThanByte(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((byte)left < (byte)right); + } + return 1; + } + } + + private sealed class LessThanUInt16 : LessThanInstruction + { + public LessThanUInt16(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((ushort)left < (ushort)right); + } + return 1; + } + } + + private sealed class LessThanUInt32 : LessThanInstruction + { + public LessThanUInt32(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((uint)left < (uint)right); + } + return 1; + } + } + + private sealed class LessThanUInt64 : LessThanInstruction + { + public LessThanUInt64(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((ulong)left < (ulong)right); + } + return 1; + } + } + + private sealed class LessThanSingle : LessThanInstruction + { + public LessThanSingle(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((float)left < (float)right); + } + return 1; + } + } + + private sealed class LessThanDouble : LessThanInstruction + { + public LessThanDouble(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((double)left < (double)right); + } + return 1; + } + } + public static Instruction Create(Type type, bool liftedToNull = false) + { + Debug.Assert(!type.IsEnum); + if (liftedToNull) + { + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.SByte => s_liftedToNullSByte ??= new LessThanSByte(null), + TypeCode.Int16 => s_liftedToNullInt16 ??= new LessThanInt16(null), + TypeCode.Char => s_liftedToNullChar ??= new LessThanChar(null), + TypeCode.Int32 => s_liftedToNullInt32 ??= new LessThanInt32(null), + TypeCode.Int64 => s_liftedToNullInt64 ??= new LessThanInt64(null), + TypeCode.Byte => s_liftedToNullByte ??= new LessThanByte(null), + TypeCode.UInt16 => s_liftedToNullUInt16 ??= new LessThanUInt16(null), + TypeCode.UInt32 => s_liftedToNullUInt32 ??= new LessThanUInt32(null), + TypeCode.UInt64 => s_liftedToNullUInt64 ??= new LessThanUInt64(null), + TypeCode.Single => s_liftedToNullSingle ??= new LessThanSingle(null), + TypeCode.Double => s_liftedToNullDouble ??= new LessThanDouble(null), + _ => throw ContractUtils.Unreachable, + }; + } + else + { + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.SByte => s_SByte ??= new LessThanSByte(Utils.BoxedFalse), + TypeCode.Int16 => s_Int16 ??= new LessThanInt16(Utils.BoxedFalse), + TypeCode.Char => s_Char ??= new LessThanChar(Utils.BoxedFalse), + TypeCode.Int32 => s_Int32 ??= new LessThanInt32(Utils.BoxedFalse), + TypeCode.Int64 => s_Int64 ??= new LessThanInt64(Utils.BoxedFalse), + TypeCode.Byte => s_Byte ??= new LessThanByte(Utils.BoxedFalse), + TypeCode.UInt16 => s_UInt16 ??= new LessThanUInt16(Utils.BoxedFalse), + TypeCode.UInt32 => s_UInt32 ??= new LessThanUInt32(Utils.BoxedFalse), + TypeCode.UInt64 => s_UInt64 ??= new LessThanUInt64(Utils.BoxedFalse), + TypeCode.Single => s_Single ??= new LessThanSingle(Utils.BoxedFalse), + TypeCode.Double => s_Double ??= new LessThanDouble(Utils.BoxedFalse), + _ => throw ContractUtils.Unreachable, + }; + } + } + } +} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/LessThanOrEqualInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/LessThanOrEqualInstruction.cs new file mode 100644 index 00000000..b34e63a8 --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/LessThanOrEqualInstruction.cs @@ -0,0 +1,320 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.Diagnostics; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpreter +{ + internal abstract class LessThanOrEqualInstruction : Instruction + { + private readonly object? _nullValue; + private static Instruction? s_SByte, s_Int16, s_Char, s_Int32, s_Int64, s_Byte, s_UInt16, s_UInt32, s_UInt64, s_Single, s_Double; + private static Instruction? s_liftedToNullSByte, s_liftedToNullInt16, s_liftedToNullChar, s_liftedToNullInt32, s_liftedToNullInt64, s_liftedToNullByte, s_liftedToNullUInt16, s_liftedToNullUInt32, s_liftedToNullUInt64, s_liftedToNullSingle, s_liftedToNullDouble; + + public override int ConsumedStack => 2; + public override int ProducedStack => 1; + public override string InstructionName => "LessThanOrEqual"; + + private LessThanOrEqualInstruction(object? nullValue) + { + _nullValue = nullValue; + } + + private sealed class LessThanOrEqualSByte : LessThanOrEqualInstruction + { + public LessThanOrEqualSByte(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((sbyte)left <= (sbyte)right); + } + return 1; + } + } + + private sealed class LessThanOrEqualInt16 : LessThanOrEqualInstruction + { + public LessThanOrEqualInt16(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((short)left <= (short)right); + } + return 1; + } + } + + private sealed class LessThanOrEqualChar : LessThanOrEqualInstruction + { + public LessThanOrEqualChar(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((char)left <= (char)right); + } + return 1; + } + } + + private sealed class LessThanOrEqualInt32 : LessThanOrEqualInstruction + { + public LessThanOrEqualInt32(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((int)left <= (int)right); + } + return 1; + } + } + + private sealed class LessThanOrEqualInt64 : LessThanOrEqualInstruction + { + public LessThanOrEqualInt64(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((long)left <= (long)right); + } + return 1; + } + } + + private sealed class LessThanOrEqualByte : LessThanOrEqualInstruction + { + public LessThanOrEqualByte(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((byte)left <= (byte)right); + } + return 1; + } + } + + private sealed class LessThanOrEqualUInt16 : LessThanOrEqualInstruction + { + public LessThanOrEqualUInt16(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((ushort)left <= (ushort)right); + } + return 1; + } + } + + private sealed class LessThanOrEqualUInt32 : LessThanOrEqualInstruction + { + public LessThanOrEqualUInt32(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((uint)left <= (uint)right); + } + return 1; + } + } + + private sealed class LessThanOrEqualUInt64 : LessThanOrEqualInstruction + { + public LessThanOrEqualUInt64(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((ulong)left <= (ulong)right); + } + return 1; + } + } + + private sealed class LessThanOrEqualSingle : LessThanOrEqualInstruction + { + public LessThanOrEqualSingle(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((float)left <= (float)right); + } + return 1; + } + } + + private sealed class LessThanOrEqualDouble : LessThanOrEqualInstruction + { + public LessThanOrEqualDouble(object? nullValue) + : base(nullValue) + { + } + + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(_nullValue); + } + else + { + frame.Push((double)left <= (double)right); + } + return 1; + } + } + + public static Instruction Create(Type type, bool liftedToNull = false) + { + Debug.Assert(!type.IsEnum); + if (liftedToNull) + { + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.SByte => s_liftedToNullSByte ??= new LessThanOrEqualSByte(null), + TypeCode.Int16 => s_liftedToNullInt16 ??= new LessThanOrEqualInt16(null), + TypeCode.Char => s_liftedToNullChar ??= new LessThanOrEqualChar(null), + TypeCode.Int32 => s_liftedToNullInt32 ??= new LessThanOrEqualInt32(null), + TypeCode.Int64 => s_liftedToNullInt64 ??= new LessThanOrEqualInt64(null), + TypeCode.Byte => s_liftedToNullByte ??= new LessThanOrEqualByte(null), + TypeCode.UInt16 => s_liftedToNullUInt16 ??= new LessThanOrEqualUInt16(null), + TypeCode.UInt32 => s_liftedToNullUInt32 ??= new LessThanOrEqualUInt32(null), + TypeCode.UInt64 => s_liftedToNullUInt64 ??= new LessThanOrEqualUInt64(null), + TypeCode.Single => s_liftedToNullSingle ??= new LessThanOrEqualSingle(null), + TypeCode.Double => s_liftedToNullDouble ??= new LessThanOrEqualDouble(null), + _ => throw ContractUtils.Unreachable, + }; + } + else + { + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.SByte => s_SByte ??= new LessThanOrEqualSByte(Utils.BoxedFalse), + TypeCode.Int16 => s_Int16 ??= new LessThanOrEqualInt16(Utils.BoxedFalse), + TypeCode.Char => s_Char ??= new LessThanOrEqualChar(Utils.BoxedFalse), + TypeCode.Int32 => s_Int32 ??= new LessThanOrEqualInt32(Utils.BoxedFalse), + TypeCode.Int64 => s_Int64 ??= new LessThanOrEqualInt64(Utils.BoxedFalse), + TypeCode.Byte => s_Byte ??= new LessThanOrEqualByte(Utils.BoxedFalse), + TypeCode.UInt16 => s_UInt16 ??= new LessThanOrEqualUInt16(Utils.BoxedFalse), + TypeCode.UInt32 => s_UInt32 ??= new LessThanOrEqualUInt32(Utils.BoxedFalse), + TypeCode.UInt64 => s_UInt64 ??= new LessThanOrEqualUInt64(Utils.BoxedFalse), + TypeCode.Single => s_Single ??= new LessThanOrEqualSingle(Utils.BoxedFalse), + TypeCode.Double => s_Double ??= new LessThanOrEqualDouble(Utils.BoxedFalse), + _ => throw ContractUtils.Unreachable, + }; + } + } + } +} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/ModuloInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/ModuloInstruction.cs new file mode 100644 index 00000000..6505fd48 --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/ModuloInstruction.cs @@ -0,0 +1,182 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.Diagnostics; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpreter +{ + internal abstract class ModuloInstruction : Instruction + { + private static Instruction? s_Int16, s_Int32, s_Int64, s_UInt16, s_UInt32, s_UInt64, s_Single, s_Double; + + public override int ConsumedStack => 2; + public override int ProducedStack => 1; + public override string InstructionName => "Modulo"; + + private ModuloInstruction() { } + + private sealed class ModuloInt16 : ModuloInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)unchecked((short)((short)left % (short)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class ModuloInt32 : ModuloInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : ScriptingRuntimeHelpers.Int32ToObject((int)left % (int)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class ModuloInt64 : ModuloInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)((long)left % (long)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class ModuloUInt16 : ModuloInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)unchecked((ushort)((ushort)left % (ushort)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class ModuloUInt32 : ModuloInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)((uint)left % (uint)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class ModuloUInt64 : ModuloInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)((ulong)left % (ulong)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class ModuloSingle : ModuloInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)((float)left % (float)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class ModuloDouble : ModuloInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)((double)left % (double)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + public static Instruction Create(Type type) + { + Debug.Assert(!type.IsEnum); + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.Int16 => s_Int16 ??= new ModuloInt16(), + TypeCode.Int32 => s_Int32 ??= new ModuloInt32(), + TypeCode.Int64 => s_Int64 ??= new ModuloInt64(), + TypeCode.UInt16 => s_UInt16 ??= new ModuloUInt16(), + TypeCode.UInt32 => s_UInt32 ??= new ModuloUInt32(), + TypeCode.UInt64 => s_UInt64 ??= new ModuloUInt64(), + TypeCode.Single => s_Single ??= new ModuloSingle(), + TypeCode.Double => s_Double ??= new ModuloDouble(), + _ => throw ContractUtils.Unreachable, + }; + } + } +} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/MulInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/MulInstruction.cs new file mode 100644 index 00000000..f38b0bc9 --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/MulInstruction.cs @@ -0,0 +1,317 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.Diagnostics; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpreter +{ + internal abstract class MulInstruction : Instruction + { + private static Instruction? s_Int16, s_Int32, s_Int64, s_UInt16, s_UInt32, s_UInt64, s_Single, s_Double; + + public override int ConsumedStack => 2; + public override int ProducedStack => 1; + public override string InstructionName => "Mul"; + + private MulInstruction() { } + + private sealed class MulInt16 : MulInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)unchecked((short)((short)left * (short)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class MulInt32 : MulInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : ScriptingRuntimeHelpers.Int32ToObject(unchecked((int)left * (int)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class MulInt64 : MulInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)unchecked((long)left * (long)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class MulUInt16 : MulInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)unchecked((ushort)((ushort)left * (ushort)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class MulUInt32 : MulInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)unchecked((uint)left * (uint)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class MulUInt64 : MulInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)unchecked((ulong)left * (ulong)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class MulSingle : MulInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)((float)left * (float)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class MulDouble : MulInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)((double)left * (double)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + public static Instruction Create(Type type) + { + Debug.Assert(type.IsArithmetic()); + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.Int16 => s_Int16 ??= new MulInt16(), + TypeCode.Int32 => s_Int32 ??= new MulInt32(), + TypeCode.Int64 => s_Int64 ??= new MulInt64(), + TypeCode.UInt16 => s_UInt16 ??= new MulUInt16(), + TypeCode.UInt32 => s_UInt32 ??= new MulUInt32(), + TypeCode.UInt64 => s_UInt64 ??= new MulUInt64(), + TypeCode.Single => s_Single ??= new MulSingle(), + TypeCode.Double => s_Double ??= new MulDouble(), + + _ => throw ContractUtils.Unreachable, + }; + } + } + + internal abstract class MulOvfInstruction : Instruction + { + private static Instruction? s_Int16, s_Int32, s_Int64, s_UInt16, s_UInt32, s_UInt64; + + public override int ConsumedStack => 2; + public override int ProducedStack => 1; + public override string InstructionName => "MulOvf"; + + private MulOvfInstruction() { } + + private sealed class MulOvfInt16 : MulOvfInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)checked((short)((short)left * (short)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class MulOvfInt32 : MulOvfInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : ScriptingRuntimeHelpers.Int32ToObject(checked((int)left * (int)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class MulOvfInt64 : MulOvfInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)checked((long)left * (long)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class MulOvfUInt16 : MulOvfInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)checked((ushort)((ushort)left * (ushort)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class MulOvfUInt32 : MulOvfInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)checked((uint)left * (uint)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class MulOvfUInt64 : MulOvfInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)checked((ulong)left * (ulong)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + public static Instruction Create(Type type) + { + Debug.Assert(type.IsArithmetic()); + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.Int16 => s_Int16 ??= new MulOvfInt16(), + TypeCode.Int32 => s_Int32 ??= new MulOvfInt32(), + TypeCode.Int64 => s_Int64 ??= new MulOvfInt64(), + TypeCode.UInt16 => s_UInt16 ??= new MulOvfUInt16(), + TypeCode.UInt32 => s_UInt32 ??= new MulOvfUInt32(), + TypeCode.UInt64 => s_UInt64 ??= new MulOvfUInt64(), + _ => MulInstruction.Create(type), + }; + } + } +} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/NegateInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/NegateInstruction.cs new file mode 100644 index 00000000..a74d9a28 --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/NegateInstruction.cs @@ -0,0 +1,194 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.Diagnostics; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpreter +{ + internal abstract class NegateInstruction : Instruction + { + private static Instruction? s_Int16, s_Int32, s_Int64, s_Single, s_Double; + + public override int ConsumedStack => 1; + public override int ProducedStack => 1; + public override string InstructionName => "Negate"; + + private NegateInstruction() { } + + private sealed class NegateInt16 : NegateInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked((short)(-(short)obj))); + } + return 1; + } + } + + private sealed class NegateInt32 : NegateInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked(-(int)obj)); + } + return 1; + } + } + + private sealed class NegateInt64 : NegateInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked(-(long)obj)); + } + return 1; + } + } + + private sealed class NegateSingle : NegateInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(-(float)obj); + } + return 1; + } + } + + private sealed class NegateDouble : NegateInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(-(double)obj); + } + return 1; + } + } + + public static Instruction Create(Type type) + { + Debug.Assert(!type.IsEnum); + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.Int16 => s_Int16 ??= new NegateInt16(), + TypeCode.Int32 => s_Int32 ??= new NegateInt32(), + TypeCode.Int64 => s_Int64 ??= new NegateInt64(), + TypeCode.Single => s_Single ??= new NegateSingle(), + TypeCode.Double => s_Double ??= new NegateDouble(), + _ => throw ContractUtils.Unreachable, + }; + } + } + + internal abstract class NegateCheckedInstruction : Instruction + { + private static Instruction? s_Int16, s_Int32, s_Int64; + + public override int ConsumedStack => 1; + public override int ProducedStack => 1; + public override string InstructionName => "NegateChecked"; + + private NegateCheckedInstruction() { } + + private sealed class NegateCheckedInt32 : NegateCheckedInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(checked(-(int)obj)); + } + return 1; + } + } + + private sealed class NegateCheckedInt16 : NegateCheckedInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(checked((short)(-(short)obj))); + } + return 1; + } + } + + private sealed class NegateCheckedInt64 : NegateCheckedInstruction + { + public override int Run(InterpretedFrame frame) + { + object? obj = frame.Pop(); + if (obj == null) + { + frame.Push(null); + } + else + { + frame.Push(checked(-(long)obj)); + } + return 1; + } + } + + public static Instruction Create(Type type) + { + Debug.Assert(!type.IsEnum); + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.Int16 => s_Int16 ??= new NegateCheckedInt16(), + TypeCode.Int32 => s_Int32 ??= new NegateCheckedInt32(), + TypeCode.Int64 => s_Int64 ??= new NegateCheckedInt64(), + _ => NegateInstruction.Create(type), + }; + } + } +} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/NotEqualInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/NotEqualInstruction.cs new file mode 100644 index 00000000..763917fd --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/NotEqualInstruction.cs @@ -0,0 +1,540 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.Diagnostics; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpreter +{ + internal abstract class NotEqualInstruction : Instruction + { + // Perf: EqualityComparer but is 3/2 to 2 times slower. + private static Instruction? s_reference, s_Boolean, s_SByte, s_Int16, s_Char, s_Int32, s_Int64, s_Byte, s_UInt16, s_UInt32, s_UInt64, s_Single, s_Double; + private static Instruction? s_SByteLiftedToNull, s_Int16LiftedToNull, s_CharLiftedToNull, s_Int32LiftedToNull, s_Int64LiftedToNull, s_ByteLiftedToNull, s_UInt16LiftedToNull, s_UInt32LiftedToNull, s_UInt64LiftedToNull, s_SingleLiftedToNull, s_DoubleLiftedToNull; + + public override int ConsumedStack => 2; + public override int ProducedStack => 1; + public override string InstructionName => "NotEqual"; + + private NotEqualInstruction() { } + + private sealed class NotEqualBoolean : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right != null); + } + else if (right == null) + { + frame.Push(true); + } + else + { + frame.Push((bool)left != (bool)right); + } + return 1; + } + } + + private sealed class NotEqualSByte : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right != null); + } + else if (right == null) + { + frame.Push(true); + } + else + { + frame.Push((sbyte)left != (sbyte)right); + } + return 1; + } + } + + private sealed class NotEqualInt16 : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right != null); + } + else if (right == null) + { + frame.Push(true); + } + else + { + frame.Push((short)left != (short)right); + } + return 1; + } + } + + private sealed class NotEqualChar : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right != null); + } + else if (right == null) + { + frame.Push(true); + } + else + { + frame.Push((char)left != (char)right); + } + return 1; + } + } + + private sealed class NotEqualInt32 : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right != null); + } + else if (right == null) + { + frame.Push(true); + } + else + { + frame.Push((int)left != (int)right); + } + return 1; + } + } + + private sealed class NotEqualInt64 : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right != null); + } + else if (right == null) + { + frame.Push(true); + } + else + { + frame.Push((long)left != (long)right); + } + return 1; + } + } + + private sealed class NotEqualByte : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right != null); + } + else if (right == null) + { + frame.Push(true); + } + else + { + frame.Push((byte)left != (byte)right); + } + return 1; + } + } + + private sealed class NotEqualUInt16 : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right != null); + } + else if (right == null) + { + frame.Push(true); + } + else + { + frame.Push((ushort)left != (ushort)right); + } + return 1; + } + } + + private sealed class NotEqualUInt32 : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right != null); + } + else if (right == null) + { + frame.Push(true); + } + else + { + frame.Push((uint)left != (uint)right); + } + return 1; + } + } + + private sealed class NotEqualUInt64 : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right != null); + } + else if (right == null) + { + frame.Push(true); + } + else + { + frame.Push((ulong)left != (ulong)right); + } + return 1; + } + } + + private sealed class NotEqualSingle : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right != null); + } + else if (right == null) + { + frame.Push(true); + } + else + { + frame.Push((float)left != (float)right); + } + return 1; + } + } + + private sealed class NotEqualDouble : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + frame.Push(right != null); + } + else if (right == null) + { + frame.Push(true); + } + else + { + frame.Push((double)left != (double)right); + } + return 1; + } + } + + private sealed class NotEqualReference : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + frame.Push(frame.Pop() != frame.Pop()); + return 1; + } + } + + private sealed class NotEqualSByteLiftedToNull : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((sbyte)left != (sbyte)right); + } + return 1; + } + } + + private sealed class NotEqualInt16LiftedToNull : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((short)left != (short)right); + } + return 1; + } + } + + private sealed class NotEqualCharLiftedToNull : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((char)left != (char)right); + } + return 1; + } + } + + private sealed class NotEqualInt32LiftedToNull : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((int)left != (int)right); + } + return 1; + } + } + + private sealed class NotEqualInt64LiftedToNull : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((long)left != (long)right); + } + return 1; + } + } + + private sealed class NotEqualByteLiftedToNull : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((byte)left != (byte)right); + } + return 1; + } + } + + private sealed class NotEqualUInt16LiftedToNull : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((ushort)left != (ushort)right); + } + return 1; + } + } + + private sealed class NotEqualUInt32LiftedToNull : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((uint)left != (uint)right); + } + return 1; + } + } + + private sealed class NotEqualUInt64LiftedToNull : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((ulong)left != (ulong)right); + } + return 1; + } + } + + private sealed class NotEqualSingleLiftedToNull : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((float)left != (float)right); + } + return 1; + } + } + + private sealed class NotEqualDoubleLiftedToNull : NotEqualInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + } + else + { + frame.Push((double)left != (double)right); + } + return 1; + } + } + + public static Instruction Create(Type type, bool liftedToNull) + { + if (liftedToNull) + { + switch (type.GetNonNullableType().GetTypeCode()) + { + case TypeCode.Boolean: return ExclusiveOrInstruction.Create(type); + case TypeCode.SByte: return s_SByteLiftedToNull ??= new NotEqualSByteLiftedToNull(); + case TypeCode.Int16: return s_Int16LiftedToNull ??= new NotEqualInt16LiftedToNull(); + case TypeCode.Char: return s_CharLiftedToNull ??= new NotEqualCharLiftedToNull(); + case TypeCode.Int32: return s_Int32LiftedToNull ??= new NotEqualInt32LiftedToNull(); + case TypeCode.Int64: return s_Int64LiftedToNull ??= new NotEqualInt64LiftedToNull(); + case TypeCode.Byte: return s_ByteLiftedToNull ??= new NotEqualByteLiftedToNull(); + case TypeCode.UInt16: return s_UInt16LiftedToNull ??= new NotEqualUInt16LiftedToNull(); + case TypeCode.UInt32: return s_UInt32LiftedToNull ??= new NotEqualUInt32LiftedToNull(); + case TypeCode.UInt64: return s_UInt64LiftedToNull ??= new NotEqualUInt64LiftedToNull(); + case TypeCode.Single: return s_SingleLiftedToNull ??= new NotEqualSingleLiftedToNull(); + default: + Debug.Assert(type.GetNonNullableType().GetTypeCode() == TypeCode.Double); + return s_DoubleLiftedToNull ??= new NotEqualDoubleLiftedToNull(); + } + } + else + { + switch (type.GetNonNullableType().GetTypeCode()) + { + case TypeCode.Boolean: return s_Boolean ??= new NotEqualBoolean(); + case TypeCode.SByte: return s_SByte ??= new NotEqualSByte(); + case TypeCode.Int16: return s_Int16 ??= new NotEqualInt16(); + case TypeCode.Char: return s_Char ??= new NotEqualChar(); + case TypeCode.Int32: return s_Int32 ??= new NotEqualInt32(); + case TypeCode.Int64: return s_Int64 ??= new NotEqualInt64(); + case TypeCode.Byte: return s_Byte ??= new NotEqualByte(); + case TypeCode.UInt16: return s_UInt16 ??= new NotEqualUInt16(); + case TypeCode.UInt32: return s_UInt32 ??= new NotEqualUInt32(); + case TypeCode.UInt64: return s_UInt64 ??= new NotEqualUInt64(); + case TypeCode.Single: return s_Single ??= new NotEqualSingle(); + case TypeCode.Double: return s_Double ??= new NotEqualDouble(); + default: + // Nullable only valid if one operand is constant null, so this assert is slightly too broad. + Debug.Assert(type.IsNullableOrReferenceType()); + return s_reference ??= new NotEqualReference(); + } + } + } + } +} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/NotInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/NotInstruction.cs new file mode 100644 index 00000000..84d2af3e --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/NotInstruction.cs @@ -0,0 +1,188 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpreter +{ + internal abstract class NotInstruction : Instruction + { + public static Instruction? s_Boolean, s_Int64, s_Int32, s_Int16, s_UInt64, s_UInt32, s_UInt16, s_Byte, s_SByte; + + private NotInstruction() { } + + public override int ConsumedStack => 1; + public override int ProducedStack => 1; + public override string InstructionName => "Not"; + + private sealed class NotBoolean : NotInstruction + { + public override int Run(InterpretedFrame frame) + { + object? value = frame.Pop(); + if (value == null) + { + frame.Push(null); + } + else + { + frame.Push(!(bool)value); + } + return 1; + } + } + + private sealed class NotInt64 : NotInstruction + { + public override int Run(InterpretedFrame frame) + { + object? value = frame.Pop(); + if (value == null) + { + frame.Push(null); + } + else + { + frame.Push(~(long)value); + } + return 1; + } + } + + private sealed class NotInt32 : NotInstruction + { + public override int Run(InterpretedFrame frame) + { + object? value = frame.Pop(); + if (value == null) + { + frame.Push(null); + } + else + { + frame.Push(~(int)value); + } + return 1; + } + } + + private sealed class NotInt16 : NotInstruction + { + public override int Run(InterpretedFrame frame) + { + object? value = frame.Pop(); + if (value == null) + { + frame.Push(null); + } + else + { + frame.Push((short)(~(short)value)); + } + return 1; + } + } + + private sealed class NotUInt64 : NotInstruction + { + public override int Run(InterpretedFrame frame) + { + object? value = frame.Pop(); + if (value == null) + { + frame.Push(null); + } + else + { + frame.Push(~(ulong)value); + } + return 1; + } + } + + private sealed class NotUInt32 : NotInstruction + { + public override int Run(InterpretedFrame frame) + { + object? value = frame.Pop(); + if (value == null) + { + frame.Push(null); + } + else + { + frame.Push(~(uint)value); + } + return 1; + } + } + + private sealed class NotUInt16 : NotInstruction + { + public override int Run(InterpretedFrame frame) + { + object? value = frame.Pop(); + if (value == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked((ushort)(~(ushort)value))); + } + return 1; + } + } + + private sealed class NotByte : NotInstruction + { + public override int Run(InterpretedFrame frame) + { + object? value = frame.Pop(); + if (value == null) + { + frame.Push(null); + } + else + { + frame.Push(unchecked((byte)(~(byte)value))); + } + return 1; + } + } + + private sealed class NotSByte : NotInstruction + { + public override int Run(InterpretedFrame frame) + { + object? value = frame.Pop(); + if (value == null) + { + frame.Push(null); + } + else + { + frame.Push((sbyte)(~(sbyte)value)); + } + return 1; + } + } + + public static Instruction Create(Type type) => + type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.Boolean => s_Boolean ??= new NotBoolean(), + TypeCode.Int64 => s_Int64 ??= new NotInt64(), + TypeCode.Int32 => s_Int32 ??= new NotInt32(), + TypeCode.Int16 => s_Int16 ??= new NotInt16(), + TypeCode.UInt64 => s_UInt64 ??= new NotUInt64(), + TypeCode.UInt32 => s_UInt32 ??= new NotUInt32(), + TypeCode.UInt16 => s_UInt16 ??= new NotUInt16(), + TypeCode.Byte => s_Byte ??= new NotByte(), + TypeCode.SByte => s_SByte ??= new NotSByte(), + _ => throw ContractUtils.Unreachable, + }; + } +} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/OrInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/OrInstruction.cs new file mode 100644 index 00000000..32babdff --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/OrInstruction.cs @@ -0,0 +1,195 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpreter +{ + internal abstract class OrInstruction : Instruction + { + private static Instruction? s_SByte, s_Int16, s_Int32, s_Int64, s_Byte, s_UInt16, s_UInt32, s_UInt64, s_Boolean; + + public override int ConsumedStack => 2; + public override int ProducedStack => 1; + public override string InstructionName => "Or"; + + private OrInstruction() { } + + private sealed class OrSByte : OrInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((sbyte)((sbyte)left | (sbyte)right)); + return 1; + } + } + + private sealed class OrInt16 : OrInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((short)((short)left | (short)right)); + return 1; + } + } + + private sealed class OrInt32 : OrInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((int)left | (int)right); + return 1; + } + } + + private sealed class OrInt64 : OrInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((long)left | (long)right); + return 1; + } + } + + private sealed class OrByte : OrInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((byte)((byte)left | (byte)right)); + return 1; + } + } + + private sealed class OrUInt16 : OrInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((ushort)((ushort)left | (ushort)right)); + return 1; + } + } + + private sealed class OrUInt32 : OrInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((uint)left | (uint)right); + return 1; + } + } + + private sealed class OrUInt64 : OrInstruction + { + public override int Run(InterpretedFrame frame) + { + object? left = frame.Pop(); + object? right = frame.Pop(); + if (left == null || right == null) + { + frame.Push(null); + return 1; + } + frame.Push((ulong)left | (ulong)right); + return 1; + } + } + + private sealed class OrBoolean : OrInstruction + { + public override int Run(InterpretedFrame frame) + { + object? right = frame.Pop(); + object? left = frame.Pop(); + if (left == null) + { + if (right == null) + { + frame.Push(null); + } + else + { + frame.Push((bool)right ? Utils.BoxedTrue : null); + } + return 1; + } + + if (right == null) + { + frame.Push((bool)left ? Utils.BoxedTrue : null); + return 1; + } + + frame.Push((bool)left | (bool)right); + return 1; + } + } + + public static Instruction Create(Type type) + { + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.SByte => s_SByte ??= new OrSByte(), + TypeCode.Int16 => s_Int16 ??= new OrInt16(), + TypeCode.Int32 => s_Int32 ??= new OrInt32(), + TypeCode.Int64 => s_Int64 ??= new OrInt64(), + TypeCode.Byte => s_Byte ??= new OrByte(), + TypeCode.UInt16 => s_UInt16 ??= new OrUInt16(), + TypeCode.UInt32 => s_UInt32 ??= new OrUInt32(), + TypeCode.UInt64 => s_UInt64 ??= new OrUInt64(), + TypeCode.Boolean => s_Boolean ??= new OrBoolean(), + _ => throw ContractUtils.Unreachable, + }; + } + } +} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/README.md b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/README.md new file mode 100644 index 00000000..0081f66c --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/README.md @@ -0,0 +1,38 @@ +# .NET interpreter instructions + +The files in this folder implement interpreter instructions used by the `LightCompiler`. They are copied from the expression interpreter in [.NET](https://github.com/dotnet/runtime) (`System.Linq.Expressions.Interpreter`) and are covered by its MIT license, see [LICENSE.TXT](LICENSE.TXT). + +## Provenance + +Copied from commit [`76b5b76`](https://github.com/dotnet/runtime/tree/76b5b76ceb08baa587178f8654df1a964547930b). + +| File | Upstream path | +|---|---| +| `AddInstruction.cs` | `src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/AddInstruction.cs` | +| `AndInstruction.cs` | `src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/AndInstruction.cs` | +| `DecrementInstruction.cs` | `src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/DecrementInstruction.cs` | +| `DivInstruction.cs` | `src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/DivInstruction.cs` | +| `EqualInstruction.cs` | `src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/EqualInstruction.cs` | +| `ExclusiveOrInstruction.cs` | `src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/ExclusiveOrInstruction.cs` | +| `GreaterThanInstruction.cs` | `src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/GreaterThanInstruction.cs` | +| `GreaterThanOrEqualInstruction.cs` | `src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/GreaterThanOrEqualInstruction.cs` | +| `IncrementInstruction.cs` | `src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/IncrementInstruction.cs` | +| `LeftShiftInstruction.cs` | `src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LeftShiftInstruction.cs` | +| `LessThanInstruction.cs` | `src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LessThanInstruction.cs` | +| `LessThanOrEqualInstruction.cs` | `src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/LessThanOrEqualInstruction.cs` | +| `ModuloInstruction.cs` | `src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/ModuloInstruction.cs` | +| `MulInstruction.cs` | `src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/MulInstruction.cs` | +| `NegateInstruction.cs` | `src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/NegateInstruction.cs` | +| `NotEqualInstruction.cs` | `src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/NotEqualInstruction.cs` | +| `NotInstruction.cs` | `src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/NotInstruction.cs` | +| `OrInstruction.cs` | `src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/OrInstruction.cs` | +| `RightShiftInstruction.cs` | `src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/RightShiftInstruction.cs` | +| `SubInstruction.cs` | `src/libraries/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/SubInstruction.cs` | +| `LICENSE.TXT` | `LICENSE.TXT` | + +### Local changes + +The copied files are unmodified, except that: + +- `#nullable enable` is added. +- The `System.Dynamic.Utils` using and the `System.Linq.Expressions.Interpreter` namespace are replaced with `Microsoft.Scripting.Utils` and `Microsoft.Scripting.Interpreter`. diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/RightShiftInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/RightShiftInstruction.cs new file mode 100644 index 00000000..490d21d0 --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/RightShiftInstruction.cs @@ -0,0 +1,180 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpreter +{ + internal abstract class RightShiftInstruction : Instruction + { + private static Instruction? s_SByte, s_Int16, s_Int32, s_Int64, s_Byte, s_UInt16, s_UInt32, s_UInt64; + + public override int ConsumedStack => 2; + public override int ProducedStack => 1; + public override string InstructionName => "RightShift"; + + private RightShiftInstruction() { } + + private sealed class RightShiftSByte : RightShiftInstruction + { + public override int Run(InterpretedFrame frame) + { + object? shift = frame.Pop(); + object? value = frame.Pop(); + if (value == null || shift == null) + { + frame.Push(null); + } + else + { + frame.Push((sbyte)((sbyte)value >> (int)shift)); + } + return 1; + } + } + + private sealed class RightShiftInt16 : RightShiftInstruction + { + public override int Run(InterpretedFrame frame) + { + object? shift = frame.Pop(); + object? value = frame.Pop(); + if (value == null || shift == null) + { + frame.Push(null); + } + else + { + frame.Push((short)((short)value >> (int)shift)); + } + return 1; + } + } + + private sealed class RightShiftInt32 : RightShiftInstruction + { + public override int Run(InterpretedFrame frame) + { + object? shift = frame.Pop(); + object? value = frame.Pop(); + if (value == null || shift == null) + { + frame.Push(null); + } + else + { + frame.Push((int)value >> (int)shift); + } + return 1; + } + } + + private sealed class RightShiftInt64 : RightShiftInstruction + { + public override int Run(InterpretedFrame frame) + { + object? shift = frame.Pop(); + object? value = frame.Pop(); + if (value == null || shift == null) + { + frame.Push(null); + } + else + { + frame.Push((long)value >> (int)shift); + } + return 1; + } + } + + private sealed class RightShiftByte : RightShiftInstruction + { + public override int Run(InterpretedFrame frame) + { + object? shift = frame.Pop(); + object? value = frame.Pop(); + if (value == null || shift == null) + { + frame.Push(null); + } + else + { + frame.Push((byte)((byte)value >> (int)shift)); + } + return 1; + } + } + + private sealed class RightShiftUInt16 : RightShiftInstruction + { + public override int Run(InterpretedFrame frame) + { + object? shift = frame.Pop(); + object? value = frame.Pop(); + if (value == null || shift == null) + { + frame.Push(null); + } + else + { + frame.Push((ushort)((ushort)value >> (int)shift)); + } + return 1; + } + } + + private sealed class RightShiftUInt32 : RightShiftInstruction + { + public override int Run(InterpretedFrame frame) + { + object? shift = frame.Pop(); + object? value = frame.Pop(); + if (value == null || shift == null) + { + frame.Push(null); + } + else + { + frame.Push((uint)value >> (int)shift); + } + return 1; + } + } + + private sealed class RightShiftUInt64 : RightShiftInstruction + { + public override int Run(InterpretedFrame frame) + { + object? shift = frame.Pop(); + object? value = frame.Pop(); + if (value == null || shift == null) + { + frame.Push(null); + } + else + { + frame.Push((ulong)value >> (int)shift); + } + return 1; + } + } + + public static Instruction Create(Type type) + { + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.SByte => s_SByte ??= new RightShiftSByte(), + TypeCode.Int16 => s_Int16 ??= new RightShiftInt16(), + TypeCode.Int32 => s_Int32 ??= new RightShiftInt32(), + TypeCode.Int64 => s_Int64 ??= new RightShiftInt64(), + TypeCode.Byte => s_Byte ??= new RightShiftByte(), + TypeCode.UInt16 => s_UInt16 ??= new RightShiftUInt16(), + TypeCode.UInt32 => s_UInt32 ??= new RightShiftUInt32(), + TypeCode.UInt64 => s_UInt64 ??= new RightShiftUInt64(), + _ => throw ContractUtils.Unreachable, + }; + } + } +} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/SubInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/SubInstruction.cs new file mode 100644 index 00000000..2563c97c --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/DotNet/SubInstruction.cs @@ -0,0 +1,316 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.Diagnostics; +using Microsoft.Scripting.Utils; + +namespace Microsoft.Scripting.Interpreter +{ + internal abstract class SubInstruction : Instruction + { + private static Instruction? s_Int16, s_Int32, s_Int64, s_UInt16, s_UInt32, s_UInt64, s_Single, s_Double; + + public override int ConsumedStack => 2; + public override int ProducedStack => 1; + public override string InstructionName => "Sub"; + + private SubInstruction() { } + + private sealed class SubInt16 : SubInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)unchecked((short)((short)left - (short)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class SubInt32 : SubInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : ScriptingRuntimeHelpers.Int32ToObject(unchecked((int)left - (int)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class SubInt64 : SubInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)unchecked((long)left - (long)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class SubUInt16 : SubInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)unchecked((ushort)((ushort)left - (ushort)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class SubUInt32 : SubInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)unchecked((uint)left - (uint)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class SubUInt64 : SubInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)unchecked((ulong)left - (ulong)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class SubSingle : SubInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)((float)left - (float)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class SubDouble : SubInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)((double)left - (double)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + public static Instruction Create(Type type) + { + Debug.Assert(type.IsArithmetic()); + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.Int16 => s_Int16 ??= new SubInt16(), + TypeCode.Int32 => s_Int32 ??= new SubInt32(), + TypeCode.Int64 => s_Int64 ??= new SubInt64(), + TypeCode.UInt16 => s_UInt16 ??= new SubUInt16(), + TypeCode.UInt32 => s_UInt32 ??= new SubUInt32(), + TypeCode.UInt64 => s_UInt64 ??= new SubUInt64(), + TypeCode.Single => s_Single ??= new SubSingle(), + TypeCode.Double => s_Double ??= new SubDouble(), + _ => throw ContractUtils.Unreachable, + }; + } + } + + internal abstract class SubOvfInstruction : Instruction + { + private static Instruction? s_Int16, s_Int32, s_Int64, s_UInt16, s_UInt32, s_UInt64; + + public override int ConsumedStack => 2; + public override int ProducedStack => 1; + public override string InstructionName => "SubOvf"; + + private SubOvfInstruction() { } + + private sealed class SubOvfInt16 : SubOvfInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)checked((short)((short)left - (short)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class SubOvfInt32 : SubOvfInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : ScriptingRuntimeHelpers.Int32ToObject(checked((int)left - (int)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class SubOvfInt64 : SubOvfInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)checked((long)left - (long)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class SubOvfUInt16 : SubOvfInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)checked((ushort)((ushort)left - (ushort)right)); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class SubOvfUInt32 : SubOvfInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)checked((uint)left - (uint)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + private sealed class SubOvfUInt64 : SubOvfInstruction + { + public override int Run(InterpretedFrame frame) + { + int index = frame.StackIndex; + object?[] stack = frame.Data; + object? left = stack[index - 2]; + if (left != null) + { + object? right = stack[index - 1]; + stack[index - 2] = right == null ? null : (object)checked((ulong)left - (ulong)right); + } + + frame.StackIndex = index - 1; + return 1; + } + } + + public static Instruction Create(Type type) + { + Debug.Assert(type.IsArithmetic()); + return type.GetNonNullableType().GetTypeCode() switch + { + TypeCode.Int16 => s_Int16 ??= new SubOvfInt16(), + TypeCode.Int32 => s_Int32 ??= new SubOvfInt32(), + TypeCode.Int64 => s_Int64 ??= new SubOvfInt64(), + TypeCode.UInt16 => s_UInt16 ??= new SubOvfUInt16(), + TypeCode.UInt32 => s_UInt32 ??= new SubOvfUInt32(), + TypeCode.UInt64 => s_UInt64 ??= new SubOvfUInt64(), + _ => SubInstruction.Create(type), + }; + } + } +} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/EqualInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/EqualInstruction.cs deleted file mode 100644 index 951ef68f..00000000 --- a/src/core/Microsoft.Dynamic/Interpreter/Instructions/EqualInstruction.cs +++ /dev/null @@ -1,146 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System; - -using Microsoft.Scripting.Utils; - -namespace Microsoft.Scripting.Interpreter { - internal abstract class EqualInstruction : Instruction { - // Perf: EqualityComparer but is 3/2 to 2 times slower. - private static Instruction _Reference, _Boolean, _SByte, _Int16, _Char, _Int32, _Int64, _Byte, _UInt16, _UInt32, _UInt64, _Single, _Double; - - public override int ConsumedStack => 2; - public override int ProducedStack => 1; - - private EqualInstruction() { - } - - internal sealed class EqualBoolean : EqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((Boolean)frame.Pop()) == ((Boolean)frame.Pop())); - return +1; - } - } - - internal sealed class EqualSByte : EqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((SByte)frame.Pop()) == ((SByte)frame.Pop())); - return +1; - } - } - - internal sealed class EqualInt16 : EqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((Int16)frame.Pop()) == ((Int16)frame.Pop())); - return +1; - } - } - - internal sealed class EqualChar : EqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((Char)frame.Pop()) == ((Char)frame.Pop())); - return +1; - } - } - - internal sealed class EqualInt32 : EqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((Int32)frame.Pop()) == ((Int32)frame.Pop())); - return +1; - } - } - - internal sealed class EqualInt64 : EqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((Int64)frame.Pop()) == ((Int64)frame.Pop())); - return +1; - } - } - - internal sealed class EqualByte : EqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((Byte)frame.Pop()) == ((Byte)frame.Pop())); - return +1; - } - } - - internal sealed class EqualUInt16 : EqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((UInt16)frame.Pop()) == ((UInt16)frame.Pop())); - return +1; - } - } - - internal sealed class EqualUInt32 : EqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((UInt32)frame.Pop()) == ((UInt32)frame.Pop())); - return +1; - } - } - - internal sealed class EqualUInt64 : EqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((UInt64)frame.Pop()) == ((UInt64)frame.Pop())); - return +1; - } - } - - internal sealed class EqualSingle : EqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((Single)frame.Pop()) == ((Single)frame.Pop())); - return +1; - } - } - - internal sealed class EqualDouble : EqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((Double)frame.Pop()) == ((Double)frame.Pop())); - return +1; - } - } - - internal sealed class EqualReference : EqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(frame.Pop() == frame.Pop()); - return +1; - } - } - - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] - public static Instruction Create(Type type) { - // Boxed enums can be unboxed as their underlying types: - switch ((type.IsEnum ? Enum.GetUnderlyingType(type) : type).GetTypeCode()) { - case TypeCode.Boolean: return _Boolean ?? (_Boolean = new EqualBoolean()); - case TypeCode.SByte: return _SByte ?? (_SByte = new EqualSByte()); - case TypeCode.Byte: return _Byte ?? (_Byte = new EqualByte()); - case TypeCode.Char: return _Char ?? (_Char = new EqualChar()); - case TypeCode.Int16: return _Int16 ?? (_Int16 = new EqualInt16()); - case TypeCode.Int32: return _Int32 ?? (_Int32 = new EqualInt32()); - case TypeCode.Int64: return _Int64 ?? (_Int64 = new EqualInt64()); - - case TypeCode.UInt16: return _UInt16 ?? (_UInt16 = new EqualInt16()); - case TypeCode.UInt32: return _UInt32 ?? (_UInt32 = new EqualInt32()); - case TypeCode.UInt64: return _UInt64 ?? (_UInt64 = new EqualInt64()); - - case TypeCode.Single: return _Single ?? (_Single = new EqualSingle()); - case TypeCode.Double: return _Double ?? (_Double = new EqualDouble()); - - case TypeCode.Object: - if (!type.IsValueType) { - return _Reference ?? (_Reference = new EqualReference()); - } - // TODO: Nullable - throw new NotImplementedException(); - - default: - throw new NotImplementedException(); - } - } - - public override string ToString() { - return "Equal()"; - } - } -} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/GreaterThanInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/GreaterThanInstruction.cs deleted file mode 100644 index e9feba0c..00000000 --- a/src/core/Microsoft.Dynamic/Interpreter/Instructions/GreaterThanInstruction.cs +++ /dev/null @@ -1,132 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics; - -using Microsoft.Scripting.Utils; - -namespace Microsoft.Scripting.Interpreter { - internal abstract class GreaterThanInstruction : Instruction { - private static Instruction _SByte, _Int16, _Char, _Int32, _Int64, _Byte, _UInt16, _UInt32, _UInt64, _Single, _Double; - - public override int ConsumedStack => 2; - public override int ProducedStack => 1; - - private GreaterThanInstruction() { - } - - internal sealed class GreaterThanSByte : GreaterThanInstruction { - public override int Run(InterpretedFrame frame) { - SByte right = (SByte)frame.Pop(); - frame.Push(((SByte)frame.Pop()) > right); - return +1; - } - } - - internal sealed class GreaterThanInt16 : GreaterThanInstruction { - public override int Run(InterpretedFrame frame) { - Int16 right = (Int16)frame.Pop(); - frame.Push(((Int16)frame.Pop()) > right); - return +1; - } - } - - internal sealed class GreaterThanChar : GreaterThanInstruction { - public override int Run(InterpretedFrame frame) { - Char right = (Char)frame.Pop(); - frame.Push(((Char)frame.Pop()) > right); - return +1; - } - } - - internal sealed class GreaterThanInt32 : GreaterThanInstruction { - public override int Run(InterpretedFrame frame) { - Int32 right = (Int32)frame.Pop(); - frame.Push(((Int32)frame.Pop()) > right); - return +1; - } - } - - internal sealed class GreaterThanInt64 : GreaterThanInstruction { - public override int Run(InterpretedFrame frame) { - Int64 right = (Int64)frame.Pop(); - frame.Push(((Int64)frame.Pop()) > right); - return +1; - } - } - - internal sealed class GreaterThanByte : GreaterThanInstruction { - public override int Run(InterpretedFrame frame) { - Byte right = (Byte)frame.Pop(); - frame.Push(((Byte)frame.Pop()) > right); - return +1; - } - } - - internal sealed class GreaterThanUInt16 : GreaterThanInstruction { - public override int Run(InterpretedFrame frame) { - UInt16 right = (UInt16)frame.Pop(); - frame.Push(((UInt16)frame.Pop()) > right); - return +1; - } - } - - internal sealed class GreaterThanUInt32 : GreaterThanInstruction { - public override int Run(InterpretedFrame frame) { - UInt32 right = (UInt32)frame.Pop(); - frame.Push(((UInt32)frame.Pop()) > right); - return +1; - } - } - - internal sealed class GreaterThanUInt64 : GreaterThanInstruction { - public override int Run(InterpretedFrame frame) { - UInt64 right = (UInt64)frame.Pop(); - frame.Push(((UInt64)frame.Pop()) > right); - return +1; - } - } - - internal sealed class GreaterThanSingle : GreaterThanInstruction { - public override int Run(InterpretedFrame frame) { - Single right = (Single)frame.Pop(); - frame.Push(((Single)frame.Pop()) > right); - return +1; - } - } - - internal sealed class GreaterThanDouble : GreaterThanInstruction { - public override int Run(InterpretedFrame frame) { - Double right = (Double)frame.Pop(); - frame.Push(((Double)frame.Pop()) > right); - return +1; - } - } - - public static Instruction Create(Type type) { - Debug.Assert(!type.IsEnum); - switch (type.GetTypeCode()) { - case TypeCode.SByte: return _SByte ?? (_SByte = new GreaterThanSByte()); - case TypeCode.Byte: return _Byte ?? (_Byte = new GreaterThanByte()); - case TypeCode.Char: return _Char ?? (_Char = new GreaterThanChar()); - case TypeCode.Int16: return _Int16 ?? (_Int16 = new GreaterThanInt16()); - case TypeCode.Int32: return _Int32 ?? (_Int32 = new GreaterThanInt32()); - case TypeCode.Int64: return _Int64 ?? (_Int64 = new GreaterThanInt64()); - case TypeCode.UInt16: return _UInt16 ?? (_UInt16 = new GreaterThanUInt16()); - case TypeCode.UInt32: return _UInt32 ?? (_UInt32 = new GreaterThanUInt32()); - case TypeCode.UInt64: return _UInt64 ?? (_UInt64 = new GreaterThanUInt64()); - case TypeCode.Single: return _Single ?? (_Single = new GreaterThanSingle()); - case TypeCode.Double: return _Double ?? (_Double = new GreaterThanDouble()); - - default: - throw Assert.Unreachable; - } - } - - public override string ToString() { - return "GreaterThan()"; - } - } -} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/Instruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/Instruction.cs index 28d0201a..27b33dae 100644 --- a/src/core/Microsoft.Dynamic/Interpreter/Instructions/Instruction.cs +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/Instruction.cs @@ -5,8 +5,6 @@ using System; using System.Collections.Generic; -using Microsoft.Scripting.Runtime; - namespace Microsoft.Scripting.Interpreter { public interface IInstructionProvider { void AddInstructions(LightCompiler compiler); @@ -38,17 +36,4 @@ public virtual object GetDebugCookie(LightCompiler compiler) { return null; } } - - internal sealed class NotInstruction : Instruction { - public static readonly Instruction Instance = new NotInstruction(); - - private NotInstruction() { } - public override int ConsumedStack => 1; - public override int ProducedStack => 1; - - public override int Run(InterpretedFrame frame) { - frame.Push(!(bool)frame.Pop()); - return +1; - } - } } diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/InstructionList.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/InstructionList.cs index 79d05162..d724d699 100644 --- a/src/core/Microsoft.Dynamic/Interpreter/Instructions/InstructionList.cs +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/InstructionList.cs @@ -28,7 +28,7 @@ public readonly struct InstructionArray { // list of (instruction index, cookie) sorted by instruction index: internal readonly List> DebugCookies; - internal InstructionArray(int maxStackDepth, int maxContinuationDepth, Instruction[] instructions, + internal InstructionArray(int maxStackDepth, int maxContinuationDepth, Instruction[] instructions, object[] objects, RuntimeLabel[] labels, List> debugCookies) { MaxStackDepth = maxStackDepth; @@ -55,9 +55,9 @@ public DebugView(InstructionArray array) { public InstructionList.DebugView.InstructionView[]/*!*/ A0 { get { return InstructionList.DebugView.GetInstructionViews( - _array.Instructions, - _array.Objects, - (index) => _array.Labels[index].Index, + _array.Instructions, + _array.Objects, + (index) => _array.Labels[index].Index, _array.DebugCookies ); } @@ -99,9 +99,9 @@ public DebugView(InstructionList list) { public InstructionView[]/*!*/ A0 { get { return GetInstructionViews( - _list._instructions, - _list._objects, - (index) => _list._labels[index].TargetIndex, + _list._instructions, + _list._objects, + (index) => _list._labels[index].TargetIndex, _list._debugCookies ); } @@ -307,8 +307,8 @@ public void EmitLoad(object value, Type type) { if (value is bool b) { EmitLoad(b); return; - } - + } + if (value is int i) { if (i >= PushIntMinCachedValue && i <= PushIntMaxCachedValue) { _ints ??= new Instruction[PushIntMaxCachedValue - PushIntMinCachedValue + 1]; @@ -575,55 +575,91 @@ public void EmitNewArrayInit(Type elementType, int elementCount) { #region Arithmetic Operations public void EmitAdd(Type type, bool @checked) { - if (@checked) { - Emit(AddOvfInstruction.Create(type)); - } else { - Emit(AddInstruction.Create(type)); - } + Emit(@checked ? AddOvfInstruction.Create(type) : AddInstruction.Create(type)); } - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters")] public void EmitSub(Type type, bool @checked) { - throw new NotSupportedException(); + Emit(@checked ? SubOvfInstruction.Create(type) : SubInstruction.Create(type)); } - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters")] public void EmitMul(Type type, bool @checked) { - throw new NotSupportedException(); + Emit(@checked ? MulOvfInstruction.Create(type) : MulInstruction.Create(type)); } public void EmitDiv(Type type) { Emit(DivInstruction.Create(type)); } + public void EmitModulo(Type type) { + Emit(ModuloInstruction.Create(type)); + } + + public void EmitNegate(Type type) { + Emit(NegateInstruction.Create(type)); + } + + public void EmitNegateChecked(Type type) { + Emit(NegateCheckedInstruction.Create(type)); + } + + public void EmitIncrement(Type type) { + Emit(IncrementInstruction.Create(type)); + } + + public void EmitDecrement(Type type) { + Emit(DecrementInstruction.Create(type)); + } + + #endregion + + #region Bitwise Operations + + public void EmitExclusiveOr(Type type) { + Emit(ExclusiveOrInstruction.Create(type)); + } + + public void EmitAnd(Type type) { + Emit(AndInstruction.Create(type)); + } + + public void EmitOr(Type type) { + Emit(OrInstruction.Create(type)); + } + + public void EmitLeftShift(Type type) { + Emit(LeftShiftInstruction.Create(type)); + } + + public void EmitRightShift(Type type) { + Emit(RightShiftInstruction.Create(type)); + } + #endregion #region Comparisons - public void EmitEqual(Type type) { - Emit(EqualInstruction.Create(type)); + public void EmitEqual(Type type, bool liftedToNull = false) { + Emit(EqualInstruction.Create(type, liftedToNull)); } - public void EmitNotEqual(Type type) { - Emit(NotEqualInstruction.Create(type)); + public void EmitNotEqual(Type type, bool liftedToNull = false) { + Emit(NotEqualInstruction.Create(type, liftedToNull)); } - public void EmitLessThan(Type type) { - Emit(LessThanInstruction.Create(type)); + public void EmitLessThan(Type type, bool liftedToNull = false) { + Emit(LessThanInstruction.Create(type, liftedToNull)); } - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters")] - public void EmitLessThanOrEqual(Type type) { - throw new NotSupportedException(); + public void EmitLessThanOrEqual(Type type, bool liftedToNull = false) { + Emit(LessThanOrEqualInstruction.Create(type, liftedToNull)); } - public void EmitGreaterThan(Type type) { - Emit(GreaterThanInstruction.Create(type)); + public void EmitGreaterThan(Type type, bool liftedToNull = false) { + Emit(GreaterThanInstruction.Create(type, liftedToNull)); } - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters")] - public void EmitGreaterThanOrEqual(Type type) { - throw new NotSupportedException(); + public void EmitGreaterThanOrEqual(Type type, bool liftedToNull = false) { + Emit(GreaterThanOrEqualInstruction.Create(type, liftedToNull)); } #endregion @@ -643,7 +679,11 @@ public void EmitNumericConvertUnchecked(TypeCode from, TypeCode to) { #region Boolean Operators public void EmitNot() { - Emit(NotInstruction.Instance); + EmitNot(typeof(bool)); + } + + public void EmitNot(Type type) { + Emit(NotInstruction.Create(type)); } #endregion @@ -697,7 +737,7 @@ private static Instruction GetLoadField(FieldInfo field) { return instruction; } } - + public void EmitStoreField(FieldInfo field) { if (field.IsStatic) { Emit(new StoreStaticFieldInstruction(field)); diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/LessThanInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/LessThanInstruction.cs deleted file mode 100644 index 6aa2d0c2..00000000 --- a/src/core/Microsoft.Dynamic/Interpreter/Instructions/LessThanInstruction.cs +++ /dev/null @@ -1,251 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System; -using System.Diagnostics; - -using Microsoft.Scripting.Runtime; -using Microsoft.Scripting.Utils; - -namespace Microsoft.Scripting.Interpreter { - internal abstract class LessThanInstruction : Instruction { - private readonly object _nullValue; - private static Instruction _SByte, _Int16, _Char, _Int32, _Int64, _Byte, _UInt16, _UInt32, _UInt64, _Single, _Double; - private static Instruction _liftedToNullSByte, _liftedToNullInt16, _liftedToNullChar, _liftedToNullInt32, _liftedToNullInt64, _liftedToNullByte, _liftedToNullUInt16, _liftedToNullUInt32, _liftedToNullUInt64, _liftedToNullSingle, _liftedToNullDouble; - - public override int ConsumedStack => 2; - public override int ProducedStack => 1; - - private LessThanInstruction(object nullValue) { - _nullValue = nullValue; - } - - private sealed class LessThanSByte : LessThanInstruction { - public LessThanSByte(object nullValue) - : base(nullValue) { - } - - public override int Run(InterpretedFrame frame) { - object right = frame.Pop(); - object left = frame.Pop(); - if (left is null || right is null) { - frame.Push(_nullValue); - } else { - frame.Push((sbyte)left < (sbyte)right); - } - return 1; - } - } - - private sealed class LessThanInt16 : LessThanInstruction { - public LessThanInt16(object nullValue) - : base(nullValue) { - } - - public override int Run(InterpretedFrame frame) { - object right = frame.Pop(); - object left = frame.Pop(); - if (left is null || right is null) { - frame.Push(_nullValue); - } else { - frame.Push((short)left < (short)right); - } - return 1; - } - } - - private sealed class LessThanChar : LessThanInstruction { - public LessThanChar(object nullValue) - : base(nullValue) { - } - - public override int Run(InterpretedFrame frame) { - object right = frame.Pop(); - object left = frame.Pop(); - if (left is null || right is null) { - frame.Push(_nullValue); - } else { - frame.Push((char)left < (char)right); - } - return 1; - } - } - - private sealed class LessThanInt32 : LessThanInstruction { - public LessThanInt32(object nullValue) - : base(nullValue) { - } - - public override int Run(InterpretedFrame frame) { - object right = frame.Pop(); - object left = frame.Pop(); - if (left is null || right is null) { - frame.Push(_nullValue); - } else { - frame.Push((int)left < (int)right); - } - return 1; - } - } - - private sealed class LessThanInt64 : LessThanInstruction { - public LessThanInt64(object nullValue) - : base(nullValue) { - } - - public override int Run(InterpretedFrame frame) { - object right = frame.Pop(); - object left = frame.Pop(); - if (left is null || right is null) { - frame.Push(_nullValue); - } else { - frame.Push((long)left < (long)right); - } - return 1; - } - } - - private sealed class LessThanByte : LessThanInstruction { - public LessThanByte(object nullValue) - : base(nullValue) { - } - - public override int Run(InterpretedFrame frame) { - object right = frame.Pop(); - object left = frame.Pop(); - if (left is null || right is null) { - frame.Push(_nullValue); - } else { - frame.Push((byte)left < (byte)right); - } - return 1; - } - } - - private sealed class LessThanUInt16 : LessThanInstruction { - public LessThanUInt16(object nullValue) - : base(nullValue) { - } - - public override int Run(InterpretedFrame frame) { - object right = frame.Pop(); - object left = frame.Pop(); - if (left is null || right is null) { - frame.Push(_nullValue); - } else { - frame.Push((ushort)left < (ushort)right); - } - return 1; - } - } - - private sealed class LessThanUInt32 : LessThanInstruction { - public LessThanUInt32(object nullValue) - : base(nullValue) { - } - - public override int Run(InterpretedFrame frame) { - object right = frame.Pop(); - object left = frame.Pop(); - if (left is null || right is null) { - frame.Push(_nullValue); - } else { - frame.Push((uint)left < (uint)right); - } - return 1; - } - } - - private sealed class LessThanUInt64 : LessThanInstruction { - public LessThanUInt64(object nullValue) - : base(nullValue) { - } - - public override int Run(InterpretedFrame frame) { - object right = frame.Pop(); - object left = frame.Pop(); - if (left is null || right is null) { - frame.Push(_nullValue); - } else { - frame.Push((ulong)left < (ulong)right); - } - return 1; - } - } - - private sealed class LessThanSingle : LessThanInstruction { - public LessThanSingle(object nullValue) - : base(nullValue) { - } - - public override int Run(InterpretedFrame frame) { - object right = frame.Pop(); - object left = frame.Pop(); - if (left is null || right is null) { - frame.Push(_nullValue); - } else { - frame.Push((float)left < (float)right); - } - return 1; - } - } - - private sealed class LessThanDouble : LessThanInstruction { - public LessThanDouble(object nullValue) - : base(nullValue) { - } - - public override int Run(InterpretedFrame frame) { - object right = frame.Pop(); - object left = frame.Pop(); - if (left is null || right is null) { - frame.Push(_nullValue); - } else { - frame.Push((double)left < (double)right); - } - return 1; - } - } - public static Instruction Create(Type type, bool liftedToNull = false) { - Debug.Assert(!type.IsEnum); - if (liftedToNull) { - switch (type.GetNonNullableType().GetTypeCode()) { - case TypeCode.SByte: return _liftedToNullSByte ?? (_liftedToNullSByte = new LessThanSByte(null)); - case TypeCode.Int16: return _liftedToNullInt16 ?? (_liftedToNullInt16 = new LessThanInt16(null)); - case TypeCode.Char: return _liftedToNullChar ?? (_liftedToNullChar = new LessThanChar(null)); - case TypeCode.Int32: return _liftedToNullInt32 ?? (_liftedToNullInt32 = new LessThanInt32(null)); - case TypeCode.Int64: return _liftedToNullInt64 ?? (_liftedToNullInt64 = new LessThanInt64(null)); - case TypeCode.Byte: return _liftedToNullByte ?? (_liftedToNullByte = new LessThanByte(null)); - case TypeCode.UInt16: return _liftedToNullUInt16 ?? (_liftedToNullUInt16 = new LessThanUInt16(null)); - case TypeCode.UInt32: return _liftedToNullUInt32 ?? (_liftedToNullUInt32 = new LessThanUInt32(null)); - case TypeCode.UInt64: return _liftedToNullUInt64 ?? (_liftedToNullUInt64 = new LessThanUInt64(null)); - case TypeCode.Single: return _liftedToNullSingle ?? (_liftedToNullSingle = new LessThanSingle(null)); - case TypeCode.Double: return _liftedToNullDouble ?? (_liftedToNullDouble = new LessThanDouble(null)); - default: - throw Assert.Unreachable; - } - } - - switch (type.GetNonNullableType().GetTypeCode()) { - case TypeCode.SByte: return _SByte ?? (_SByte = new LessThanSByte(ScriptingRuntimeHelpers.False)); - case TypeCode.Int16: return _Int16 ?? (_Int16 = new LessThanInt16(ScriptingRuntimeHelpers.False)); - case TypeCode.Char: return _Char ?? (_Char = new LessThanChar(ScriptingRuntimeHelpers.False)); - case TypeCode.Int32: return _Int32 ?? (_Int32 = new LessThanInt32(ScriptingRuntimeHelpers.False)); - case TypeCode.Int64: return _Int64 ?? (_Int64 = new LessThanInt64(ScriptingRuntimeHelpers.False)); - case TypeCode.Byte: return _Byte ?? (_Byte = new LessThanByte(ScriptingRuntimeHelpers.False)); - case TypeCode.UInt16: return _UInt16 ?? (_UInt16 = new LessThanUInt16(ScriptingRuntimeHelpers.False)); - case TypeCode.UInt32: return _UInt32 ?? (_UInt32 = new LessThanUInt32(ScriptingRuntimeHelpers.False)); - case TypeCode.UInt64: return _UInt64 ?? (_UInt64 = new LessThanUInt64(ScriptingRuntimeHelpers.False)); - case TypeCode.Single: return _Single ?? (_Single = new LessThanSingle(ScriptingRuntimeHelpers.False)); - case TypeCode.Double: return _Double ?? (_Double = new LessThanDouble(ScriptingRuntimeHelpers.False)); - default: - throw Assert.Unreachable; - } - } - - public override string ToString() { - return "LessThan()"; - } - } -} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/NotEqualInstruction.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/NotEqualInstruction.cs deleted file mode 100644 index 97739777..00000000 --- a/src/core/Microsoft.Dynamic/Interpreter/Instructions/NotEqualInstruction.cs +++ /dev/null @@ -1,146 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information. - -using System; - -using Microsoft.Scripting.Utils; - -namespace Microsoft.Scripting.Interpreter { - internal abstract class NotEqualInstruction : Instruction { - // Perf: EqualityComparer but is 3/2 to 2 times slower. - private static Instruction _Reference, _Boolean, _SByte, _Int16, _Char, _Int32, _Int64, _Byte, _UInt16, _UInt32, _UInt64, _Single, _Double; - - public override int ConsumedStack => 2; - public override int ProducedStack => 1; - - private NotEqualInstruction() { - } - - internal sealed class NotEqualBoolean : NotEqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((Boolean)frame.Pop()) != ((Boolean)frame.Pop())); - return +1; - } - } - - internal sealed class NotEqualSByte : NotEqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((SByte)frame.Pop()) != ((SByte)frame.Pop())); - return +1; - } - } - - internal sealed class NotEqualInt16 : NotEqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((Int16)frame.Pop()) != ((Int16)frame.Pop())); - return +1; - } - } - - internal sealed class NotEqualChar : NotEqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((Char)frame.Pop()) != ((Char)frame.Pop())); - return +1; - } - } - - internal sealed class NotEqualInt32 : NotEqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((Int32)frame.Pop()) != ((Int32)frame.Pop())); - return +1; - } - } - - internal sealed class NotEqualInt64 : NotEqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((Int64)frame.Pop()) != ((Int64)frame.Pop())); - return +1; - } - } - - internal sealed class NotEqualByte : NotEqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((Byte)frame.Pop()) != ((Byte)frame.Pop())); - return +1; - } - } - - internal sealed class NotEqualUInt16 : NotEqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((UInt16)frame.Pop()) != ((UInt16)frame.Pop())); - return +1; - } - } - - internal sealed class NotEqualUInt32 : NotEqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((UInt32)frame.Pop()) != ((UInt32)frame.Pop())); - return +1; - } - } - - internal sealed class NotEqualUInt64 : NotEqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((UInt64)frame.Pop()) != ((UInt64)frame.Pop())); - return +1; - } - } - - internal sealed class NotEqualSingle : NotEqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((Single)frame.Pop()) != ((Single)frame.Pop())); - return +1; - } - } - - internal sealed class NotEqualDouble : NotEqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(((Double)frame.Pop()) != ((Double)frame.Pop())); - return +1; - } - } - - internal sealed class NotEqualReference : NotEqualInstruction { - public override int Run(InterpretedFrame frame) { - frame.Push(frame.Pop() != frame.Pop()); - return +1; - } - } - - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] - public static Instruction Create(Type type) { - // Boxed enums can be unboxed as their underlying types: - switch ((type.IsEnum ? Enum.GetUnderlyingType(type) : type).GetTypeCode()) { - case TypeCode.Boolean: return _Boolean ?? (_Boolean = new NotEqualBoolean()); - case TypeCode.SByte: return _SByte ?? (_SByte = new NotEqualSByte()); - case TypeCode.Byte: return _Byte ?? (_Byte = new NotEqualByte()); - case TypeCode.Char: return _Char ?? (_Char = new NotEqualChar()); - case TypeCode.Int16: return _Int16 ?? (_Int16 = new NotEqualInt16()); - case TypeCode.Int32: return _Int32 ?? (_Int32 = new NotEqualInt32()); - case TypeCode.Int64: return _Int64 ?? (_Int64 = new NotEqualInt64()); - - case TypeCode.UInt16: return _UInt16 ?? (_UInt16 = new NotEqualInt16()); - case TypeCode.UInt32: return _UInt32 ?? (_UInt32 = new NotEqualInt32()); - case TypeCode.UInt64: return _UInt64 ?? (_UInt64 = new NotEqualInt64()); - - case TypeCode.Single: return _Single ?? (_Single = new NotEqualSingle()); - case TypeCode.Double: return _Double ?? (_Double = new NotEqualDouble()); - - case TypeCode.Object: - if (!type.IsValueType) { - return _Reference ?? (_Reference = new NotEqualReference()); - } - // TODO: Nullable - throw new NotImplementedException(); - - default: - throw new NotImplementedException(); - } - } - - public override string ToString() { - return "NotEqual()"; - } - } -} diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/Utils.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/Utils.cs new file mode 100644 index 00000000..7a838ebd --- /dev/null +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/Utils.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information. + +#nullable enable + +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Interpreter { + /// + /// Helpers referenced by the instructions copied from .NET (see DotNet/README.md). + /// + internal static class Utils { + public static readonly object BoxedFalse = ScriptingRuntimeHelpers.False; + public static readonly object BoxedTrue = ScriptingRuntimeHelpers.True; + } +} diff --git a/src/core/Microsoft.Dynamic/Interpreter/InterpretedFrame.cs b/src/core/Microsoft.Dynamic/Interpreter/InterpretedFrame.cs index 1a32e587..196c3875 100644 --- a/src/core/Microsoft.Dynamic/Interpreter/InterpretedFrame.cs +++ b/src/core/Microsoft.Dynamic/Interpreter/InterpretedFrame.cs @@ -69,6 +69,22 @@ public void Push(int value) { Data[StackIndex++] = ScriptingRuntimeHelpers.Int32ToObject(value); } + internal void Push(byte value) { + Data[StackIndex++] = value; + } + + internal void Push(sbyte value) { + Data[StackIndex++] = value; + } + + internal void Push(short value) { + Data[StackIndex++] = value; + } + + internal void Push(ushort value) { + Data[StackIndex++] = value; + } + public object Pop() { var val = Data[--StackIndex]; Data[StackIndex] = null; diff --git a/src/core/Microsoft.Dynamic/Interpreter/LightCompiler.cs b/src/core/Microsoft.Dynamic/Interpreter/LightCompiler.cs index f2ed6e5f..eaef8782 100644 --- a/src/core/Microsoft.Dynamic/Interpreter/LightCompiler.cs +++ b/src/core/Microsoft.Dynamic/Interpreter/LightCompiler.cs @@ -526,22 +526,53 @@ private void CompileBinaryExpression(Expression expr) { case ExpressionType.Multiply: case ExpressionType.MultiplyChecked: case ExpressionType.Divide: + case ExpressionType.Modulo: CompileArithmetic(node.NodeType, node.Left, node.Right); return; + case ExpressionType.ExclusiveOr: + Compile(node.Left); + Compile(node.Right); + Instructions.EmitExclusiveOr(node.Left.Type); + return; + + case ExpressionType.Or: + Compile(node.Left); + Compile(node.Right); + Instructions.EmitOr(node.Left.Type); + return; + + case ExpressionType.And: + Compile(node.Left); + Compile(node.Right); + Instructions.EmitAnd(node.Left.Type); + return; + + case ExpressionType.LeftShift: + Compile(node.Left); + Compile(node.Right); + Instructions.EmitLeftShift(node.Left.Type); + return; + + case ExpressionType.RightShift: + Compile(node.Left); + Compile(node.Right); + Instructions.EmitRightShift(node.Left.Type); + return; + case ExpressionType.Equal: - CompileEqual(node.Left, node.Right); + CompileEqual(node.Left, node.Right, node.IsLiftedToNull); return; case ExpressionType.NotEqual: - CompileNotEqual(node.Left, node.Right); + CompileNotEqual(node.Left, node.Right, node.IsLiftedToNull); return; case ExpressionType.LessThan: case ExpressionType.LessThanOrEqual: case ExpressionType.GreaterThan: case ExpressionType.GreaterThanOrEqual: - CompileComparison(node.NodeType, node.Left, node.Right); + CompileComparison(node); return; default: @@ -550,34 +581,49 @@ private void CompileBinaryExpression(Expression expr) { } } - private void CompileEqual(Expression left, Expression right) { - Debug.Assert(left.Type == right.Type || !left.Type.IsValueType && !right.Type.IsValueType); +#if DEBUG + private static bool IsNullComparison(Expression left, Expression right) { + return IsNullConstant(left) + ? !IsNullConstant(right) && right.Type.IsNullableType() + : IsNullConstant(right) && left.Type.IsNullableType(); + } + + private static bool IsNullConstant(Expression e) { + return e is ConstantExpression c && c.Value is null; + } +#endif + + private void CompileEqual(Expression left, Expression right, bool liftedToNull) { +#if DEBUG + Debug.Assert(IsNullComparison(left, right) || left.Type == right.Type || !left.Type.IsValueType && !right.Type.IsValueType); +#endif Compile(left); Compile(right); - Instructions.EmitEqual(left.Type); + Instructions.EmitEqual(left.Type, liftedToNull); } - private void CompileNotEqual(Expression left, Expression right) { - Debug.Assert(left.Type == right.Type || !left.Type.IsValueType && !right.Type.IsValueType); + private void CompileNotEqual(Expression left, Expression right, bool liftedToNull) { +#if DEBUG + Debug.Assert(IsNullComparison(left, right) || left.Type == right.Type || !left.Type.IsValueType && !right.Type.IsValueType); +#endif Compile(left); Compile(right); - Instructions.EmitNotEqual(left.Type); + Instructions.EmitNotEqual(left.Type, liftedToNull); } - private void CompileComparison(ExpressionType nodeType, Expression left, Expression right) { + private void CompileComparison(BinaryExpression node) { + Expression left = node.Left; + Expression right = node.Right; Debug.Assert(left.Type == right.Type && left.Type.IsNumeric()); - // TODO: - // if (TypeUtils.IsNullableType(left.Type) && liftToNull) ... - Compile(left); Compile(right); - - switch (nodeType) { - case ExpressionType.LessThan: Instructions.EmitLessThan(left.Type); break; - case ExpressionType.LessThanOrEqual: Instructions.EmitLessThanOrEqual(left.Type); break; - case ExpressionType.GreaterThan: Instructions.EmitGreaterThan(left.Type); break; - case ExpressionType.GreaterThanOrEqual: Instructions.EmitGreaterThanOrEqual(left.Type); break; + + switch (node.NodeType) { + case ExpressionType.LessThan: Instructions.EmitLessThan(left.Type, node.IsLiftedToNull); break; + case ExpressionType.LessThanOrEqual: Instructions.EmitLessThanOrEqual(left.Type, node.IsLiftedToNull); break; + case ExpressionType.GreaterThan: Instructions.EmitGreaterThan(left.Type, node.IsLiftedToNull); break; + case ExpressionType.GreaterThanOrEqual: Instructions.EmitGreaterThanOrEqual(left.Type, node.IsLiftedToNull); break; default: throw Assert.Unreachable; } } @@ -594,6 +640,7 @@ private void CompileArithmetic(ExpressionType nodeType, Expression left, Express case ExpressionType.Multiply: Instructions.EmitMul(left.Type, false); break; case ExpressionType.MultiplyChecked: Instructions.EmitMul(left.Type, true); break; case ExpressionType.Divide: Instructions.EmitDiv(left.Type); break; + case ExpressionType.Modulo: Instructions.EmitModulo(left.Type); break; default: throw Assert.Unreachable; } } @@ -639,12 +686,8 @@ private void CompileConvertToType(Type typeFrom, Type typeTo, bool isChecked) { } private void CompileNotExpression(UnaryExpression node) { - if (node.Operand.Type == typeof(bool)) { - Compile(node.Operand); - Instructions.EmitNot(); - } else { - throw new NotImplementedException(); - } + Compile(node.Operand); + Instructions.EmitNot(node.Operand.Type); } private void CompileUnaryBoolCheck(UnaryExpression node) { @@ -676,8 +719,28 @@ private void CompileUnaryExpression(Expression expr) { } else { switch (node.NodeType) { case ExpressionType.Not: + case ExpressionType.OnesComplement: CompileNotExpression(node); return; + case ExpressionType.NegateChecked: + Compile(node.Operand); + Instructions.EmitNegateChecked(node.Type); + return; + case ExpressionType.Negate: + Compile(node.Operand); + Instructions.EmitNegate(node.Type); + return; + case ExpressionType.Increment: + Compile(node.Operand); + Instructions.EmitIncrement(node.Type); + return; + case ExpressionType.Decrement: + Compile(node.Operand); + Instructions.EmitDecrement(node.Type); + return; + case ExpressionType.UnaryPlus: + Compile(node.Operand); + return; case ExpressionType.IsTrue: case ExpressionType.IsFalse: CompileUnaryBoolCheck(node); diff --git a/src/core/Microsoft.Dynamic/Microsoft.Dynamic.csproj b/src/core/Microsoft.Dynamic/Microsoft.Dynamic.csproj index 7acb50d9..1c27b495 100644 --- a/src/core/Microsoft.Dynamic/Microsoft.Dynamic.csproj +++ b/src/core/Microsoft.Dynamic/Microsoft.Dynamic.csproj @@ -19,6 +19,12 @@ + + + + + + diff --git a/src/core/Microsoft.Dynamic/Utils/ContractUtils.cs b/src/core/Microsoft.Dynamic/Utils/ContractUtils.cs index f15f2f82..30c3cfc6 100644 --- a/src/core/Microsoft.Dynamic/Utils/ContractUtils.cs +++ b/src/core/Microsoft.Dynamic/Utils/ContractUtils.cs @@ -11,6 +11,8 @@ namespace Microsoft.Scripting.Utils { public static class ContractUtils { + internal static Exception Unreachable => Utils.Assert.Unreachable; + [Conditional("DEBUG")] public static void Assert(bool precondition) { Debug.Assert(precondition); diff --git a/src/core/Microsoft.Dynamic/Utils/TypeUtils.cs b/src/core/Microsoft.Dynamic/Utils/TypeUtils.cs index d57d2345..d63028db 100644 --- a/src/core/Microsoft.Dynamic/Utils/TypeUtils.cs +++ b/src/core/Microsoft.Dynamic/Utils/TypeUtils.cs @@ -30,6 +30,8 @@ internal static Type GetNullableType(this Type type) { internal static bool IsNullableType(this Type type) => type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + internal static bool IsNullableOrReferenceType(this Type type) => !type.IsValueType || IsNullableType(type); + internal static bool IsBool(this Type type) => GetNonNullableType(type) == typeof(bool); internal static bool IsNumeric(this Type type) { @@ -115,9 +117,9 @@ internal static bool IsIntegerOrBool(this Type type) { internal static bool CanAssign(Type to, Expression from) { if (CanAssign(to, from.Type)) return true; - if (to.IsValueType && - to.IsGenericType && - to.GetGenericTypeDefinition() == typeof(Nullable<>) && + if (to.IsValueType && + to.IsGenericType && + to.GetGenericTypeDefinition() == typeof(Nullable<>) && ConstantCheck.Check(from, null)) { return true; } @@ -140,7 +142,7 @@ internal static bool CanAssign(Type to, Type from) { CanAssign(to.GetElementType(), from.GetElementType())) { return true; } - } + } return false; } @@ -165,8 +167,8 @@ internal static bool GetNumericConversionOrder(TypeCode code, out int x, out int // | | | // v v v // 1: I1 -> I2 -> I4 -> I8 - // | | - // v v + // | | + // v v // 2: R4 -> R8 switch (code) { diff --git a/tests/Microsoft.Dynamic.Test/InterpreterTest.cs b/tests/Microsoft.Dynamic.Test/InterpreterTest.cs index f3d5e2a4..0781ecc6 100644 --- a/tests/Microsoft.Dynamic.Test/InterpreterTest.cs +++ b/tests/Microsoft.Dynamic.Test/InterpreterTest.cs @@ -1,6 +1,8 @@ using NUnit.Framework; using System; +using System.Collections.Generic; using System.Linq.Expressions; +using System.Reflection; namespace Microsoft.Dynamic.Test { [TestFixture] @@ -36,6 +38,213 @@ public void PopTest() { Scripting.Generation.CompilerHelpers.LightCompile(lambda).DynamicInvoke(); } + [TestCase(typeof(short))] + [TestCase(typeof(ushort))] + [TestCase(typeof(int))] + [TestCase(typeof(uint))] + [TestCase(typeof(long))] + [TestCase(typeof(ulong))] + public void EqualityTest(Type type) { + var x = Expression.Parameter(type, "x"); + var y = Expression.Parameter(type, "y"); + var one = Convert.ChangeType(1, type); + var two = Convert.ChangeType(2, type); + + foreach (var nodeType in new[] { ExpressionType.Equal, ExpressionType.NotEqual }) { + var lambda = Expression.Lambda(Expression.MakeBinary(nodeType, x, y), x, y); + var interpreted = Scripting.Generation.CompilerHelpers.LightCompile(lambda); + var compiled = lambda.Compile(); + + Assert.That(interpreted.DynamicInvoke(one, one), Is.EqualTo(compiled.DynamicInvoke(one, one))); + Assert.That(interpreted.DynamicInvoke(one, two), Is.EqualTo(compiled.DynamicInvoke(one, two))); + } + } + + public enum TestEnum : ushort { A, B } + + [TestCase(typeof(int), false)] + [TestCase(typeof(int), true)] + [TestCase(typeof(uint), false)] + [TestCase(typeof(uint), true)] + [TestCase(typeof(long), false)] + [TestCase(typeof(ulong), true)] + [TestCase(typeof(bool), false)] + [TestCase(typeof(bool), true)] + [TestCase(typeof(double), false)] + [TestCase(typeof(double), true)] + [TestCase(typeof(char), false)] + [TestCase(typeof(TestEnum), false)] + [TestCase(typeof(TestEnum), true)] + public void NullableEqualityTest(Type type, bool liftToNull) { + var nullableType = typeof(Nullable<>).MakeGenericType(type); + var x = Expression.Parameter(nullableType, "x"); + var y = Expression.Parameter(nullableType, "y"); + object one = type.IsEnum ? Enum.ToObject(type, 1) : Convert.ChangeType(1, type); + object two = type.IsEnum ? Enum.ToObject(type, 0) : Convert.ChangeType(type == typeof(bool) ? 0 : 2, type); + var values = new[] { (null, null), (null, one), (one, null), (one, one), (one, two) }; + + foreach (var lambda in new[] { + Expression.Lambda(Expression.Equal(x, y, liftToNull, null), x, y), + Expression.Lambda(Expression.NotEqual(x, y, liftToNull, null), x, y), + }) { + var interpreted = Scripting.Generation.CompilerHelpers.LightCompile(lambda); + var compiled = lambda.Compile(); + + foreach (var (left, right) in values) { + Assert.That(interpreted.DynamicInvoke(left, right), Is.EqualTo(compiled.DynamicInvoke(left, right)), $"{lambda.Body.NodeType}({left ?? "null"}, {right ?? "null"})"); + } + } + } + + [Test] + public void NullComparisonTest() { + var x = Expression.Parameter(typeof(int?), "x"); + + foreach (var lambda in new[] { + Expression.Lambda>(Expression.Equal(x, Expression.Constant(null)), x), + Expression.Lambda>(Expression.NotEqual(Expression.Constant(null), x), x), + }) { + var interpreted = Scripting.Generation.CompilerHelpers.LightCompile(lambda); + var compiled = lambda.Compile(); + + Assert.That(interpreted(null), Is.EqualTo(compiled(null))); + Assert.That(interpreted(1), Is.EqualTo(compiled(1))); + } + } + + private static readonly Type[] PrimitiveTypes = { + typeof(bool), typeof(char), typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), + typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), + }; + + private static object[] SampleValues(Type type) { + if (type == typeof(bool)) return new object[] { false, true }; + if (type == typeof(char)) return new object[] { '\0', 'a', 'b', char.MaxValue }; + if (type == typeof(float)) return new object[] { 0f, 1f, 2.5f, -7f, float.MaxValue, float.NaN }; + if (type == typeof(double)) return new object[] { 0d, 1d, 2.5d, -7d, double.MaxValue, double.NaN }; + + var values = new List { + type.GetField("MinValue").GetValue(null), + type.GetField("MaxValue").GetValue(null), + }; + foreach (var value in new[] { 0, 1, 2, 7, -1, -3 }) { + try { + values.Add(Convert.ChangeType(value, type)); + } catch (OverflowException) { } + } + return values.ToArray(); + } + + private static object InvokeOrException(Delegate d, object[] args) { + try { + return d.DynamicInvoke(args); + } catch (TargetInvocationException ex) { + return ex.InnerException.GetType(); + } + } + + private static void AssertInterpretedMatchesCompiled(LambdaExpression lambda, IEnumerable argSets) { + // never switch to the compiled lambda + var interpreted = Scripting.Generation.CompilerHelpers.LightCompile(lambda, int.MaxValue); + var compiled = lambda.Compile(); + + foreach (var args in argSets) { + var expected = InvokeOrException(compiled, args); + var actual = InvokeOrException(interpreted, args); + Assert.That(Equals(actual, expected), Is.True, $"{lambda.Body}({string.Join(", ", Array.ConvertAll(args, a => a ?? "null"))}): expected {expected ?? "null"} but was {actual ?? "null"}"); + } + } + + private static IEnumerable Pairs(object[] left, object[] right) { + foreach (var l in left) { + foreach (var r in right) { + yield return new[] { l, r }; + } + } + } + + private static object[] WithNull(object[] values) { + var res = new object[values.Length + 1]; + values.CopyTo(res, 1); + return res; + } + + [Test] + public void BinaryOperatorTest([Values( + ExpressionType.Add, ExpressionType.AddChecked, ExpressionType.Subtract, ExpressionType.SubtractChecked, + ExpressionType.Multiply, ExpressionType.MultiplyChecked, ExpressionType.Divide, ExpressionType.Modulo, + ExpressionType.And, ExpressionType.Or, ExpressionType.ExclusiveOr, + ExpressionType.LessThan, ExpressionType.LessThanOrEqual, ExpressionType.GreaterThan, ExpressionType.GreaterThanOrEqual + )] ExpressionType nodeType, [Values] bool nullable) { + int tested = 0; + foreach (var type in PrimitiveTypes) { + var paramType = nullable ? typeof(Nullable<>).MakeGenericType(type) : type; + var x = Expression.Parameter(paramType, "x"); + var y = Expression.Parameter(paramType, "y"); + var values = nullable ? WithNull(SampleValues(type)) : SampleValues(type); + + BinaryExpression body; + try { + body = Expression.MakeBinary(nodeType, x, y); + } catch (InvalidOperationException) { + continue; // operator not defined for this type + } + + AssertInterpretedMatchesCompiled(Expression.Lambda(body, x, y), Pairs(values, values)); + if (nullable && body.Type == typeof(bool)) { + // comparisons lifted to null + AssertInterpretedMatchesCompiled(Expression.Lambda(Expression.MakeBinary(nodeType, x, y, liftToNull: true, method: null), x, y), Pairs(values, values)); + } + tested++; + } + Assert.That(tested, Is.GreaterThan(0)); + } + + [Test] + public void ShiftTest([Values(ExpressionType.LeftShift, ExpressionType.RightShift)] ExpressionType nodeType, [Values] bool nullable) { + int tested = 0; + var shifts = new object[] { 0, 1, 7, 31, 32, 63, 65, -1 }; + foreach (var type in PrimitiveTypes) { + var x = Expression.Parameter(nullable ? typeof(Nullable<>).MakeGenericType(type) : type, "x"); + var y = Expression.Parameter(nullable ? typeof(int?) : typeof(int), "y"); + + BinaryExpression body; + try { + body = Expression.MakeBinary(nodeType, x, y); + } catch (InvalidOperationException) { + continue; // operator not defined for this type + } + + var values = SampleValues(type); + AssertInterpretedMatchesCompiled(Expression.Lambda(body, x, y), nullable ? Pairs(WithNull(values), WithNull(shifts)) : Pairs(values, shifts)); + tested++; + } + Assert.That(tested, Is.GreaterThan(0)); + } + + [Test] + public void UnaryOperatorTest([Values( + ExpressionType.Negate, ExpressionType.NegateChecked, ExpressionType.Not, ExpressionType.OnesComplement, + ExpressionType.Increment, ExpressionType.Decrement, ExpressionType.UnaryPlus + )] ExpressionType nodeType, [Values] bool nullable) { + int tested = 0; + foreach (var type in PrimitiveTypes) { + var x = Expression.Parameter(nullable ? typeof(Nullable<>).MakeGenericType(type) : type, "x"); + + UnaryExpression body; + try { + body = Expression.MakeUnary(nodeType, x, null); + } catch (InvalidOperationException) { + continue; // operator not defined for this type + } + + var values = nullable ? WithNull(SampleValues(type)) : SampleValues(type); + AssertInterpretedMatchesCompiled(Expression.Lambda(body, x), Array.ConvertAll(values, v => new[] { v })); + tested++; + } + Assert.That(tested, Is.GreaterThan(0)); + } + [TestCase(ExpressionType.IsTrue, true)] [TestCase(ExpressionType.IsTrue, false)] [TestCase(ExpressionType.IsFalse, true)] From 819f6db8eee47345d45dc5249a9bfb0a2e0afede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Mon, 19 Oct 2026 20:30:24 -0400 Subject: [PATCH 3/5] Add binary compat overloads --- .../Instructions/InstructionList.cs | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/core/Microsoft.Dynamic/Interpreter/Instructions/InstructionList.cs b/src/core/Microsoft.Dynamic/Interpreter/Instructions/InstructionList.cs index d724d699..f5aa1fdd 100644 --- a/src/core/Microsoft.Dynamic/Interpreter/Instructions/InstructionList.cs +++ b/src/core/Microsoft.Dynamic/Interpreter/Instructions/InstructionList.cs @@ -638,27 +638,39 @@ public void EmitRightShift(Type type) { #region Comparisons - public void EmitEqual(Type type, bool liftedToNull = false) { + public void EmitEqual(Type type) => EmitEqual(type, false); + + public void EmitEqual(Type type, bool liftedToNull) { Emit(EqualInstruction.Create(type, liftedToNull)); } - public void EmitNotEqual(Type type, bool liftedToNull = false) { + public void EmitNotEqual(Type type) => EmitNotEqual(type, false); + + public void EmitNotEqual(Type type, bool liftedToNull) { Emit(NotEqualInstruction.Create(type, liftedToNull)); } - public void EmitLessThan(Type type, bool liftedToNull = false) { + public void EmitLessThan(Type type) => EmitLessThan(type, false); + + public void EmitLessThan(Type type, bool liftedToNull) { Emit(LessThanInstruction.Create(type, liftedToNull)); } - public void EmitLessThanOrEqual(Type type, bool liftedToNull = false) { + public void EmitLessThanOrEqual(Type type) => EmitLessThanOrEqual(type, false); + + public void EmitLessThanOrEqual(Type type, bool liftedToNull) { Emit(LessThanOrEqualInstruction.Create(type, liftedToNull)); } - public void EmitGreaterThan(Type type, bool liftedToNull = false) { + public void EmitGreaterThan(Type type) => EmitGreaterThan(type, false); + + public void EmitGreaterThan(Type type, bool liftedToNull) { Emit(GreaterThanInstruction.Create(type, liftedToNull)); } - public void EmitGreaterThanOrEqual(Type type, bool liftedToNull = false) { + public void EmitGreaterThanOrEqual(Type type) => EmitGreaterThanOrEqual(type, false); + + public void EmitGreaterThanOrEqual(Type type, bool liftedToNull) { Emit(GreaterThanOrEqualInstruction.Create(type, liftedToNull)); } From 9ecbe1207270b587f850027a775e017c09f1e827 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Mon, 19 Oct 2026 20:30:47 -0400 Subject: [PATCH 4/5] Catch ArithmeticException on Mono --- tests/Microsoft.Dynamic.Test/InterpreterTest.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Microsoft.Dynamic.Test/InterpreterTest.cs b/tests/Microsoft.Dynamic.Test/InterpreterTest.cs index 0781ecc6..155a011c 100644 --- a/tests/Microsoft.Dynamic.Test/InterpreterTest.cs +++ b/tests/Microsoft.Dynamic.Test/InterpreterTest.cs @@ -140,6 +140,9 @@ private static object InvokeOrException(Delegate d, object[] args) { return d.DynamicInvoke(args); } catch (TargetInvocationException ex) { return ex.InnerException.GetType(); + } catch (ArithmeticException ex) { + // Mono doesn't wrap arithmetic exceptions thrown by dynamic methods + return ex.GetType(); } } From dabe58df507901ca233ec5c80cc68771450e969c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Mon, 19 Oct 2026 21:22:44 -0400 Subject: [PATCH 5/5] Test cleanup --- .../Microsoft.Dynamic.Test/InterpreterTest.cs | 149 ++++-------------- 1 file changed, 34 insertions(+), 115 deletions(-) diff --git a/tests/Microsoft.Dynamic.Test/InterpreterTest.cs b/tests/Microsoft.Dynamic.Test/InterpreterTest.cs index 155a011c..eebf4db9 100644 --- a/tests/Microsoft.Dynamic.Test/InterpreterTest.cs +++ b/tests/Microsoft.Dynamic.Test/InterpreterTest.cs @@ -1,6 +1,7 @@ using NUnit.Framework; using System; using System.Collections.Generic; +using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -38,90 +39,16 @@ public void PopTest() { Scripting.Generation.CompilerHelpers.LightCompile(lambda).DynamicInvoke(); } - [TestCase(typeof(short))] - [TestCase(typeof(ushort))] - [TestCase(typeof(int))] - [TestCase(typeof(uint))] - [TestCase(typeof(long))] - [TestCase(typeof(ulong))] - public void EqualityTest(Type type) { - var x = Expression.Parameter(type, "x"); - var y = Expression.Parameter(type, "y"); - var one = Convert.ChangeType(1, type); - var two = Convert.ChangeType(2, type); - - foreach (var nodeType in new[] { ExpressionType.Equal, ExpressionType.NotEqual }) { - var lambda = Expression.Lambda(Expression.MakeBinary(nodeType, x, y), x, y); - var interpreted = Scripting.Generation.CompilerHelpers.LightCompile(lambda); - var compiled = lambda.Compile(); - - Assert.That(interpreted.DynamicInvoke(one, one), Is.EqualTo(compiled.DynamicInvoke(one, one))); - Assert.That(interpreted.DynamicInvoke(one, two), Is.EqualTo(compiled.DynamicInvoke(one, two))); - } - } - - public enum TestEnum : ushort { A, B } - - [TestCase(typeof(int), false)] - [TestCase(typeof(int), true)] - [TestCase(typeof(uint), false)] - [TestCase(typeof(uint), true)] - [TestCase(typeof(long), false)] - [TestCase(typeof(ulong), true)] - [TestCase(typeof(bool), false)] - [TestCase(typeof(bool), true)] - [TestCase(typeof(double), false)] - [TestCase(typeof(double), true)] - [TestCase(typeof(char), false)] - [TestCase(typeof(TestEnum), false)] - [TestCase(typeof(TestEnum), true)] - public void NullableEqualityTest(Type type, bool liftToNull) { - var nullableType = typeof(Nullable<>).MakeGenericType(type); - var x = Expression.Parameter(nullableType, "x"); - var y = Expression.Parameter(nullableType, "y"); - object one = type.IsEnum ? Enum.ToObject(type, 1) : Convert.ChangeType(1, type); - object two = type.IsEnum ? Enum.ToObject(type, 0) : Convert.ChangeType(type == typeof(bool) ? 0 : 2, type); - var values = new[] { (null, null), (null, one), (one, null), (one, one), (one, two) }; - - foreach (var lambda in new[] { - Expression.Lambda(Expression.Equal(x, y, liftToNull, null), x, y), - Expression.Lambda(Expression.NotEqual(x, y, liftToNull, null), x, y), - }) { - var interpreted = Scripting.Generation.CompilerHelpers.LightCompile(lambda); - var compiled = lambda.Compile(); - - foreach (var (left, right) in values) { - Assert.That(interpreted.DynamicInvoke(left, right), Is.EqualTo(compiled.DynamicInvoke(left, right)), $"{lambda.Body.NodeType}({left ?? "null"}, {right ?? "null"})"); - } - } - } - - [Test] - public void NullComparisonTest() { - var x = Expression.Parameter(typeof(int?), "x"); - - foreach (var lambda in new[] { - Expression.Lambda>(Expression.Equal(x, Expression.Constant(null)), x), - Expression.Lambda>(Expression.NotEqual(Expression.Constant(null), x), x), - }) { - var interpreted = Scripting.Generation.CompilerHelpers.LightCompile(lambda); - var compiled = lambda.Compile(); - - Assert.That(interpreted(null), Is.EqualTo(compiled(null))); - Assert.That(interpreted(1), Is.EqualTo(compiled(1))); - } - } - private static readonly Type[] PrimitiveTypes = { typeof(bool), typeof(char), typeof(sbyte), typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), }; private static object[] SampleValues(Type type) { - if (type == typeof(bool)) return new object[] { false, true }; - if (type == typeof(char)) return new object[] { '\0', 'a', 'b', char.MaxValue }; - if (type == typeof(float)) return new object[] { 0f, 1f, 2.5f, -7f, float.MaxValue, float.NaN }; - if (type == typeof(double)) return new object[] { 0d, 1d, 2.5d, -7d, double.MaxValue, double.NaN }; + if (type == typeof(bool)) return [false, true]; + if (type == typeof(char)) return ['\0', 'a', 'b', char.MaxValue]; + if (type == typeof(float)) return [0f, 1f, 2.5f, -7f, float.MaxValue, float.NaN]; + if (type == typeof(double)) return [0d, 1d, 2.5d, -7d, double.MaxValue, double.NaN]; var values = new List { type.GetField("MinValue").GetValue(null), @@ -161,22 +88,17 @@ private static void AssertInterpretedMatchesCompiled(LambdaExpression lambda, IE private static IEnumerable Pairs(object[] left, object[] right) { foreach (var l in left) { foreach (var r in right) { - yield return new[] { l, r }; + yield return [l, r]; } } } - private static object[] WithNull(object[] values) { - var res = new object[values.Length + 1]; - values.CopyTo(res, 1); - return res; - } - [Test] public void BinaryOperatorTest([Values( ExpressionType.Add, ExpressionType.AddChecked, ExpressionType.Subtract, ExpressionType.SubtractChecked, ExpressionType.Multiply, ExpressionType.MultiplyChecked, ExpressionType.Divide, ExpressionType.Modulo, ExpressionType.And, ExpressionType.Or, ExpressionType.ExclusiveOr, + ExpressionType.Equal, ExpressionType.NotEqual, ExpressionType.LessThan, ExpressionType.LessThanOrEqual, ExpressionType.GreaterThan, ExpressionType.GreaterThanOrEqual )] ExpressionType nodeType, [Values] bool nullable) { int tested = 0; @@ -184,7 +106,7 @@ public void BinaryOperatorTest([Values( var paramType = nullable ? typeof(Nullable<>).MakeGenericType(type) : type; var x = Expression.Parameter(paramType, "x"); var y = Expression.Parameter(paramType, "y"); - var values = nullable ? WithNull(SampleValues(type)) : SampleValues(type); + object[] values = nullable ? [null, .. SampleValues(type)] : SampleValues(type); BinaryExpression body; try { @@ -203,10 +125,30 @@ public void BinaryOperatorTest([Values( Assert.That(tested, Is.GreaterThan(0)); } + public enum TestEnum : ushort { A, B } + + [Test] + public void NullableEnumEqualityTest([Values(ExpressionType.Equal, ExpressionType.NotEqual)] ExpressionType nodeType, [Values] bool liftToNull) { + var x = Expression.Parameter(typeof(TestEnum?), "x"); + var y = Expression.Parameter(typeof(TestEnum?), "y"); + object[] values = [null, TestEnum.A, TestEnum.B]; + + AssertInterpretedMatchesCompiled(Expression.Lambda(Expression.MakeBinary(nodeType, x, y, liftToNull, null), x, y), Pairs(values, values)); + } + + [Test] + public void NullComparisonTest() { + var x = Expression.Parameter(typeof(int?), "x"); + object[][] argSets = [[null], [1]]; + + AssertInterpretedMatchesCompiled(Expression.Lambda(Expression.Equal(x, Expression.Constant(null)), x), argSets); + AssertInterpretedMatchesCompiled(Expression.Lambda(Expression.NotEqual(Expression.Constant(null), x), x), argSets); + } + [Test] public void ShiftTest([Values(ExpressionType.LeftShift, ExpressionType.RightShift)] ExpressionType nodeType, [Values] bool nullable) { int tested = 0; - var shifts = new object[] { 0, 1, 7, 31, 32, 63, 65, -1 }; + object[] shifts = [0, 1, 7, 31, 32, 63, 65, -1]; foreach (var type in PrimitiveTypes) { var x = Expression.Parameter(nullable ? typeof(Nullable<>).MakeGenericType(type) : type, "x"); var y = Expression.Parameter(nullable ? typeof(int?) : typeof(int), "y"); @@ -219,7 +161,7 @@ public void ShiftTest([Values(ExpressionType.LeftShift, ExpressionType.RightShif } var values = SampleValues(type); - AssertInterpretedMatchesCompiled(Expression.Lambda(body, x, y), nullable ? Pairs(WithNull(values), WithNull(shifts)) : Pairs(values, shifts)); + AssertInterpretedMatchesCompiled(Expression.Lambda(body, x, y), nullable ? Pairs([null, .. values], [null, .. shifts]) : Pairs(values, shifts)); tested++; } Assert.That(tested, Is.GreaterThan(0)); @@ -228,7 +170,8 @@ public void ShiftTest([Values(ExpressionType.LeftShift, ExpressionType.RightShif [Test] public void UnaryOperatorTest([Values( ExpressionType.Negate, ExpressionType.NegateChecked, ExpressionType.Not, ExpressionType.OnesComplement, - ExpressionType.Increment, ExpressionType.Decrement, ExpressionType.UnaryPlus + ExpressionType.Increment, ExpressionType.Decrement, ExpressionType.UnaryPlus, + ExpressionType.IsTrue, ExpressionType.IsFalse )] ExpressionType nodeType, [Values] bool nullable) { int tested = 0; foreach (var type in PrimitiveTypes) { @@ -241,35 +184,11 @@ public void UnaryOperatorTest([Values( continue; // operator not defined for this type } - var values = nullable ? WithNull(SampleValues(type)) : SampleValues(type); - AssertInterpretedMatchesCompiled(Expression.Lambda(body, x), Array.ConvertAll(values, v => new[] { v })); + object[] values = nullable ? [null, .. SampleValues(type)] : SampleValues(type); + AssertInterpretedMatchesCompiled(Expression.Lambda(body, x), values.Select(v => new[] { v })); tested++; } Assert.That(tested, Is.GreaterThan(0)); } - - [TestCase(ExpressionType.IsTrue, true)] - [TestCase(ExpressionType.IsTrue, false)] - [TestCase(ExpressionType.IsFalse, true)] - [TestCase(ExpressionType.IsFalse, false)] - public void UnaryBoolCheckTest(ExpressionType nodeType, bool value) { - var x = Expression.Parameter(typeof(bool), "x"); - var lambda = Expression.Lambda>(Expression.MakeUnary(nodeType, x, null), x); - - Assert.That(Scripting.Generation.CompilerHelpers.LightCompile(lambda)(value), Is.EqualTo(lambda.Compile()(value))); - } - - [TestCase(ExpressionType.IsTrue, true)] - [TestCase(ExpressionType.IsTrue, false)] - [TestCase(ExpressionType.IsTrue, null)] - [TestCase(ExpressionType.IsFalse, true)] - [TestCase(ExpressionType.IsFalse, false)] - [TestCase(ExpressionType.IsFalse, null)] - public void LiftedUnaryBoolCheckTest(ExpressionType nodeType, bool? value) { - var x = Expression.Parameter(typeof(bool?), "x"); - var lambda = Expression.Lambda>(Expression.MakeUnary(nodeType, x, null), x); - - Assert.That(Scripting.Generation.CompilerHelpers.LightCompile(lambda)(value), Is.EqualTo(lambda.Compile()(value))); - } } }