Add new eval arithmetic related code.
authorMikhail Kurinnoi <m.kurinnoi@samsung.com>
Tue, 19 Oct 2021 15:06:45 +0000 (08:06 -0700)
committerAlexander Soldatov/Platform Lab /SRR/Staff Engineer/Samsung Electronics <soldatov.a@samsung.com>
Sun, 24 Oct 2021 17:24:21 +0000 (20:24 +0300)
Implementation for SyntaxKinds: AddExpression, MultiplyExpression, SubtractExpression, DivideExpression, ModuloExpression, RightShiftExpression, LeftShiftExpression, BitwiseNotExpression, LogicalAndExpression, LogicalOrExpression, ExclusiveOrExpression, BitwiseAndExpression, BitwiseOrExpression, LogicalNotExpression, EqualsExpression, NotEqualsExpression, GreaterThanExpression, LessThanExpression, GreaterThanOrEqualExpression, LessThanOrEqualExpression.

src/debugger/evalstackmachine.cpp
src/debugger/valueprint.cpp
src/debugger/valueprint.h
src/managed/Evaluation.cs
src/managed/StackMachine.cs
src/managed/interop.h
test-suite/MITestEvaluate/Program.cs
test-suite/VSCodeTestEvaluate/Program.cs

index 9ecd8f0968fc15328a7a4b546cb5f3753c233c7c..2c43b27f1ef55e43a8330e7dc6294bbfe492e225 100644 (file)
@@ -53,6 +53,51 @@ namespace
         PVOID Ptr;
     };
 
+    // Keep in sync with BasicTypes enum in Evaluation.cs
+    enum class BasicTypes : int32_t
+    {
+        TypeBoolean = 1,
+        TypeByte,
+        TypeSByte,
+        TypeChar,
+        TypeDouble,
+        TypeSingle,
+        TypeInt32,
+        TypeUInt32,
+        TypeInt64,
+        TypeUInt64,
+        TypeInt16,
+        TypeUInt16,
+        TypeString
+    };
+
+    // Keep in sync with OperationType enum in Evaluation.cs
+    enum class OperationType : int32_t
+    {
+        AddExpression = 1,
+        SubtractExpression,
+        MultiplyExpression,
+        DivideExpression,
+        ModuloExpression,
+        RightShiftExpression,
+        LeftShiftExpression,
+        BitwiseNotExpression,
+        LogicalAndExpression,
+        LogicalOrExpression,
+        ExclusiveOrExpression,
+        BitwiseAndExpression,
+        BitwiseOrExpression,
+        LogicalNotExpression,
+        EqualsExpression,
+        NotEqualsExpression,
+        LessThanExpression,
+        GreaterThanExpression,
+        LessThanOrEqualExpression,
+        GreaterThanOrEqualExpression,
+        UnaryPlusExpression,
+        UnaryMinusExpression
+    };
+
     void ReplaceAllSubstring(std::string &str, const std::string &from, const std::string &to)
     {
         size_t start = 0;
@@ -280,104 +325,6 @@ namespace
         return S_OK;
     }
 
-    template<typename T1, typename T2>
-    HRESULT NumericPromotionWithValue(ICorDebugValue *pInputValue, ICorDebugValue **ppResultValue, EvalData &ed)
-    {
-        HRESULT Status;
-        ToRelease<ICorDebugGenericValue> pGenericValue;
-        IfFailRet(pInputValue->QueryInterface(IID_ICorDebugGenericValue, (LPVOID*) &pGenericValue));
-
-        T1 oldTypeValue = 0;
-        IfFailRet(pGenericValue->GetValue((LPVOID) &oldTypeValue));
-        T2 newTypeValue = oldTypeValue;
-
-        static_assert(std::is_same<T2, int32_t>::value || std::is_same<T2, int64_t>::value, "only int32_t or int64_t allowed");
-        CorElementType elemType = std::is_same<T2, int32_t>::value ? ELEMENT_TYPE_I4 : ELEMENT_TYPE_I8;
-
-        return CreatePrimitiveValue(ed.pThread, ppResultValue, elemType, &newTypeValue);
-    }
-
-    HRESULT UnaryNumericPromotion(ICorDebugValue *pInputValue, ICorDebugValue **ppResultValue, EvalData &ed)
-    {
-        HRESULT Status;
-        CorElementType elemType;
-        IfFailRet(pInputValue->GetType(&elemType));
-
-        // From ECMA-334:
-        // Unary numeric promotions
-        // Unary numeric promotion occurs for the operands of the predefined +, -, and ~unary operators.
-        // Unary numeric promotion simply consists of converting operands of type sbyte, byte, short, ushort, or char to type int.
-        // Additionally, for the unary - operator, unary numeric promotion converts operands of type uint to type long.
-
-        switch (elemType)
-        {
-            case ELEMENT_TYPE_CHAR:
-                return NumericPromotionWithValue<uint16_t, int32_t>(pInputValue, ppResultValue, ed);
-
-            case ELEMENT_TYPE_I1:
-                return NumericPromotionWithValue<int8_t, int32_t>(pInputValue, ppResultValue, ed);
-
-            case ELEMENT_TYPE_U1:
-                return NumericPromotionWithValue<uint8_t, int32_t>(pInputValue, ppResultValue, ed);
-
-            case ELEMENT_TYPE_I2:
-                return NumericPromotionWithValue<int16_t, int32_t>(pInputValue, ppResultValue, ed);
-
-            case ELEMENT_TYPE_U2:
-                return NumericPromotionWithValue<uint16_t, int32_t>(pInputValue, ppResultValue, ed);
-
-            case ELEMENT_TYPE_U4:
-                return NumericPromotionWithValue<uint32_t, int64_t>(pInputValue, ppResultValue, ed);
-
-            default:
-                return E_INVALIDARG;
-        }
-    }
-
-    template<typename T>
-    HRESULT InvertNumberValue(ICorDebugValue *pInputValue)
-    {
-        HRESULT Status;
-        ToRelease<ICorDebugGenericValue> pGenericValue;
-        IfFailRet(pInputValue->QueryInterface(IID_ICorDebugGenericValue, (LPVOID*) &pGenericValue));
-
-        T value = 0;
-        IfFailRet(pGenericValue->GetValue(&value));
-        value = -value;
-        return pGenericValue->SetValue(&value);
-    }
-
-    HRESULT InvertNumber(ICorDebugValue *pValue)
-    {
-        HRESULT Status;
-        CorElementType elemType;
-        IfFailRet(pValue->GetType(&elemType));
-
-        switch (elemType)
-        {
-            case ELEMENT_TYPE_I1:
-                return InvertNumberValue<int8_t>(pValue);
-
-            case ELEMENT_TYPE_I2:
-                return InvertNumberValue<int16_t>(pValue);
-
-            case ELEMENT_TYPE_I4:
-                return InvertNumberValue<int32_t>(pValue);
-
-            case ELEMENT_TYPE_I8:
-                return InvertNumberValue<int64_t>(pValue);
-
-            case ELEMENT_TYPE_R4:
-                return InvertNumberValue<float>(pValue);
-
-            case ELEMENT_TYPE_R8:
-                return InvertNumberValue<double>(pValue);
-
-            default:
-                return E_INVALIDARG;
-        }
-    }
-
     HRESULT GetArgData(ICorDebugValue *pTypeValue, std::string &typeName, CorElementType &elemType)
     {
         HRESULT Status;
@@ -393,7 +340,7 @@ namespace
         return S_OK;
     };
 
-    HRESULT CallUnaryOperator(const std::string opName, ICorDebugValue *pValue, ICorDebugValue **pResultValue, EvalData &ed)
+    HRESULT CallUnaryOperator(const std::string &opName, ICorDebugValue *pValue, ICorDebugValue **pResultValue, EvalData &ed)
     {
         HRESULT Status;
         std::string typeName;
@@ -422,16 +369,13 @@ namespace
         return ed.pEvalHelpers->EvalFunction(ed.pThread, iCorFunc, nullptr, 0, &pValue, 1, pResultValue, ed.evalFlags);
     }
 
-    HRESULT CallCastOperator(const std::string opName, ICorDebugValue *pValue, ICorDebugValue *pType1Value, ICorDebugValue *pType2Value,
-                             ICorDebugValue **pResultValue, EvalData &ed)
+    HRESULT CallCastOperator(const std::string &opName, ICorDebugValue *pValue, CorElementType elemRetType, const std::string &typeRetName,
+                             ICorDebugValue *pTypeValue, ICorDebugValue **pResultValue, EvalData &ed)
     {
         HRESULT Status;
-        std::string typeName1;
-        CorElementType elemType1;
-        IfFailRet(GetArgData(pType1Value, typeName1, elemType1));
-        std::string typeName2;
-        CorElementType elemType2;
-        IfFailRet(GetArgData(pType2Value, typeName2, elemType2));
+        std::string typeName;
+        CorElementType elemType;
+        IfFailRet(GetArgData(pTypeValue, typeName, elemType));
 
         ToRelease<ICorDebugFunction> iCorFunc;
         ed.pEvaluator->WalkMethods(pValue, [&](
@@ -442,8 +386,8 @@ namespace
             Evaluator::GetFunctionCallback getFunction)
         {
             if (!is_static || methodArgs.size() != 1 || opName != methodName ||
-                elemType1 != methodRet.corType || typeName1 != methodRet.typeName ||
-                elemType2 != methodArgs[0].corType || typeName2 != methodArgs[0].typeName)
+                elemRetType != methodRet.corType || typeRetName != methodRet.typeName ||
+                elemType != methodArgs[0].corType || typeName != methodArgs[0].typeName)
                 return S_OK;
 
             IfFailRet(getFunction(&iCorFunc));
@@ -453,7 +397,18 @@ namespace
         if (!iCorFunc)
             return E_FAIL;
 
-        return ed.pEvalHelpers->EvalFunction(ed.pThread, iCorFunc, nullptr, 0, &pType2Value, 1, pResultValue, ed.evalFlags);
+        return ed.pEvalHelpers->EvalFunction(ed.pThread, iCorFunc, nullptr, 0, &pTypeValue, 1, pResultValue, ed.evalFlags);
+    }
+
+    HRESULT CallCastOperator(const std::string &opName, ICorDebugValue *pValue, ICorDebugValue *pTypeRetValue, ICorDebugValue *pTypeValue,
+                             ICorDebugValue **pResultValue, EvalData &ed)
+    {
+        HRESULT Status;
+        std::string typeRetName;
+        CorElementType elemRetType;
+        IfFailRet(GetArgData(pTypeRetValue, typeRetName, elemRetType));
+
+        return CallCastOperator(opName, pValue, elemRetType, typeRetName, pTypeValue, pResultValue, ed);
     }
 
     template<typename T1, typename T2>
@@ -540,23 +495,28 @@ namespace
         return implicitCastLiteralMap;
     }
 
-    HRESULT GetRealValueWithType(ICorDebugValue *pValue, ICorDebugValue **ppResultValue, CorElementType &elemType)
+    HRESULT GetRealValueWithType(ICorDebugValue *pValue, ICorDebugValue **ppResultValue, CorElementType *pElemType = nullptr)
     {
         HRESULT Status;
         // Dereference and unbox value, since we need real value.
         ToRelease<ICorDebugValue> iCorRealValue;
         IfFailRet(DereferenceAndUnboxValue(pValue, &iCorRealValue));
+        CorElementType elemType;
         IfFailRet(iCorRealValue->GetType(&elemType));
         // Note, in case of class (string is class), we must use reference instead.
         if (elemType == ELEMENT_TYPE_STRING ||
             elemType == ELEMENT_TYPE_CLASS)
         {
             pValue->AddRef();
-            (*ppResultValue) = pValue;
+            *ppResultValue = pValue;
+            if (pElemType)
+                *pElemType = elemType;
         }
         else
         {
-            (*ppResultValue) = iCorRealValue.Detach();
+            *ppResultValue = iCorRealValue.Detach();
+            if (pElemType)
+                *pElemType = elemType;
         }
 
         return S_OK;
@@ -627,11 +587,11 @@ namespace
         
         ToRelease<ICorDebugValue> iCorRealValue1;
         CorElementType elemType1;
-        IfFailRet(GetRealValueWithType(pSrcValue, &iCorRealValue1, elemType1));
+        IfFailRet(GetRealValueWithType(pSrcValue, &iCorRealValue1, &elemType1));
 
         ToRelease<ICorDebugValue> iCorRealValue2;
         CorElementType elemType2;
-        IfFailRet(GetRealValueWithType(pDstValue, &iCorRealValue2, elemType2));
+        IfFailRet(GetRealValueWithType(pDstValue, &iCorRealValue2, &elemType2));
 
         bool haveSameType = true;
         if (elemType1 == elemType2)
@@ -662,7 +622,7 @@ namespace
                 return Status;
 
             iCorRealValue1.Free();
-            IfFailRet(GetRealValueWithType(iCorResultValue, &iCorRealValue1, elemType1));
+            IfFailRet(GetRealValueWithType(iCorResultValue, &iCorRealValue1, &elemType1));
 
             haveSameType = true;
         }
@@ -682,6 +642,368 @@ namespace
         return E_INVALIDARG;
     }
 
+    HRESULT GetOperandDataTypeByValue(ICorDebugValue *pValue, CorElementType elemType, PVOID &resultData, int32_t &resultType)
+    {
+        HRESULT Status;
+
+        if (elemType == ELEMENT_TYPE_STRING)
+        {
+            resultType = (int32_t)BasicTypes::TypeString;
+            ToRelease<ICorDebugValue> iCorValue;
+            BOOL isNull = FALSE;
+            IfFailRet(DereferenceAndUnboxValue(pValue, &iCorValue, &isNull));
+            resultData = 0;
+            if (!isNull)
+            {
+                std::string String;
+                IfFailRet(PrintStringValue(iCorValue, String));
+                resultData = Interop::AllocString(String);
+            }
+            return S_OK;
+        }
+
+        static std::unordered_map<CorElementType, BasicTypes> basicTypesMap
+        {
+            {ELEMENT_TYPE_BOOLEAN, BasicTypes::TypeBoolean},
+            {ELEMENT_TYPE_U1, BasicTypes::TypeByte},
+            {ELEMENT_TYPE_I1, BasicTypes::TypeSByte},
+            {ELEMENT_TYPE_CHAR, BasicTypes::TypeChar},
+            {ELEMENT_TYPE_R8, BasicTypes::TypeDouble},
+            {ELEMENT_TYPE_R4, BasicTypes::TypeSingle},
+            {ELEMENT_TYPE_I4, BasicTypes::TypeInt32},
+            {ELEMENT_TYPE_U4, BasicTypes::TypeUInt32},
+            {ELEMENT_TYPE_I8, BasicTypes::TypeInt64},
+            {ELEMENT_TYPE_U8, BasicTypes::TypeUInt64},
+            {ELEMENT_TYPE_I2, BasicTypes::TypeInt16},
+            {ELEMENT_TYPE_U2, BasicTypes::TypeUInt16}
+        };
+
+        auto findType = basicTypesMap.find(elemType);
+        if (findType == basicTypesMap.end())
+            return E_FAIL;
+        resultType = (int32_t)findType->second;
+
+        ToRelease<ICorDebugGenericValue> iCorGenValue;
+        IfFailRet(pValue->QueryInterface(IID_ICorDebugGenericValue, (LPVOID *) &iCorGenValue));
+        return iCorGenValue->GetValue(resultData);
+    }
+
+    HRESULT GetValueByOperandDataType(PVOID valueData, BasicTypes valueType, ICorDebugValue **ppValue, EvalData &ed)
+    {
+        if (valueType == BasicTypes::TypeString)
+        {
+            std::string String = to_utf8((WCHAR*)valueData);
+            return ed.pEvalHelpers->CreateString(ed.pThread, String, ppValue);
+        }
+
+        static std::unordered_map<BasicTypes, CorElementType> basicTypesMap
+        {
+            {BasicTypes::TypeBoolean, ELEMENT_TYPE_BOOLEAN},
+            {BasicTypes::TypeByte, ELEMENT_TYPE_U1},
+            {BasicTypes::TypeSByte, ELEMENT_TYPE_I1},
+            {BasicTypes::TypeChar, ELEMENT_TYPE_CHAR},
+            {BasicTypes::TypeDouble, ELEMENT_TYPE_R8},
+            {BasicTypes::TypeSingle, ELEMENT_TYPE_R4},
+            {BasicTypes::TypeInt32, ELEMENT_TYPE_I4},
+            {BasicTypes::TypeUInt32, ELEMENT_TYPE_U4},
+            {BasicTypes::TypeInt64, ELEMENT_TYPE_I8},
+            {BasicTypes::TypeUInt64, ELEMENT_TYPE_U8},
+            {BasicTypes::TypeInt16, ELEMENT_TYPE_I2},
+            {BasicTypes::TypeUInt16, ELEMENT_TYPE_U2}
+        };
+
+        auto findType = basicTypesMap.find(valueType);
+        if (findType == basicTypesMap.end())
+            return E_FAIL;
+
+        return CreatePrimitiveValue(ed.pThread, ppValue, findType->second, valueData);
+    }
+
+    HRESULT CallBinaryOperator(const std::string &opName, ICorDebugValue *pValue, ICorDebugValue *pType1Value, ICorDebugValue *pType2Value,
+                               ICorDebugValue **pResultValue, EvalData &ed)
+    {
+        HRESULT Status;
+        std::string typeName1;
+        CorElementType elemType1;
+        IfFailRet(GetArgData(pType1Value, typeName1, elemType1));
+        std::string typeName2;
+        CorElementType elemType2;
+        IfFailRet(GetArgData(pType2Value, typeName2, elemType2));
+        // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/operator-overloading
+        // A unary operator has one input parameter. A binary operator has two input parameters. In each case,
+        // at least one parameter must have type T or T? where T is the type that contains the operator declaration.
+        std::string typeName;
+        CorElementType elemType;
+        IfFailRet(GetArgData(pValue, typeName, elemType));
+        if ((elemType != elemType1 || typeName != typeName1) && (elemType != elemType2 || typeName != typeName2))
+            return E_INVALIDARG;
+
+        ToRelease<ICorDebugValue> iCorTypeValue;
+        auto CallOperator = [&](std::function<HRESULT(std::vector<Evaluator::ArgElementType>&)> cb)
+        {
+            ToRelease<ICorDebugFunction> iCorFunc;
+            ed.pEvaluator->WalkMethods(pValue, [&](
+                bool is_static,
+                const std::string &methodName,
+                Evaluator::ReturnElementType&,
+                std::vector<Evaluator::ArgElementType> &methodArgs,
+                Evaluator::GetFunctionCallback getFunction)
+            {
+                if (!is_static || methodArgs.size() != 2 || opName != methodName ||
+                    FAILED(cb(methodArgs)))
+                    return S_OK; // Return with success to continue walk.
+
+                IfFailRet(getFunction(&iCorFunc));
+
+                return E_ABORT; // Fast exit from cycle, since we already found iCorFunc.
+            });
+            if (!iCorFunc)
+                return E_INVALIDARG;
+
+            ICorDebugValue *ppArgsValue[] = {pType1Value, pType2Value};
+            return ed.pEvalHelpers->EvalFunction(ed.pThread, iCorFunc, nullptr, 0, ppArgsValue, 2, pResultValue, ed.evalFlags);
+        };
+
+        // Try execute operator for exact same type as provided values.
+        if (SUCCEEDED(CallOperator([&](std::vector<Evaluator::ArgElementType> &methodArgs)
+            {
+                return elemType1 != methodArgs[0].corType || typeName1 != methodArgs[0].typeName ||
+                       elemType2 != methodArgs[1].corType || typeName2 != methodArgs[1].typeName
+                       ? E_FAIL : S_OK;
+            })))
+            return S_OK;
+
+        // Try execute operator with implicit cast for second value.
+        // Make sure we don't cast "base" struct/class value for this case, since "... at least one parameter must have type T...".
+        if (elemType == elemType1 && typeName == typeName1 &&
+            SUCCEEDED(CallOperator([&](std::vector<Evaluator::ArgElementType> &methodArgs)
+            {
+                if (elemType1 != methodArgs[0].corType || typeName1 != methodArgs[0].typeName)
+                    return E_FAIL;
+
+                ToRelease<ICorDebugValue> iCorResultValue;
+                if (FAILED(CallCastOperator("op_Implicit", pType1Value, methodArgs[1].corType, methodArgs[1].typeName, pType2Value, &iCorResultValue, ed)) &&
+                    FAILED(CallCastOperator("op_Implicit", pType2Value, methodArgs[1].corType, methodArgs[1].typeName, pType2Value, &iCorResultValue, ed)))
+                    return E_FAIL;
+
+                IfFailRet(GetRealValueWithType(iCorResultValue, &iCorTypeValue));
+                pType2Value = iCorTypeValue.GetPtr();
+
+                return S_OK;
+            })))
+            return S_OK;
+
+        // Try execute operator with implicit cast for first value.
+        return CallOperator([&](std::vector<Evaluator::ArgElementType> &methodArgs)
+            {
+                if (elemType2 != methodArgs[1].corType || typeName2 != methodArgs[1].typeName)
+                    return E_FAIL;
+
+                ToRelease<ICorDebugValue> iCorResultValue;
+                if (FAILED(CallCastOperator("op_Implicit", pType1Value, methodArgs[0].corType, methodArgs[0].typeName, pType1Value, &iCorResultValue, ed)) &&
+                    FAILED(CallCastOperator("op_Implicit", pType2Value, methodArgs[0].corType, methodArgs[0].typeName, pType1Value, &iCorResultValue, ed)))
+                    return E_FAIL;
+
+                iCorTypeValue.Free();
+                IfFailRet(GetRealValueWithType(iCorResultValue, &iCorTypeValue));
+                pType1Value = iCorTypeValue.GetPtr();
+
+                return S_OK;
+            });
+    }
+
+    bool SupportedByCalculationDelegateType(CorElementType elemType)
+    {
+        static std::unordered_set<CorElementType> supportedElementTypes{
+            ELEMENT_TYPE_BOOLEAN,
+            ELEMENT_TYPE_U1,
+            ELEMENT_TYPE_I1,
+            ELEMENT_TYPE_CHAR,
+            ELEMENT_TYPE_R8,
+            ELEMENT_TYPE_R4,
+            ELEMENT_TYPE_I4,
+            ELEMENT_TYPE_U4,
+            ELEMENT_TYPE_I8,
+            ELEMENT_TYPE_U8,
+            ELEMENT_TYPE_I2,
+            ELEMENT_TYPE_U2,
+            ELEMENT_TYPE_STRING
+        };
+
+        return supportedElementTypes.find(elemType) != supportedElementTypes.end();
+    }
+
+    HRESULT CalculateTwoOparands(OperationType opType, std::list<EvalStackEntry> &evalStack, std::string &output, EvalData &ed)
+    {
+        HRESULT Status;
+        ToRelease<ICorDebugValue> iCorValue2;
+        IfFailRet(GetFrontStackEntryValue(&iCorValue2, evalStack, ed, output));
+        evalStack.pop_front();
+        ToRelease<ICorDebugValue> iCorRealValue2;
+        CorElementType elemType2;
+        IfFailRet(GetRealValueWithType(iCorValue2, &iCorRealValue2, &elemType2));
+
+        ToRelease<ICorDebugValue> iCorValue1;
+        IfFailRet(GetFrontStackEntryValue(&iCorValue1, evalStack, ed, output));
+        evalStack.front().ResetEntry();
+        ToRelease<ICorDebugValue> iCorRealValue1;
+        CorElementType elemType1;
+        IfFailRet(GetRealValueWithType(iCorValue1, &iCorRealValue1, &elemType1));
+
+        if (elemType1 == ELEMENT_TYPE_VALUETYPE || elemType2 == ELEMENT_TYPE_VALUETYPE ||
+            elemType1 == ELEMENT_TYPE_CLASS || elemType2 == ELEMENT_TYPE_CLASS)
+        {
+            static std::unordered_map<OperationType, std::pair<std::string,std::string>> opMap{
+                {OperationType::AddExpression, {"op_Addition", "+"}},
+                {OperationType::SubtractExpression, {"op_Subtraction", "-"}},
+                {OperationType::MultiplyExpression, {"op_Multiply", "*"}},
+                {OperationType::DivideExpression, {"op_Division", "/"}},
+                {OperationType::ModuloExpression, {"op_Modulus", "%"}},
+                {OperationType::RightShiftExpression, {"op_RightShift", ">>"}},
+                {OperationType::LeftShiftExpression, {"op_LeftShift", "<<"}},
+                {OperationType::LogicalAndExpression, {"op_LogicalAnd", "&&"}},
+                {OperationType::LogicalOrExpression, {"op_LogicalOr", "||"}},
+                {OperationType::ExclusiveOrExpression, {"op_ExclusiveOr", "^"}},
+                {OperationType::BitwiseAndExpression, {"op_BitwiseAnd", "&"}},
+                {OperationType::BitwiseOrExpression, {"op_BitwiseOr", "|"}},
+                {OperationType::EqualsExpression, {"op_Equality", "=="}},
+                {OperationType::NotEqualsExpression, {"op_Inequality", "!="}},
+                {OperationType::LessThanExpression, {"op_LessThan", "<"}},
+                {OperationType::GreaterThanExpression, {"op_GreaterThan", ">"}},
+                {OperationType::LessThanOrEqualExpression, {"op_LessThanOrEqual", "<="}},
+                {OperationType::GreaterThanOrEqualExpression, {"op_GreaterThanOrEqual", ">="}}
+            };
+
+            auto findOpName = opMap.find(opType);
+            if (findOpName == opMap.end())
+                return E_FAIL;
+
+            if (((elemType1 == ELEMENT_TYPE_VALUETYPE || elemType1 == ELEMENT_TYPE_CLASS) &&
+                    SUCCEEDED(CallBinaryOperator(findOpName->second.first, iCorRealValue1, iCorRealValue1, iCorRealValue2, &evalStack.front().iCorValue, ed))) ||
+                ((elemType2 == ELEMENT_TYPE_VALUETYPE || elemType2 == ELEMENT_TYPE_CLASS) &&
+                    SUCCEEDED(CallBinaryOperator(findOpName->second.first, iCorRealValue2, iCorRealValue1, iCorRealValue2, &evalStack.front().iCorValue, ed))))
+                return S_OK;
+
+            std::string typeRetName;
+            CorElementType elemRetType;
+            ToRelease<ICorDebugValue> iCorResultValue;
+            // Try to implicitly cast struct/class object into build-in type supported by CalculationDelegate().
+            if (SupportedByCalculationDelegateType(elemType2) && // First is ELEMENT_TYPE_VALUETYPE or ELEMENT_TYPE_CLASS
+                SUCCEEDED(GetArgData(iCorRealValue2, typeRetName, elemRetType)) &&
+                SUCCEEDED(CallCastOperator("op_Implicit", iCorRealValue1, elemRetType, typeRetName, iCorRealValue1, &iCorResultValue, ed)))
+            {
+                iCorRealValue1.Free();
+                IfFailRet(GetRealValueWithType(iCorResultValue, &iCorRealValue1, &elemType1));
+                // goto CalculationDelegate() related routine (see code below this 'if' statement scope)
+            }
+            else if (SupportedByCalculationDelegateType(elemType1) && // Second is ELEMENT_TYPE_VALUETYPE or ELEMENT_TYPE_CLASS
+                     SUCCEEDED(GetArgData(iCorRealValue1, typeRetName, elemRetType)) &&
+                     SUCCEEDED(CallCastOperator("op_Implicit", iCorRealValue2, elemRetType, typeRetName, iCorRealValue2, &iCorResultValue, ed)))
+            {
+                iCorRealValue2.Free();
+                IfFailRet(GetRealValueWithType(iCorResultValue, &iCorRealValue2, &elemType2));
+                // goto CalculationDelegate() related routine (see code below this 'if' statement scope)
+            }
+            else
+            {
+                std::string typeName1;
+                IfFailRet(TypePrinter::GetTypeOfValue(iCorRealValue1, typeName1));
+                std::string typeName2;
+                IfFailRet(TypePrinter::GetTypeOfValue(iCorRealValue2, typeName2));
+                output = "error CS0019: Operator '" + findOpName->second.second + "' cannot be applied to operands of type '" + typeName1 + "' and '" + typeName2 + "'";
+                return E_INVALIDARG;
+            }
+        }
+        else if (!SupportedByCalculationDelegateType(elemType1) || !SupportedByCalculationDelegateType(elemType2))
+            return E_INVALIDARG;
+
+        int64_t valueDataHolder1 = 0;
+        PVOID valueData1 = &valueDataHolder1;
+        int32_t valueType1 = 0;
+        int64_t valueDataHolder2 = 0;
+        PVOID valueData2 = &valueDataHolder2;
+        int32_t valueType2 = 0;
+        PVOID resultData = NULL;
+        int32_t resultType = 0;
+        if (SUCCEEDED(Status = GetOperandDataTypeByValue(iCorRealValue1, elemType1, valueData1, valueType1)) &&
+            SUCCEEDED(Status = GetOperandDataTypeByValue(iCorRealValue2, elemType2, valueData2, valueType2)) &&
+            SUCCEEDED(Status = Interop::CalculationDelegate(valueData1, valueType1, valueData2, valueType2, (int32_t)opType, resultType, &resultData, output)))
+        {
+            Status = GetValueByOperandDataType(resultData, (BasicTypes)resultType, &evalStack.front().iCorValue, ed);
+            if (resultType == (int32_t)BasicTypes::TypeString)
+                Interop::SysFreeString((BSTR)resultData);
+            else
+                Interop::CoTaskMemFree(resultData);
+        }
+
+        if (valueType1 == (int32_t)BasicTypes::TypeString && valueData1)
+            Interop::SysFreeString((BSTR)valueData1);
+
+        if (valueType2 == (int32_t)BasicTypes::TypeString && valueData2)
+            Interop::SysFreeString((BSTR)valueData2);
+
+        return Status;
+    }
+
+    HRESULT CalculateOneOparand(OperationType opType, std::list<EvalStackEntry> &evalStack, std::string &output, EvalData &ed)
+    {
+        HRESULT Status;
+        ToRelease<ICorDebugValue> iCorValue;
+        IfFailRet(GetFrontStackEntryValue(&iCorValue, evalStack, ed, output));
+        evalStack.front().ResetEntry(true); // Don't reset literal status.
+        ToRelease<ICorDebugValue> iCorRealValue;
+        CorElementType elemType;
+        IfFailRet(GetRealValueWithType(iCorValue, &iCorRealValue, &elemType));
+
+        if (elemType == ELEMENT_TYPE_VALUETYPE || elemType == ELEMENT_TYPE_CLASS)
+        {
+            static std::unordered_map<OperationType, std::pair<std::string,std::string>> opMap{
+                {OperationType::LogicalNotExpression, {"op_LogicalNot", "!"}},
+                {OperationType::BitwiseNotExpression, {"op_OnesComplement", "~"}},
+                {OperationType::UnaryPlusExpression, {"op_UnaryPlus", "+"}},
+                {OperationType::UnaryMinusExpression, {"op_UnaryNegation", "-"}}
+            };
+
+            auto findOpName = opMap.find(opType);
+            if (findOpName == opMap.end())
+                return E_FAIL;
+
+            if (SUCCEEDED(CallUnaryOperator(findOpName->second.first, iCorRealValue, &evalStack.front().iCorValue, ed)))
+                return S_OK;
+            else
+            {
+                std::string typeName;
+                IfFailRet(TypePrinter::GetTypeOfValue(iCorRealValue, typeName));
+                output = "error CS0023: Operator '" + findOpName->second.second + "' cannot be applied to operand of type '" + typeName + "'";
+                return E_INVALIDARG;
+            }
+        }
+        else if (!SupportedByCalculationDelegateType(elemType))
+            return E_INVALIDARG;
+
+        int64_t valueDataHolder1 = 0;
+        PVOID valueData1 = &valueDataHolder1;
+        int32_t valueType1 = 0;
+        // Note, we need fake second operand for delegate.
+        int64_t fakeValueData2 = 0;
+        PVOID resultData = NULL;
+        int32_t resultType = 0;
+        if (SUCCEEDED(Status = GetOperandDataTypeByValue(iCorRealValue, elemType, valueData1, valueType1)) &&
+            SUCCEEDED(Status = Interop::CalculationDelegate(valueData1, valueType1, &fakeValueData2, (int32_t)BasicTypes::TypeInt64, (int32_t)opType, resultType, &resultData, output)))
+        {
+            Status = GetValueByOperandDataType(resultData, (BasicTypes)resultType, &evalStack.front().iCorValue, ed);
+            if (resultType == (int32_t)BasicTypes::TypeString)
+                Interop::SysFreeString((BSTR)resultData);
+            else
+                Interop::CoTaskMemFree(resultData);
+        }
+
+        if (valueType1 == (int32_t)BasicTypes::TypeString && valueData1)
+            Interop::SysFreeString((BSTR)valueData1);
+
+        return Status;
+    }
+
 
     HRESULT IdentifierName(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
@@ -1011,110 +1333,92 @@ namespace
 
     HRESULT AddExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        // TODO uint32_t Flags = ((FormatF*)pArguments)->Flags;
-        return E_NOTIMPL;
+        return CalculateTwoOparands(OperationType::AddExpression, evalStack, output, ed);
     }
 
     HRESULT MultiplyExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        // TODO uint32_t Flags = ((FormatF*)pArguments)->Flags;
-        return E_NOTIMPL;
+        return CalculateTwoOparands(OperationType::MultiplyExpression, evalStack, output, ed);
     }
 
     HRESULT SubtractExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        // TODO uint32_t Flags = ((FormatF*)pArguments)->Flags;
-        return E_NOTIMPL;
+        return CalculateTwoOparands(OperationType::SubtractExpression, evalStack, output, ed);
     }
 
     HRESULT DivideExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        // TODO uint32_t Flags = ((FormatF*)pArguments)->Flags;
-        return E_NOTIMPL;
+        return CalculateTwoOparands(OperationType::DivideExpression, evalStack, output, ed);
     }
 
     HRESULT ModuloExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        // TODO uint32_t Flags = ((FormatF*)pArguments)->Flags;
-        return E_NOTIMPL;
+        return CalculateTwoOparands(OperationType::ModuloExpression, evalStack, output, ed);
     }
 
     HRESULT LeftShiftExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        // TODO uint32_t Flags = ((FormatF*)pArguments)->Flags;
-        return E_NOTIMPL;
+        return CalculateTwoOparands(OperationType::LeftShiftExpression, evalStack, output, ed);
     }
 
     HRESULT RightShiftExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        // TODO uint32_t Flags = ((FormatF*)pArguments)->Flags;
-        return E_NOTIMPL;
+        return CalculateTwoOparands(OperationType::RightShiftExpression, evalStack, output, ed);
     }
 
     HRESULT BitwiseAndExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        // TODO uint32_t Flags = ((FormatF*)pArguments)->Flags;
-        return E_NOTIMPL;
+        return CalculateTwoOparands(OperationType::BitwiseAndExpression, evalStack, output, ed);
     }
 
     HRESULT BitwiseOrExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        // TODO uint32_t Flags = ((FormatF*)pArguments)->Flags;
-        return E_NOTIMPL;
+        return CalculateTwoOparands(OperationType::BitwiseOrExpression, evalStack, output, ed);
     }
 
     HRESULT ExclusiveOrExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        // TODO uint32_t Flags = ((FormatF*)pArguments)->Flags;
-        return E_NOTIMPL;
+        return CalculateTwoOparands(OperationType::ExclusiveOrExpression, evalStack, output, ed);
     }
 
     HRESULT LogicalAndExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        // TODO uint32_t Flags = ((FormatF*)pArguments)->Flags;
-        return E_NOTIMPL;
+        return CalculateTwoOparands(OperationType::LogicalAndExpression, evalStack, output, ed);
     }
 
     HRESULT LogicalOrExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        // TODO uint32_t Flags = ((FormatF*)pArguments)->Flags;
-        return E_NOTIMPL;
+        return CalculateTwoOparands(OperationType::LogicalOrExpression, evalStack, output, ed);
     }
 
     HRESULT EqualsExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        // TODO uint32_t Flags = ((FormatF*)pArguments)->Flags;
-        return E_NOTIMPL;
+        return CalculateTwoOparands(OperationType::EqualsExpression, evalStack, output, ed);
     }
 
     HRESULT NotEqualsExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        // TODO uint32_t Flags = ((FormatF*)pArguments)->Flags;
-        return E_NOTIMPL;
+        return CalculateTwoOparands(OperationType::NotEqualsExpression, evalStack, output, ed);
     }
 
     HRESULT GreaterThanExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        // TODO uint32_t Flags = ((FormatF*)pArguments)->Flags;
-        return E_NOTIMPL;
+        return CalculateTwoOparands(OperationType::GreaterThanExpression, evalStack, output, ed);
     }
 
     HRESULT LessThanExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        // TODO uint32_t Flags = ((FormatF*)pArguments)->Flags;
-        return E_NOTIMPL;
+        return CalculateTwoOparands(OperationType::LessThanExpression, evalStack, output, ed);
     }
 
     HRESULT GreaterThanOrEqualExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        // TODO uint32_t Flags = ((FormatF*)pArguments)->Flags;
-        return E_NOTIMPL;
+        return CalculateTwoOparands(OperationType::GreaterThanOrEqualExpression, evalStack, output, ed);
     }
 
     HRESULT LessThanOrEqualExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        // TODO uint32_t Flags = ((FormatF*)pArguments)->Flags;
-        return E_NOTIMPL;
+        return CalculateTwoOparands(OperationType::LessThanOrEqualExpression, evalStack, output, ed);
     }
 
     HRESULT IsExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
@@ -1125,112 +1429,22 @@ namespace
 
     HRESULT UnaryPlusExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        HRESULT Status;
-        ToRelease<ICorDebugValue> iCorRefValue;
-        IfFailRet(GetFrontStackEntryValue(&iCorRefValue, evalStack, ed, output));
-        evalStack.front().ResetEntry(true);
-
-        ToRelease<ICorDebugValue> iCorValue;
-        IfFailRet(DereferenceAndUnboxValue(iCorRefValue, &iCorValue, nullptr));
-        CorElementType elemType;
-        IfFailRet(iCorValue->GetType(&elemType));
-
-        switch (elemType)
-        {
-            case ELEMENT_TYPE_CHAR:
-            case ELEMENT_TYPE_I1:
-            case ELEMENT_TYPE_U1:
-            case ELEMENT_TYPE_I2:
-            case ELEMENT_TYPE_U2:
-                return UnaryNumericPromotion(iCorValue, &evalStack.front().iCorValue, ed);
-
-            case ELEMENT_TYPE_I4:
-            case ELEMENT_TYPE_U4:
-            case ELEMENT_TYPE_I8:
-            case ELEMENT_TYPE_U8:
-            case ELEMENT_TYPE_R4:
-            case ELEMENT_TYPE_R8:
-                evalStack.front().iCorValue = iCorValue.Detach();
-                return S_OK;
-
-            case ELEMENT_TYPE_VALUETYPE:
-            case ELEMENT_TYPE_CLASS:
-                if (SUCCEEDED(CallUnaryOperator("op_UnaryPlus", iCorValue, &evalStack.front().iCorValue, ed)))
-                    return S_OK;
-                else
-                {
-                    std::string typeName;
-                    IfFailRet(TypePrinter::NameForTypeByValue(iCorValue, typeName));
-                    output = "error CS0023: Operator '+' cannot be applied to operand of type " + typeName;
-                    return E_INVALIDARG;
-                }
-
-            default:
-                return E_INVALIDARG;
-        }
+        return CalculateOneOparand(OperationType::UnaryPlusExpression, evalStack, output, ed);
     }
 
     HRESULT UnaryMinusExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        HRESULT Status;
-        ToRelease<ICorDebugValue> iCorRefValue;
-        IfFailRet(GetFrontStackEntryValue(&iCorRefValue, evalStack, ed, output));
-        evalStack.front().ResetEntry(true);
-
-        ToRelease<ICorDebugValue> iCorValue;
-        IfFailRet(DereferenceAndUnboxValue(iCorRefValue, &iCorValue, nullptr));
-        CorElementType elemType;
-        IfFailRet(iCorValue->GetType(&elemType));
-
-        switch (elemType)
-        {
-            case ELEMENT_TYPE_U8:
-                output = "error CS0023: Operator '-' cannot be applied to operand of type 'ulong'";
-                return E_INVALIDARG;
-
-            case ELEMENT_TYPE_CHAR:
-            case ELEMENT_TYPE_I1:
-            case ELEMENT_TYPE_U1:
-            case ELEMENT_TYPE_I2:
-            case ELEMENT_TYPE_U2:
-            case ELEMENT_TYPE_U4:
-                IfFailRet(UnaryNumericPromotion(iCorValue, &evalStack.front().iCorValue, ed));
-                return InvertNumber(evalStack.front().iCorValue);
-
-            case ELEMENT_TYPE_I4:
-            case ELEMENT_TYPE_I8:
-            case ELEMENT_TYPE_R4:
-            case ELEMENT_TYPE_R8:
-                evalStack.front().iCorValue = iCorValue.Detach();
-                return InvertNumber(evalStack.front().iCorValue);
-
-            case ELEMENT_TYPE_VALUETYPE:
-            case ELEMENT_TYPE_CLASS:
-                if (SUCCEEDED(CallUnaryOperator("op_UnaryNegation", iCorValue, &evalStack.front().iCorValue, ed)))
-                    return S_OK;
-                else
-                {
-                    std::string typeName;
-                    IfFailRet(TypePrinter::NameForTypeByValue(iCorValue, typeName));
-                    output = "error CS0023: Operator '-' cannot be applied to operand of type " + typeName;
-                    return E_INVALIDARG;
-                }
-
-            default:
-                return E_INVALIDARG;
-        }
+        return CalculateOneOparand(OperationType::UnaryMinusExpression, evalStack, output, ed);
     }
 
     HRESULT LogicalNotExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        // TODO uint32_t Flags = ((FormatF*)pArguments)->Flags;
-        return E_NOTIMPL;
+        return CalculateOneOparand(OperationType::LogicalNotExpression, evalStack, output, ed);
     }
 
     HRESULT BitwiseNotExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
     {
-        // TODO uint32_t Flags = ((FormatF*)pArguments)->Flags;
-        return E_NOTIMPL;
+        return CalculateOneOparand(OperationType::BitwiseNotExpression, evalStack, output, ed);
     }
 
     HRESULT TrueLiteralExpression(std::list<EvalStackEntry> &evalStack, PVOID pArguments, std::string &output, EvalData &ed)
@@ -1406,7 +1620,7 @@ HRESULT EvalStackMachine::Run(ICorDebugThread *pThread, FrameLevel frameLevel, i
         if (FAILED(Status = GetFrontStackEntryValue(&iCorValue, m_evalStack, m_evalData, output)))
             break;
 
-        Status = ImplicitCast(iCorValue, (*ppResultValue), m_evalStack.front().literal, m_evalData);
+        Status = ImplicitCast(iCorValue, *ppResultValue, m_evalStack.front().literal, m_evalData);
     }
     while (0);
 
index 1957b80b34443d829b43770047e6d25d25941254..9a8be28acfe3717f478b5e84b28d5470756057d9 100644 (file)
@@ -560,7 +560,7 @@ static HRESULT PrintArrayValue(ICorDebugValue *pValue, std::string &output)
     return S_OK;
 }
 
-static HRESULT PrintStringValue(ICorDebugValue * pValue, std::string &output)
+HRESULT PrintStringValue(ICorDebugValue * pValue, std::string &output)
 {
     HRESULT Status;
 
index bc63c900215c18842406a493c522414a3ccca7d1..a60a02fec9b7308904b30d5aef2b4af3d4989b7d 100644 (file)
@@ -12,6 +12,7 @@ namespace netcoredbg
 {
 
 HRESULT PrintValue(ICorDebugValue *pInputValue, std::string &output, bool escape = true);
+HRESULT PrintStringValue(ICorDebugValue * pValue, std::string &output);
 HRESULT DereferenceAndUnboxValue(ICorDebugValue * pValue, ICorDebugValue** ppOutputValue, BOOL * pIsNull = nullptr);
 
 } // namespace netcoredbg
index 0ac0492fbb14c9d6c8c582d67ef2efe1d8daebc2..f98a0f2dca8660b0d6889644c0f6851631065796 100644 (file)
@@ -5,13 +5,6 @@
 using System;
 using System.Collections.Generic;
 using System.Runtime.InteropServices;
-using Microsoft.CodeAnalysis.CSharp.Scripting;
-using Microsoft.CodeAnalysis.Scripting;
-using Microsoft.CodeAnalysis;
-using System.Reflection;
-using System.Dynamic;
-using Microsoft.CodeAnalysis.CSharp;
-using System.Text;
 
 namespace NetCoreDbg
 {
@@ -20,9 +13,7 @@ namespace NetCoreDbg
         //BasicTypes enum must be sync with enum from native part
         internal enum BasicTypes
         {
-            TypeCorValue = -1,
-            TypeObject = 0,
-            TypeBoolean,
+            TypeBoolean = 1,
             TypeByte,
             TypeSByte,
             TypeChar,
@@ -34,59 +25,60 @@ namespace NetCoreDbg
             TypeUInt64,
             TypeInt16,
             TypeUInt16,
-            TypeIntPtr,
-            TypeUIntPtr,
-            TypeDecimal,
             TypeString,
         };
 
         //OperationType enum must be sync with enum from native part
         internal enum OperationType
         {
-            Addition = 1,
-            Subtraction,
-            Multiplication,
-            Division,
-            Remainder,
-            BitwiseRightShift,
-            BitwiseLeftShift,
-            BitwiseComplement,
-            LogicalAnd,
-            LogicalOR,
-            LogicalXOR,
-            ConditionalLogicalAnd,
-            ConditionalLogicalOR,
-            LogicalNegation,
-            Equality,
-            Inequality,
-            LessThan,
-            GreaterThan,
-            LessThanOrEqual,
-            GreaterThanOrEqual
+            AddExpression = 1,
+            SubtractExpression,
+            MultiplyExpression,
+            DivideExpression,
+            ModuloExpression,
+            RightShiftExpression,
+            LeftShiftExpression,
+            BitwiseNotExpression,
+            LogicalAndExpression,
+            LogicalOrExpression,
+            ExclusiveOrExpression,
+            BitwiseAndExpression,
+            BitwiseOrExpression,
+            LogicalNotExpression,
+            EqualsExpression,
+            NotEqualsExpression,
+            LessThanExpression,
+            GreaterThanExpression,
+            LessThanOrEqualExpression,
+            GreaterThanOrEqualExpression,
+            UnaryPlusExpression,
+            UnaryMinusExpression
         };
 
         internal static Dictionary<OperationType, Func<object, object, object>> operationTypesMap = new Dictionary<OperationType, Func<object, object, object>>
         {
-            { OperationType.Addition, (object firstOp, object secondOp) => { return Addition(firstOp, secondOp); }},
-            { OperationType.Division, (object firstOp, object secondOp) => { return Division(firstOp, secondOp); }},
-            { OperationType.Multiplication, (object firstOp, object secondOp) => { return Multiplication(firstOp, secondOp); }},
-            { OperationType.Remainder, (object firstOp, object secondOp) => { return Remainder(firstOp, secondOp); }},
-            { OperationType.Subtraction, (object firstOp, object secondOp) => { return Subtraction(firstOp, secondOp); }},
-            { OperationType.BitwiseRightShift, (object firstOp, object secondOp) => { return BitwiseRightShift(firstOp, secondOp); }},
-            { OperationType.BitwiseLeftShift, (object firstOp, object secondOp) => { return BitwiseLeftShift(firstOp, secondOp); }},
-            { OperationType.BitwiseComplement, (object firstOp, object secondOp) => { return BitwiseComplement(firstOp); }},
-            { OperationType.LogicalAnd, (object firstOp, object secondOp) => { return LogicalAnd(firstOp, secondOp); }},
-            { OperationType.LogicalOR, (object firstOp, object secondOp) => { return LogicalOR(firstOp, secondOp); }},
-            { OperationType.LogicalXOR, (object firstOp, object secondOp) => { return LogicalXOR(firstOp, secondOp); }},
-            { OperationType.ConditionalLogicalAnd, (object firstOp, object secondOp) => { return ConditionalLogicalAnd(firstOp, secondOp); }},
-            { OperationType.ConditionalLogicalOR, (object firstOp, object secondOp) => { return ConditionalLogicalOR(firstOp, secondOp); }},
-            { OperationType.LogicalNegation, (object firstOp, object secondOp) => { return LogicalNegation(firstOp); }},
-            { OperationType.Equality, (object firstOp, object secondOp) => { return Equality(firstOp, secondOp); }},
-            { OperationType.Inequality, (object firstOp, object secondOp) => { return Inequality(firstOp, secondOp); }},
-            { OperationType.LessThan, (object firstOp, object secondOp) => { return LessThan(firstOp, secondOp); }},
-            { OperationType.GreaterThan, (object firstOp, object secondOp) => { return GreaterThan(firstOp, secondOp); }},
-            { OperationType.LessThanOrEqual, (object firstOp, object secondOp) => { return LessThanOrEqual(firstOp, secondOp); }},
-            { OperationType.GreaterThanOrEqual, (object firstOp, object secondOp) => { return GreaterThanOrEqual(firstOp, secondOp); }}
+            { OperationType.AddExpression, (object firstOp, object secondOp) => { return AddExpression(firstOp, secondOp); }},
+            { OperationType.DivideExpression, (object firstOp, object secondOp) => { return DivideExpression(firstOp, secondOp); }},
+            { OperationType.MultiplyExpression, (object firstOp, object secondOp) => { return MultiplyExpression(firstOp, secondOp); }},
+            { OperationType.ModuloExpression, (object firstOp, object secondOp) => { return ModuloExpression(firstOp, secondOp); }},
+            { OperationType.SubtractExpression, (object firstOp, object secondOp) => { return SubtractExpression(firstOp, secondOp); }},
+            { OperationType.RightShiftExpression, (object firstOp, object secondOp) => { return RightShiftExpression(firstOp, secondOp); }},
+            { OperationType.LeftShiftExpression, (object firstOp, object secondOp) => { return LeftShiftExpression(firstOp, secondOp); }},
+            { OperationType.BitwiseNotExpression, (object firstOp, object secondOp) => { return BitwiseNotExpression(firstOp); }},
+            { OperationType.LogicalAndExpression, (object firstOp, object secondOp) => { return LogicalAndExpression(firstOp, secondOp); }},
+            { OperationType.LogicalOrExpression, (object firstOp, object secondOp) => { return LogicalOrExpression(firstOp, secondOp); }},
+            { OperationType.ExclusiveOrExpression, (object firstOp, object secondOp) => { return ExclusiveOrExpression(firstOp, secondOp); }},
+            { OperationType.BitwiseAndExpression, (object firstOp, object secondOp) => { return BitwiseAndExpression(firstOp, secondOp); }},
+            { OperationType.BitwiseOrExpression, (object firstOp, object secondOp) => { return BitwiseOrExpression(firstOp, secondOp); }},
+            { OperationType.LogicalNotExpression, (object firstOp, object secondOp) => { return LogicalNotExpression(firstOp); }},
+            { OperationType.EqualsExpression, (object firstOp, object secondOp) => { return EqualsExpression(firstOp, secondOp); }},
+            { OperationType.NotEqualsExpression, (object firstOp, object secondOp) => { return NotEqualsExpression(firstOp, secondOp); }},
+            { OperationType.LessThanExpression, (object firstOp, object secondOp) => { return LessThanExpression(firstOp, secondOp); }},
+            { OperationType.GreaterThanExpression, (object firstOp, object secondOp) => { return GreaterThanExpression(firstOp, secondOp); }},
+            { OperationType.LessThanOrEqualExpression, (object firstOp, object secondOp) => { return LessThanOrEqualExpression(firstOp, secondOp); }},
+            { OperationType.GreaterThanOrEqualExpression, (object firstOp, object secondOp) => { return GreaterThanOrEqualExpression(firstOp, secondOp); }},
+            { OperationType.UnaryPlusExpression, (object firstOp, object secondOp) => { return UnaryPlusExpression(firstOp); }},
+            { OperationType.UnaryMinusExpression, (object firstOp, object secondOp) => { return UnaryMinusExpression(firstOp); }}
         };
 
         internal static Dictionary<BasicTypes, Func<byte[], object>> typesMap = new Dictionary<BasicTypes, Func<byte[], object>>
@@ -118,19 +110,10 @@ namespace NetCoreDbg
             { typeof(Single), BasicTypes.TypeSingle },
             { typeof(UInt16), BasicTypes.TypeUInt16 },
             { typeof(UInt32), BasicTypes.TypeUInt32 },
-            { typeof(UInt64), BasicTypes.TypeUInt64 }
+            { typeof(UInt64), BasicTypes.TypeUInt64 },
+            { typeof(String), BasicTypes.TypeString }
         };
 
-        /// <summary>
-        /// Convert Single(float) type to Int32
-        /// </summary>
-        /// <param name="value">float value</param>
-        /// <returns></returns>
-        private static unsafe int floatToInt32Bits(float value)
-        {
-            return *((int*)&value);
-        }
-
         /// <summary>
         /// Converts value â€‹â€‹to a IntPtr to IntPtr
         /// </summary>
@@ -138,41 +121,37 @@ namespace NetCoreDbg
         /// <returns></returns>
         private static IntPtr valueToPtr(object value) 
         {
-            IntPtr result = IntPtr.Zero;
-            IntPtr ptr = IntPtr.Zero;
-            Int64 newValue = 0;
             if (value.GetType() == typeof(string))
+                return Marshal.StringToBSTR(value as string);
+
+            dynamic dynValue = value;
+            byte[] bytes = BitConverter.GetBytes(dynValue);
+            IntPtr ptr = Marshal.AllocCoTaskMem(bytes.Length);
+            for (int i = 0; i < bytes.Length; i++)
             {
-                result = Marshal.AllocHGlobal(Marshal.SizeOf(newValue));
-                ptr = Marshal.StringToBSTR(value as string);
-            }
-            else 
-            {
-                if (value.GetType() == typeof(float) || value.GetType() == typeof(double))
-                    newValue = value.GetType() == typeof(float) ? Convert.ToInt64(floatToInt32Bits(Convert.ToSingle(value))) : BitConverter.DoubleToInt64Bits(Convert.ToDouble(value));
-                else
-                    newValue = Convert.ToInt64(value);
-                var size = Marshal.SizeOf(newValue);
-                result = Marshal.AllocHGlobal(Marshal.SizeOf(newValue));
-                ptr = Marshal.AllocHGlobal(size);
-                Marshal.WriteInt64(ptr, newValue);
+                Marshal.WriteByte(ptr, i, bytes[i]);
             }
-            Marshal.WriteIntPtr(result, ptr);
-            return result;
+            return ptr;
         }
 
         private static object ptrToValue(IntPtr ptr, int type) 
         {
             if ((BasicTypes)type == BasicTypes.TypeString)
-                return Marshal.PtrToStringAuto(ptr);
+            {
+                if (ptr == IntPtr.Zero)
+                    return String.Empty;
+                else
+                    return Marshal.PtrToStringBSTR(ptr);
+            }
 
             var intValue = Marshal.ReadInt64(ptr);
             var bytesArray = BitConverter.GetBytes(intValue);
             return typesMap[(BasicTypes)type](bytesArray);
         }
 
-        internal static RetCode CalculationDelegate(IntPtr firstOpPtr, int firstType, IntPtr secondOpPtr, int secondType, int operation, int resultType, out IntPtr result, out IntPtr errorText)
+        internal static RetCode CalculationDelegate(IntPtr firstOpPtr, int firstType, IntPtr secondOpPtr, int secondType, int operation, out int resultType, out IntPtr result, out IntPtr errorText)
         {
+            resultType = 0;
             result = IntPtr.Zero;
             errorText = IntPtr.Zero;
 
@@ -184,118 +163,123 @@ namespace NetCoreDbg
                 resultType = (int)basicTypesMap[operationResult.GetType()];
                 result = valueToPtr(operationResult);
             }
-            catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException ex)
-            {
-                errorText = Marshal.StringToBSTR(ex.ToString());
-                return RetCode.Exception;
-            }
             catch (System.Exception ex)
             {
-                errorText = Marshal.StringToBSTR(ex.ToString());
+                errorText = Marshal.StringToBSTR("error: " + ex.Message);
                 return RetCode.Exception;
             }
 
             return RetCode.OK;
         }
 
-        private static object Addition(dynamic first, dynamic second)
+        private static object AddExpression(dynamic first, dynamic second)
         {
             return first + second;
         }
 
-        private static object Subtraction(dynamic first, dynamic second)
+        private static object SubtractExpression(dynamic first, dynamic second)
         {
             return first - second;
         }
 
-        private static object Multiplication(dynamic first, dynamic second)
+        private static object MultiplyExpression(dynamic first, dynamic second)
         {
             return first * second;
         }
 
-        private static object Division(dynamic first, dynamic second)
+        private static object DivideExpression(dynamic first, dynamic second)
         {
             return first / second;
         }
 
-        private static object Remainder(dynamic first, dynamic second)
+        private static object ModuloExpression(dynamic first, dynamic second)
         {
             return first % second;
         }
 
-        private static object BitwiseRightShift(dynamic first, dynamic second) 
+        private static object RightShiftExpression(dynamic first, dynamic second) 
         {
             return first >> second;
         }
 
-        private static object BitwiseLeftShift(dynamic first, dynamic second)
+        private static object LeftShiftExpression(dynamic first, dynamic second)
         {
             return first << second;
         }
 
-        private static object BitwiseComplement(dynamic first)
+        private static object BitwiseNotExpression(dynamic first)
         {
             return ~first;
         }
 
-        private static object LogicalAnd(dynamic first, dynamic second)
+        private static object ExclusiveOrExpression(dynamic first, dynamic second)
         {
-            return first & second;
+            return first ^ second;
         }
 
-        private static object LogicalOR(dynamic first, dynamic second)
+        private static object BitwiseAndExpression(dynamic first, dynamic second)
         {
-            return first | second;
+            return first & second;
         }
 
-        private static object LogicalXOR(dynamic first, dynamic second)
+        private static object BitwiseOrExpression(dynamic first, dynamic second)
         {
-            return first ^ second;
+            return first | second;
         }
 
-        private static bool ConditionalLogicalAnd(dynamic first, dynamic second)
+        private static bool LogicalAndExpression(dynamic first, dynamic second)
         {
             return first && second;
         }
 
-        private static bool ConditionalLogicalOR(dynamic first, dynamic second)
+        private static bool LogicalOrExpression(dynamic first, dynamic second)
         {
             return first || second;
         }
 
-        private static object LogicalNegation(dynamic first)
+        private static object LogicalNotExpression(dynamic first)
         {
             return !first;
         }
 
-        private static bool Equality(dynamic first, dynamic second)
+        private static bool EqualsExpression(dynamic first, dynamic second)
         {
             return first == second;
         }
 
-        private static bool Inequality(dynamic first, dynamic second) 
+        private static bool NotEqualsExpression(dynamic first, dynamic second) 
         {
             return first != second;
         }
 
-        private static bool LessThan(dynamic first, dynamic second)
+        private static bool LessThanExpression(dynamic first, dynamic second)
         {
             return first < second;
         }
 
-        private static bool GreaterThan(dynamic first, dynamic second)
+        private static bool GreaterThanExpression(dynamic first, dynamic second)
         {
             return first > second;
         }
 
-        private static bool LessThanOrEqual(dynamic first, dynamic second)
+        private static bool LessThanOrEqualExpression(dynamic first, dynamic second)
         {
             return first <= second;
         }
 
-        private static bool GreaterThanOrEqual(dynamic first, dynamic second)
+        private static bool GreaterThanOrEqualExpression(dynamic first, dynamic second)
         {
             return first >= second;
         }
+
+        private static object UnaryPlusExpression(dynamic first)
+        {
+            return +first;
+        }
+
+        private static object UnaryMinusExpression(dynamic first)
+        {
+            return -first;
+        }
     }
 }
index 3bc5fc5fc250c6819edd21f5bbafba72b23c131b..016f4299c22f1d592709caf2b0e68753b74cfd02 100644 (file)
@@ -685,34 +685,35 @@ namespace NetCoreDbg
                         case SyntaxKind.MemberBindingExpression:
                         case SyntaxKind.UnaryPlusExpression:
                         case SyntaxKind.UnaryMinusExpression:
-/* TODO
-                        case SyntaxKind.QualifiedName:
-                        case SyntaxKind.AliasQualifiedName:
-                        case SyntaxKind.ConditionalExpression:
-                        case SyntaxKind.PointerMemberAccessExpression:
-                        case SyntaxKind.CastExpression:
-                        case SyntaxKind.AsExpression:
                         case SyntaxKind.AddExpression:
                         case SyntaxKind.MultiplyExpression:
                         case SyntaxKind.SubtractExpression:
                         case SyntaxKind.DivideExpression:
                         case SyntaxKind.ModuloExpression:
-                        case SyntaxKind.LeftShiftExpression:
                         case SyntaxKind.RightShiftExpression:
-                        case SyntaxKind.BitwiseAndExpression:
-                        case SyntaxKind.BitwiseOrExpression:
-                        case SyntaxKind.ExclusiveOrExpression:
+                        case SyntaxKind.LeftShiftExpression:
+                        case SyntaxKind.BitwiseNotExpression:
                         case SyntaxKind.LogicalAndExpression:
                         case SyntaxKind.LogicalOrExpression:
+                        case SyntaxKind.ExclusiveOrExpression:
+                        case SyntaxKind.BitwiseAndExpression:
+                        case SyntaxKind.BitwiseOrExpression:
+                        case SyntaxKind.LogicalNotExpression:
                         case SyntaxKind.EqualsExpression:
                         case SyntaxKind.NotEqualsExpression:
                         case SyntaxKind.GreaterThanExpression:
                         case SyntaxKind.LessThanExpression:
                         case SyntaxKind.GreaterThanOrEqualExpression:
                         case SyntaxKind.LessThanOrEqualExpression:
+
+/* TODO
+                        case SyntaxKind.QualifiedName:
+                        case SyntaxKind.AliasQualifiedName:
+                        case SyntaxKind.ConditionalExpression:
+                        case SyntaxKind.PointerMemberAccessExpression:
+                        case SyntaxKind.CastExpression:
+                        case SyntaxKind.AsExpression:
                         case SyntaxKind.IsExpression:
-                        case SyntaxKind.LogicalNotExpression:
-                        case SyntaxKind.BitwiseNotExpression:
                         case SyntaxKind.PreIncrementExpression:
                         case SyntaxKind.PostIncrementExpression:
                         case SyntaxKind.PreDecrementExpression:
index 09ae5e48b719238e74c033874f7660482a8a4495..4b2e16c1d5a9734f31863460d3dc6b5b460ba177 100644 (file)
@@ -66,30 +66,6 @@ namespace Interop
         }
     };
 
-    // Keep in sync with OperationType enum in Evaluation.cs
-    enum class OperationType
-    {
-        Addition = 1,
-        Subtraction,
-        Multiplication,
-        Division,
-        Remainder,
-        BitwiseRightShift,
-        BitwiseLeftShift,
-        BitwiseComplement,
-        LogicalAnd,
-        LogicalOR,
-        LogicalXOR,
-        ConditionalLogicalAnd,
-        ConditionalLogicalOR,
-        LogicalNegation,
-        Equality,
-        Inequality,
-        LessThan,
-        GreaterThan,
-        LessThanOrEqual,
-        GreaterThanOrEqual
-    };
     struct AsyncAwaitInfoBlock
     {
         uint32_t yield_offset;
index f600a642de007f2c26aec3e75fa1e58d2734d4c4..3112e2886a84cb7cac55cab310747528972827e5 100644 (file)
@@ -210,6 +210,19 @@ namespace NetcoreDbgTest.Script
             return ((MIConst)res["name"]).CString;
         }
 
+        public string GetAndCheckValue(string caller_trace, string ExpectedResult1, string ExpectedResult2, string ExpectedType, string Expression)
+        {
+            var res = MIDebugger.Request(String.Format("-var-create - * \"{0}\"", Expression));
+            Assert.Equal(MIResultClass.Done, res.Class, @"__FILE__:__LINE__"+"\n"+caller_trace);
+
+            Assert.Equal(Expression, ((MIConst)res["exp"]).CString, @"__FILE__:__LINE__"+"\n"+caller_trace);
+            Assert.Equal(ExpectedType, ((MIConst)res["type"]).CString, @"__FILE__:__LINE__"+"\n"+caller_trace);
+            Assert.True(ExpectedResult1 == ((MIConst)res["value"]).CString ||
+                        ExpectedResult2 == ((MIConst)res["value"]).CString, @"__FILE__:__LINE__"+"\n"+caller_trace);
+
+            return ((MIConst)res["name"]).CString;
+        }
+
         public void CheckErrorAtRequest(string caller_trace, string Expression, string errMsgStart)
         {
             var res = MIDebugger.Request(String.Format("-var-create - * \"{0}\"", Expression));
@@ -542,6 +555,89 @@ namespace MITestEvaluate
         TWO
     };
 
+    public class TestOperators1
+    {
+        public int data;
+        public TestOperators1(int data_)
+        {
+            data = data_;
+        }
+
+        public static implicit operator TestOperators1(int value) => new TestOperators1(value);
+
+        public static int operator +(TestOperators1 d1, int d2) => 55;
+        public static int operator +(int d1, TestOperators1 d2) => 66;
+
+        public static int operator ~(TestOperators1 d1) => ~d1.data;
+        public static bool operator !(TestOperators1 d1) => true;
+        public static int operator +(TestOperators1 d1, TestOperators1 d2) => d1.data + d2.data;
+        public static int operator -(TestOperators1 d1, TestOperators1 d2) => d1.data - d2.data;
+        public static int operator *(TestOperators1 d1, TestOperators1 d2) => d1.data * d2.data;
+        public static int operator /(TestOperators1 d1, TestOperators1 d2) => d1.data / d2.data;
+        public static int operator %(TestOperators1 d1, TestOperators1 d2) => d1.data % d2.data;
+        public static int operator ^(TestOperators1 d1, TestOperators1 d2) => d1.data ^ d2.data;
+        public static int operator &(TestOperators1 d1, TestOperators1 d2) => d1.data & d2.data;
+        public static int operator |(TestOperators1 d1, TestOperators1 d2) => d1.data | d2.data;
+        public static int operator >>(TestOperators1 d1, int d2) => d1.data >> d2;
+        public static int operator <<(TestOperators1 d1, int d2) => d1.data << d2;
+        public static bool operator ==(TestOperators1 d1, TestOperators1 d2) => d1.data == d2.data;
+        public static bool operator !=(TestOperators1 d1, TestOperators1 d2) => d1.data != d2.data;
+        public static bool operator <(TestOperators1 d1, TestOperators1 d2) => d1.data < d2.data;
+        public static bool operator <=(TestOperators1 d1, TestOperators1 d2) => d1.data <= d2.data;
+        public static bool operator >(TestOperators1 d1, TestOperators1 d2) => d1.data > d2.data;
+        public static bool operator >=(TestOperators1 d1, TestOperators1 d2) => d1.data >= d2.data;
+    }
+
+    public struct TestOperators2
+    {
+        public int data;
+        public TestOperators2(int data_)
+        {
+            data = data_;
+        }
+        public static implicit operator TestOperators2(int value) => new TestOperators2(value);
+
+        public static int operator +(TestOperators2 d1, int d2) => 55;
+        public static int operator +(int d1, TestOperators2 d2) => 66;
+
+        public static int operator ~(TestOperators2 d1) => ~d1.data;
+        public static bool operator !(TestOperators2 d1) => true;
+        public static int operator +(TestOperators2 d1, TestOperators2 d2) => d1.data + d2.data;
+        public static int operator -(TestOperators2 d1, TestOperators2 d2) => d1.data - d2.data;
+        public static int operator *(TestOperators2 d1, TestOperators2 d2) => d1.data * d2.data;
+        public static int operator /(TestOperators2 d1, TestOperators2 d2) => d1.data / d2.data;
+        public static int operator %(TestOperators2 d1, TestOperators2 d2) => d1.data % d2.data;
+        public static int operator ^(TestOperators2 d1, TestOperators2 d2) => d1.data ^ d2.data;
+        public static int operator &(TestOperators2 d1, TestOperators2 d2) => d1.data & d2.data;
+        public static int operator |(TestOperators2 d1, TestOperators2 d2) => d1.data | d2.data;
+        public static int operator >>(TestOperators2 d1, int d2) => d1.data >> d2;
+        public static int operator <<(TestOperators2 d1, int d2) => d1.data << d2;
+        public static bool operator ==(TestOperators2 d1, TestOperators2 d2) => d1.data == d2.data;
+        public static bool operator !=(TestOperators2 d1, TestOperators2 d2) => d1.data != d2.data;
+        public static bool operator <(TestOperators2 d1, TestOperators2 d2) => d1.data < d2.data;
+        public static bool operator <=(TestOperators2 d1, TestOperators2 d2) => d1.data <= d2.data;
+        public static bool operator >(TestOperators2 d1, TestOperators2 d2) => d1.data > d2.data;
+        public static bool operator >=(TestOperators2 d1, TestOperators2 d2) => d1.data >= d2.data;
+    }
+
+    public struct TestOperators3
+    {
+        public int data;
+        public TestOperators3(int data_)
+        {
+            data = data_;
+        }
+
+        // Note, in order to test that was used proper operator, we use fixed return here.
+
+        public static implicit operator int(TestOperators3 value) => 777;
+
+        public static int operator +(TestOperators3 d1, int d2) => 555;
+        public static int operator +(int d1, TestOperators3 d2) => 666;
+        public static int operator >>(TestOperators3 d1, int d2) => 777;
+        public static int operator <<(TestOperators3 d1, int d2) => 888;
+    }
+
     class Program
     {
         int int_i = 505;
@@ -645,28 +741,227 @@ namespace MITestEvaluate
                 Context.Continue(@"__FILE__:__LINE__");
             });
 
-
-            int int_i1 = 5;
-            int int_i2 = 5;
-            string str_s1 = "one";
-            string str_s2 = "two";
+            // Test expression calculation.
+            TestOperators1 testClass = new TestOperators1(12);
+            TestOperators2 testStruct;
+            TestOperators3 testStruct2;
+            testStruct.data = 5;
+            string testString1 = null;
+            string testString2 = "test";
             int break_line2 = 1;                                                                    Label.Breakpoint("BREAK2");
 
             Label.Checkpoint("expression_test", "static_test", (Object context) => {
                 Context Context = (Context)context;
                 Context.WasBreakpointHit(@"__FILE__:__LINE__", "BREAK2");
-/*
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", "2", "int", "1 + 1");
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", "6", "int", "int_i1 + 1");
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", "10", "int", "int_i1 + int_i2");
 
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"onetwo\\\"", "", "\\\"one\\\" + \\\"two\\\"");
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"onetwo\\\"", "", "str_s1 + \\\"two\\\"");
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"onetwo\\\"", "", "str_s1 + str_s2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "2", "int", "1 + 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "2", "float", "1u + 1f");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "2", "long", "1u + 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "2", "decimal", "1m + 1m");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "2", "decimal", "1m + 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "2", "decimal", "1 + 1m");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "66", "int", "1 + testClass");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "55", "int", "testClass + 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "66", "int", "1 + testStruct");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "55", "int", "testStruct + 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "666", "int", "1 + testStruct2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "555", "int", "testStruct2 + 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"stringC\\\"", "string", "\\\"string\\\" + 'C'");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"test\\\"", "string", "testString1 + testString2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"test\\\"", "string", "testString2 + testString1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"\\\"", "string", "testString1 + testString1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"testtest\\\"", "string", "testString2 + testString2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"test\\\"", "string", "\\\"\\\" + \\\"test\\\"");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"test\\\"", "string", "\\\"test\\\" + \\\"\\\"");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"\\\"", "string", "\\\"\\\" + \\\"\\\"");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"testtest\\\"", "string", "\\\"test\\\" + \\\"test\\\"");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1UL + 1", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "true + 1", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1 + not_var", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1u + testClass", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "testClass + 1u", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1u + testStruct", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "testStruct + 1u", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "testClass + testStruct", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "testStruct + testClass", "error CS0019");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "2", "int", "3 - 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "2", "decimal", "3m - 1m");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "true - 1", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1 - not_var", "error");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "-11", "int", "1 - testClass");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "11", "int", "testClass - 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "-4", "int", "1 - testStruct");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "4", "int", "testStruct - 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "-776", "int", "1 - testStruct2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "776", "int", "testStruct2 - 1");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "4", "int", "2 * 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "4", "decimal", "2m * 2m");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "true * 2", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1 * not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "1", "int", "2 / 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "1", "decimal", "2m / 2m");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "true / 2", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1 / not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "0", "int", "2 % 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "0", "decimal", "2m % 2m");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "true % 2", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1 % not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "4", "int", "1 << 2");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1m << 2m", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "true << 2", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1 << not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "1", "int", "4 >> 2");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1m >> 2m", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "true >> 2", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1 >> not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "-2", "int", "~1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "-13", "int", "~testClass");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "-6", "int", "~testStruct");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "~1m", "error CS0023");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "~true", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "~not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "true", "bool", "true && true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "true && false");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "false && true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "false && false");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1 && 1", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1m && 1m", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "true && not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "true", "bool", "true || true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "true", "bool", "true || false");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "true", "bool", "false || true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "false || false");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1 || 1", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1m || 1m", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "true || not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "true ^ true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "true", "bool", "true ^ false");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "true", "bool", "false ^ true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "false ^ false");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "0", "int", "1 ^ 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "1", "int", "1 ^ 0");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "1", "int", "0 ^ 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "0", "int", "0 ^ 0");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "14", "int", "testClass ^ 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "14", "int", "2 ^ testClass");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "0", "int", "testClass ^ testClass");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "7", "int", "testStruct ^ 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "7", "int", "2 ^ testStruct");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "0", "int", "testStruct ^ testStruct");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "testStruct2 ^ testStruct2", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1m ^ 1m", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1 ^ not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "true", "bool", "true & true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "true & false");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "false & true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "false & false");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "1", "int", "3 & 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "0", "int", "5 & 8");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "0", "int", "10 & 0");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "0", "int", "0 & 7");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "0", "int", "0 & 0");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "0", "int", "testClass & 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "0", "int", "2 & testClass");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "12", "int", "testClass & testClass");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "0", "int", "testStruct & 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "0", "int", "2 & testStruct");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "5", "int", "testStruct & testStruct");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "testStruct2 & testStruct2", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1m & 1m", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1 & not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "true", "bool", "true | true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "true", "bool", "true | false");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "true", "bool", "false | true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "false | false");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "13", "int", "5 | 8");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "10", "int", "10 | 0");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "7", "int", "0 | 7");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "0", "int", "0 | 0");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "14", "int", "testClass | 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "14", "int", "2 | testClass");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "12", "int", "testClass | testClass");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "7", "int", "testStruct | 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "7", "int", "2 | testStruct");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "5", "int", "testStruct | testStruct");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "testStruct2 | testStruct2", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1m | 1m", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1 | not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "!true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "true", "bool", "!testClass");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "true", "bool", "!testStruct");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "!1", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "!1m", "error CS0023");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "!not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "true", "bool", "1 == 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "2 == 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "2m == 1m");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1 == not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "1 != 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "true", "bool", "2 != 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "true", "bool", "2m != 1m");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1 != not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "true", "bool", "1 < 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "1 < 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "1m < 1m");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "true < false", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1 < not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "true", "bool", "2 > 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "1 > 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "1m > 1m");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "true > false", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1 > not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "true", "bool", "1 <= 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "true", "bool", "1 <= 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "1 <= 0");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "1m <= 0m");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "true <= false", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1 <= not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "true", "bool", "2 >= 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "true", "bool", "1 >= 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "0 >= 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "false", "bool", "0m >= 1m");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "true >= false", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1 >= not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "4", "int", "2 << 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "888", "int", "testStruct2 << testStruct2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "24", "int", "testClass << 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "10", "int", "testStruct << 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "20", "int", "testStruct << 2");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1f << 1", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1f << 1f", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "testClass << testClass", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "testStruct << testStruct", "error CS0019");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "2", "int", "4 >> 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "777", "int", "testStruct2 >> testStruct2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "6", "int", "testClass >> 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "2", "int", "testStruct >> 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "1", "int", "testStruct >> 2");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1f >> 1", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1f >> 1f", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "testClass >> testClass", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "testStruct >> testStruct", "error CS0019");
 
-                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "int_i1 +/ int_i2", "error CS1525:");
-                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "1 + not_var", "System.AggregateException"); // error
-*/
                 Context.Continue(@"__FILE__:__LINE__");
             });
 
@@ -952,12 +1247,12 @@ namespace MITestEvaluate
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", "10", "int", "TestCallParent.GetNumber()");
 
                 // Call built-in types methods.
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"1.01\\\"", "string", "1.01M.ToString()");
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"1.01\\\"", "string", "decimalToString.ToString()");
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"2.02\\\"", "string", "2.02.ToString()");
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"2.02\\\"", "string", "doubleToString.ToString()");
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"3.03\\\"", "string", "3.03f.ToString()");
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"3.03\\\"", "string", "floatToString.ToString()");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"1.01\\\"", "\\\"1,01\\\"", "string", "1.01M.ToString()");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"1.01\\\"", "\\\"1,01\\\"", "string", "decimalToString.ToString()");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"2.02\\\"", "\\\"2,02\\\"", "string", "2.02.ToString()");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"2.02\\\"", "\\\"2,02\\\"", "string", "doubleToString.ToString()");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"3.03\\\"", "\\\"3,03\\\"", "string", "3.03f.ToString()");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"3.03\\\"", "\\\"3,03\\\"", "string", "floatToString.ToString()");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"c\\\"", "string", "'c'.ToString()");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"c\\\"", "string", "charToString.ToString()");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", "\\\"True\\\"", "string", "boolToString.ToString()");
@@ -1042,8 +1337,8 @@ namespace MITestEvaluate
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", "8", "long", "-longUnary");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", "8", "ulong", "+8UL");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", "8", "ulong", "+ulongUnary");
-                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "-8UL", "error CS0023");
-                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "-ulongUnary", "error CS0023");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "-8UL", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", "-ulongUnary", "error");
 
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", "10.5", "decimal", "+10.5m");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", "1.01", "decimal", "+decimalUnary");
index 3c4646d8b340b4cc33d026eacda9e306e702d0eb..e186bd4161fdb4a9a9795a2641d6bb0bd19ae0f7 100644 (file)
@@ -198,6 +198,22 @@ namespace NetcoreDbgTest.Script
             Assert.Equal(ExpectedType, evaluateResponse.body.type, @"__FILE__:__LINE__"+"\n"+caller_trace);
         }
 
+        public void GetAndCheckValue(string caller_trace, Int64 frameId, string ExpectedResult1, string ExpectedResult2, string ExpectedType, string Expression)
+        {
+            EvaluateRequest evaluateRequest = new EvaluateRequest();
+            evaluateRequest.arguments.expression = Expression;
+            evaluateRequest.arguments.frameId = frameId;
+            var ret = VSCodeDebugger.Request(evaluateRequest);
+            Assert.True(ret.Success, @"__FILE__:__LINE__"+"\n"+caller_trace);
+
+            EvaluateResponse evaluateResponse =
+                JsonConvert.DeserializeObject<EvaluateResponse>(ret.ResponseStr);
+
+            Assert.True(ExpectedResult1 == evaluateResponse.body.result ||
+                        ExpectedResult2 == evaluateResponse.body.result, @"__FILE__:__LINE__"+"\n"+caller_trace);
+            Assert.Equal(ExpectedType, evaluateResponse.body.type, @"__FILE__:__LINE__"+"\n"+caller_trace);
+        }
+
         public void CheckErrorAtRequest(string caller_trace, Int64 frameId, string Expression, string errMsgStart)
         {
             EvaluateRequest evaluateRequest = new EvaluateRequest();
@@ -658,6 +674,89 @@ namespace VSCodeTestEvaluate
         TWO
     };
 
+    public class TestOperators1
+    {
+        public int data;
+        public TestOperators1(int data_)
+        {
+            data = data_;
+        }
+
+        public static implicit operator TestOperators1(int value) => new TestOperators1(value);
+
+        public static int operator +(TestOperators1 d1, int d2) => 55;
+        public static int operator +(int d1, TestOperators1 d2) => 66;
+
+        public static int operator ~(TestOperators1 d1) => ~d1.data;
+        public static bool operator !(TestOperators1 d1) => true;
+        public static int operator +(TestOperators1 d1, TestOperators1 d2) => d1.data + d2.data;
+        public static int operator -(TestOperators1 d1, TestOperators1 d2) => d1.data - d2.data;
+        public static int operator *(TestOperators1 d1, TestOperators1 d2) => d1.data * d2.data;
+        public static int operator /(TestOperators1 d1, TestOperators1 d2) => d1.data / d2.data;
+        public static int operator %(TestOperators1 d1, TestOperators1 d2) => d1.data % d2.data;
+        public static int operator ^(TestOperators1 d1, TestOperators1 d2) => d1.data ^ d2.data;
+        public static int operator &(TestOperators1 d1, TestOperators1 d2) => d1.data & d2.data;
+        public static int operator |(TestOperators1 d1, TestOperators1 d2) => d1.data | d2.data;
+        public static int operator >>(TestOperators1 d1, int d2) => d1.data >> d2;
+        public static int operator <<(TestOperators1 d1, int d2) => d1.data << d2;
+        public static bool operator ==(TestOperators1 d1, TestOperators1 d2) => d1.data == d2.data;
+        public static bool operator !=(TestOperators1 d1, TestOperators1 d2) => d1.data != d2.data;
+        public static bool operator <(TestOperators1 d1, TestOperators1 d2) => d1.data < d2.data;
+        public static bool operator <=(TestOperators1 d1, TestOperators1 d2) => d1.data <= d2.data;
+        public static bool operator >(TestOperators1 d1, TestOperators1 d2) => d1.data > d2.data;
+        public static bool operator >=(TestOperators1 d1, TestOperators1 d2) => d1.data >= d2.data;
+    }
+
+    public struct TestOperators2
+    {
+        public int data;
+        public TestOperators2(int data_)
+        {
+            data = data_;
+        }
+        public static implicit operator TestOperators2(int value) => new TestOperators2(value);
+
+        public static int operator +(TestOperators2 d1, int d2) => 55;
+        public static int operator +(int d1, TestOperators2 d2) => 66;
+
+        public static int operator ~(TestOperators2 d1) => ~d1.data;
+        public static bool operator !(TestOperators2 d1) => true;
+        public static int operator +(TestOperators2 d1, TestOperators2 d2) => d1.data + d2.data;
+        public static int operator -(TestOperators2 d1, TestOperators2 d2) => d1.data - d2.data;
+        public static int operator *(TestOperators2 d1, TestOperators2 d2) => d1.data * d2.data;
+        public static int operator /(TestOperators2 d1, TestOperators2 d2) => d1.data / d2.data;
+        public static int operator %(TestOperators2 d1, TestOperators2 d2) => d1.data % d2.data;
+        public static int operator ^(TestOperators2 d1, TestOperators2 d2) => d1.data ^ d2.data;
+        public static int operator &(TestOperators2 d1, TestOperators2 d2) => d1.data & d2.data;
+        public static int operator |(TestOperators2 d1, TestOperators2 d2) => d1.data | d2.data;
+        public static int operator >>(TestOperators2 d1, int d2) => d1.data >> d2;
+        public static int operator <<(TestOperators2 d1, int d2) => d1.data << d2;
+        public static bool operator ==(TestOperators2 d1, TestOperators2 d2) => d1.data == d2.data;
+        public static bool operator !=(TestOperators2 d1, TestOperators2 d2) => d1.data != d2.data;
+        public static bool operator <(TestOperators2 d1, TestOperators2 d2) => d1.data < d2.data;
+        public static bool operator <=(TestOperators2 d1, TestOperators2 d2) => d1.data <= d2.data;
+        public static bool operator >(TestOperators2 d1, TestOperators2 d2) => d1.data > d2.data;
+        public static bool operator >=(TestOperators2 d1, TestOperators2 d2) => d1.data >= d2.data;
+    }
+
+    public struct TestOperators3
+    {
+        public int data;
+        public TestOperators3(int data_)
+        {
+            data = data_;
+        }
+
+        // Note, in order to test that was used proper operator, we use fixed return here.
+
+        public static implicit operator int(TestOperators3 value) => 777;
+
+        public static int operator +(TestOperators3 d1, int d2) => 555;
+        public static int operator +(int d1, TestOperators3 d2) => 666;
+        public static int operator >>(TestOperators3 d1, int d2) => 777;
+        public static int operator <<(TestOperators3 d1, int d2) => 888;
+    }
+
     class Program
     {
         int int_i = 505;
@@ -752,11 +851,13 @@ namespace VSCodeTestEvaluate
                 Context.Continue(@"__FILE__:__LINE__");
             });
 
-
-            int int_i1 = 5;
-            int int_i2 = 5;
-            string str_s1 = "one";
-            string str_s2 = "two";
+            // Test expression calculation.
+            TestOperators1 testClass = new TestOperators1(12);
+            TestOperators2 testStruct;
+            TestOperators3 testStruct2;
+            testStruct.data = 5;
+            string testString1 = null;
+            string testString2 = "test";
             int break_line2 = 1;                                                                    Label.Breakpoint("BREAK2");
 
             Label.Checkpoint("expression_test", "static_test", (Object context) => {
@@ -765,15 +866,212 @@ namespace VSCodeTestEvaluate
                 Int64 frameId = Context.DetectFrameId(@"__FILE__:__LINE__", "BREAK2");
 
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "2", "int", "1 + 1");
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "6", "int", "int_i1 + 1");
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "10", "int", "int_i1 + int_i2");
-
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"onetwo\"", "", "\"one\" + \"two\"");
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"onetwo\"", "", "str_s1 + \"two\"");
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"onetwo\"", "", "str_s1 + str_s2");
-
-                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "int_i1 +/ int_i2", "error CS1525:");
-                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1 + not_var", "System.AggregateException"); // error
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "2", "float", "1u + 1f");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "2", "long", "1u + 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "2", "decimal", "1m + 1m");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "2", "decimal", "1m + 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "2", "decimal", "1 + 1m");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "66", "int", "1 + testClass");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "55", "int", "testClass + 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "66", "int", "1 + testStruct");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "55", "int", "testStruct + 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "666", "int", "1 + testStruct2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "555", "int", "testStruct2 + 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"stringC\"", "string", "\"string\" + 'C'");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"test\"", "string", "testString1 + testString2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"test\"", "string", "testString2 + testString1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"\"", "string", "testString1 + testString1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"testtest\"", "string", "testString2 + testString2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"test\"", "string", "\"\" + \"test\"");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"test\"", "string", "\"test\" + \"\"");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"\"", "string", "\"\" + \"\"");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"testtest\"", "string", "\"test\" + \"test\"");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1UL + 1", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "true + 1", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1 + not_var", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1u + testClass", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "testClass + 1u", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1u + testStruct", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "testStruct + 1u", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "testClass + testStruct", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "testStruct + testClass", "error CS0019");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "2", "int", "3 - 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "2", "decimal", "3m - 1m");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "true - 1", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1 - not_var", "error");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "-11", "int", "1 - testClass");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "11", "int", "testClass - 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "-4", "int", "1 - testStruct");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "4", "int", "testStruct - 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "-776", "int", "1 - testStruct2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "776", "int", "testStruct2 - 1");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "4", "int", "2 * 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "4", "decimal", "2m * 2m");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "true * 2", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1 * not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "1", "int", "2 / 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "1", "decimal", "2m / 2m");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "true / 2", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1 / not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "0", "int", "2 % 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "0", "decimal", "2m % 2m");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "true % 2", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1 % not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "4", "int", "1 << 2");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1m << 2m", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "true << 2", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1 << not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "1", "int", "4 >> 2");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1m >> 2m", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "true >> 2", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1 >> not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "-2", "int", "~1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "-13", "int", "~testClass");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "-6", "int", "~testStruct");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "~1m", "error CS0023");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "~true", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "~not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "true", "bool", "true && true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "true && false");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "false && true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "false && false");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1 && 1", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1m && 1m", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "true && not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "true", "bool", "true || true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "true", "bool", "true || false");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "true", "bool", "false || true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "false || false");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1 || 1", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1m || 1m", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "true || not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "true ^ true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "true", "bool", "true ^ false");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "true", "bool", "false ^ true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "false ^ false");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "0", "int", "1 ^ 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "1", "int", "1 ^ 0");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "1", "int", "0 ^ 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "0", "int", "0 ^ 0");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "14", "int", "testClass ^ 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "14", "int", "2 ^ testClass");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "0", "int", "testClass ^ testClass");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "7", "int", "testStruct ^ 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "7", "int", "2 ^ testStruct");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "0", "int", "testStruct ^ testStruct");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "testStruct2 ^ testStruct2", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1m ^ 1m", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1 ^ not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "true", "bool", "true & true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "true & false");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "false & true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "false & false");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "1", "int", "3 & 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "0", "int", "5 & 8");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "0", "int", "10 & 0");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "0", "int", "0 & 7");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "0", "int", "0 & 0");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "0", "int", "testClass & 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "0", "int", "2 & testClass");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "12", "int", "testClass & testClass");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "0", "int", "testStruct & 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "0", "int", "2 & testStruct");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "5", "int", "testStruct & testStruct");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "testStruct2 & testStruct2", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1m & 1m", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1 & not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "true", "bool", "true | true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "true", "bool", "true | false");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "true", "bool", "false | true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "false | false");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "13", "int", "5 | 8");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "10", "int", "10 | 0");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "7", "int", "0 | 7");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "0", "int", "0 | 0");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "14", "int", "testClass | 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "14", "int", "2 | testClass");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "12", "int", "testClass | testClass");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "7", "int", "testStruct | 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "7", "int", "2 | testStruct");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "5", "int", "testStruct | testStruct");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "testStruct2 | testStruct2", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1m | 1m", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1 | not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "!true");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "true", "bool", "!testClass");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "true", "bool", "!testStruct");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "!1", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "!1m", "error CS0023");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "!not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "true", "bool", "1 == 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "2 == 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "2m == 1m");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1 == not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "1 != 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "true", "bool", "2 != 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "true", "bool", "2m != 1m");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1 != not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "true", "bool", "1 < 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "1 < 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "1m < 1m");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "true < false", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1 < not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "true", "bool", "2 > 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "1 > 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "1m > 1m");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "true > false", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1 > not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "true", "bool", "1 <= 2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "true", "bool", "1 <= 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "1 <= 0");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "1m <= 0m");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "true <= false", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1 <= not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "true", "bool", "2 >= 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "true", "bool", "1 >= 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "0 >= 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "false", "bool", "0m >= 1m");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "true >= false", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1 >= not_var", "error");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "4", "int", "2 << 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "888", "int", "testStruct2 << testStruct2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "24", "int", "testClass << 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "10", "int", "testStruct << 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "20", "int", "testStruct << 2");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1f << 1", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1f << 1f", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "testClass << testClass", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "testStruct << testStruct", "error CS0019");
+
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "2", "int", "4 >> 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "777", "int", "testStruct2 >> testStruct2");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "6", "int", "testClass >> 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "2", "int", "testStruct >> 1");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "1", "int", "testStruct >> 2");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1f >> 1", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "1f >> 1f", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "testClass >> testClass", "error CS0019");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "testStruct >> testStruct", "error CS0019");
 
                 Context.Continue(@"__FILE__:__LINE__");
             });
@@ -1071,12 +1369,12 @@ namespace VSCodeTestEvaluate
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "10", "int", "TestCallParent.GetNumber()");
 
                 // Call built-in types methods.
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"1.01\"", "string", "1.01M.ToString()");
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"1.01\"", "string", "decimalToString.ToString()");
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"2.02\"", "string", "2.02.ToString()");
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"2.02\"", "string", "doubleToString.ToString()");
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"3.03\"", "string", "3.03f.ToString()");
-                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"3.03\"", "string", "floatToString.ToString()");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"1.01\"", "\"1,01\"", "string", "1.01M.ToString()");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"1.01\"", "\"1,01\"", "string", "decimalToString.ToString()");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"2.02\"", "\"2,02\"", "string", "2.02.ToString()");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"2.02\"", "\"2,02\"", "string", "doubleToString.ToString()");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"3.03\"", "\"3,03\"", "string", "3.03f.ToString()");
+                Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"3.03\"", "\"3,03\"", "string", "floatToString.ToString()");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"c\"", "string", "'c'.ToString()");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"c\"", "string", "charToString.ToString()");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "\"True\"", "string", "boolToString.ToString()");
@@ -1162,8 +1460,8 @@ namespace VSCodeTestEvaluate
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "8", "long", "-longUnary");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "8", "ulong", "+8UL");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "8", "ulong", "+ulongUnary");
-                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "-8UL", "error CS0023");
-                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "-ulongUnary", "error CS0023");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "-8UL", "error");
+                Context.CheckErrorAtRequest(@"__FILE__:__LINE__", frameId, "-ulongUnary", "error");
 
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "10.5", "decimal", "+10.5m");
                 Context.GetAndCheckValue(@"__FILE__:__LINE__", frameId, "1.01", "decimal", "+decimalUnary");