<commit_before>﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeGen;
using Pchp.CodeAnalysis.FlowAnalysis;
using Pchp.CodeAnalysis.Semantics;
using Pchp.CodeAnalysis.Semantics.Graph;
using Pchp.CodeAnalysis.Symbols;
using Peachpie.CodeAnalysis.Utilities;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
using System.Text;
using System.Threading.Tasks;

namespace Pchp.CodeAnalysis.CodeGen
{
    partial class CodeGenerator
    {
        /// <summary>
        /// Copies <c>PhpNumber</c> into a temp variable and loads its address.
        /// </summary>
        internal void EmitPhpNumberAddr() => EmitStructAddr(CoreTypes.PhpNumber);

        /// <summary>
        /// Copies <c>PhpString</c> into a temp variable and loads its address.
        /// </summary>
        internal void EmitPhpStringAddr() => EmitStructAddr(CoreTypes.PhpString);

        /// <summary>
        /// Copies <c>PhpValue</c> into a temp variable and loads its address.
        /// </summary>
        internal void EmitPhpValueAddr() => EmitStructAddr(CoreTypes.PhpValue);

        /// <summary>
        /// Copies a value type from the top of evaluation stack into a temporary variable and loads its address.
        /// </summary>
        internal void EmitStructAddr(TypeSymbol t) => _il.EmitStructAddr(t);

        public TypeSymbol EmitConvertToBool(TypeSymbol from, TypeRefMask fromHint)
        {
            return this.EmitImplicitConversion(from, CoreTypes.Boolean);
        }

        public void EmitConvertToBool(BoundExpression expr)
        {
            Contract.ThrowIfNull(expr);

            expr.Access = expr.Access.WithRead(CoreTypes.Boolean);

            var place = PlaceOrNull(expr);
            var type = TryEmitVariableSpecialize(place, expr.TypeRefMask);
            if (type != null)
            {
                EmitConvertToBool(type, 0);
            }
            else
            {
                EmitConvertToBool(Emit(expr), expr.TypeRefMask);
            }
        }

        public TypeSymbol EmitConvertToPhpValue(BoundExpression expr)
        {
            if (expr == null || expr.ConstantValue.IsNull())
            {
                return Emit_PhpValue_Null();
            }
            else if (expr.ConstantValue.IsBool(out var b))
            {
                return b ? Emit_PhpValue_True() : Emit_PhpValue_False();
            }
            else
            {
                return EmitConvertToPhpValue(Emit(expr), expr.TypeRefMask);
            }
        }

        public TypeSymbol EmitConvertToPhpValue(TypeSymbol from, TypeRefMask fromHint)
        {
            // Nullable<T> -> HasValue ? T : NULL
            if (from.IsNullableType())
            {
                from = EmitNullableCastToNull(from, false); // (HasValue ? Value : NULL)
            }

            var conv = DeclaringCompilation.ClassifyCommonConversion(from, CoreTypes.PhpValue.Symbol);
            if (conv.IsImplicit)
            {
                this.EmitConversion(conv, from, CoreTypes.PhpValue.Symbol);
            }
            else
            {
                // some conversion we did not implement as operator yet:

                if (from.IsReferenceType)
                {
                    EmitCall(ILOpCode.Call, CoreMethods.PhpValue.FromClass_Object).Expect(CoreTypes.PhpValue);
                }
                else if (from.SpecialType == SpecialType.System_Void)
                {
                    // PhpValue.Void
                    Emit_PhpValue_Void();
                }
                else
                {
                    // box & wrap to PhpValue.Object
                    // Template: PhpValue.FromStruct<T>( STACK )
                    EmitCall(ILOpCode.Call, CoreMethods.PhpValue.FromStruct_T.Symbol.Construct(from)).Expect(CoreTypes.PhpValue);
                }
            }
            //
            return CoreTypes.PhpValue;
        }

        public void EmitConvertToPhpNumber(TypeSymbol from, TypeRefMask fromHint)
        {
            if (from != CoreTypes.PhpNumber)
            {
                from = EmitSpecialize(from, fromHint);
            }

            this.EmitImplicitConversion(from, CoreTypes.PhpNumber);
        }

        /// <summary>
        /// In case there is <c>Int32</c> or <c>bool</c> on the top of evaluation stack,
        /// converts it to <c>Int64</c>.
        /// </summary>
        /// <param name="stack">New type on top of stack.</param>
        /// <returns></returns>
        internal TypeSymbol EmitConvertIntToLong(TypeSymbol stack)
        {
            if (stack.SpecialType == SpecialType.System_Boolean ||
                stack.SpecialType == SpecialType.System_Byte ||
                stack.SpecialType == SpecialType.System_Int16 ||
                stack.SpecialType == SpecialType.System_UInt16 ||
                stack.SpecialType == SpecialType.System_Int32 ||
                stack.SpecialType == SpecialType.System_UInt32)
            {
                var int64 = CoreTypes.Long.Symbol;
                this.EmitImplicitConversion(stack, int64);
                stack = int64;
            }

            return stack;
        }

        internal TypeSymbol EmitPhpAliasDereference(ref TypeSymbol stack)
        {
            if (stack == CoreTypes.PhpAlias)
            {
                stack = Emit_PhpAlias_GetValue();
            }

            return stack;
        }

        /// <summary>
        /// In case there is <c>string</c> or <c>PhpString</c> on the top of evaluation stack,
        /// converts it to <c>PhpNumber</c>.
        /// </summary>
        /// <returns>New type on top of stack.</returns>
        internal TypeSymbol EmitConvertStringToPhpNumber(TypeSymbol stack)
        {
            if (stack.SpecialType == SpecialType.System_String ||
                stack == CoreTypes.PhpString)
            {
                this.EmitImplicitConversion(stack, CoreTypes.PhpNumber);
                return CoreTypes.PhpNumber;
            }

            return stack;
        }


        /// <summary>
        /// In case expression is of type <c>Int32</c> or <c>bool</c> or <c>PhpNumber</c>,
        /// converts it to <c>double</c> and leaves the result on evaluation stack. Otherwise
        /// just emits expression and leaves it on evaluation stack.
        /// </summary>
        internal TypeSymbol EmitExprConvertNumberToDouble(BoundExpression expr)
        {
            // emit number literal directly as double
            var constant = expr.ConstantValue;
            if (constant.HasValue)
            {
                if (constant.Value is long)
                {
                    _il.EmitDoubleConstant((long)constant.Value);
                    return this.CoreTypes.Double;
                }
                if (constant.Value is int)
                {
                    _il.EmitDoubleConstant((int)constant.Value);
                    return this.CoreTypes.Double;
                }
                if (constant.Value is bool)
                {
                    _il.EmitDoubleConstant((bool)constant.Value ? 1.0 : 0.0);<commit_msg>format<commit_after>﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeGen;
using Pchp.CodeAnalysis.FlowAnalysis;
using Pchp.CodeAnalysis.Semantics;
using Pchp.CodeAnalysis.Semantics.Graph;
using Pchp.CodeAnalysis.Symbols;
using Peachpie.CodeAnalysis.Utilities;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
using System.Text;
using System.Threading.Tasks;

namespace Pchp.CodeAnalysis.CodeGen
{
    partial class CodeGenerator
    {
        /// <summary>
        /// Copies <c>PhpNumber</c> into a temp variable and loads its address.
        /// </summary>
        internal void EmitPhpNumberAddr() => EmitStructAddr(CoreTypes.PhpNumber);

        /// <summary>
        /// Copies <c>PhpString</c> into a temp variable and loads its address.
        /// </summary>
        internal void EmitPhpStringAddr() => EmitStructAddr(CoreTypes.PhpString);

        /// <summary>
        /// Copies <c>PhpValue</c> into a temp variable and loads its address.
        /// </summary>
        internal void EmitPhpValueAddr() => EmitStructAddr(CoreTypes.PhpValue);

        /// <summary>
        /// Copies a value type from the top of evaluation stack into a temporary variable and loads its address.
        /// </summary>
        internal void EmitStructAddr(TypeSymbol t) => _il.EmitStructAddr(t);

        public TypeSymbol EmitConvertToBool(TypeSymbol from, TypeRefMask fromHint)
        {
            return this.EmitImplicitConversion(from, CoreTypes.Boolean);
        }

        public void EmitConvertToBool(BoundExpression expr)
        {
            Contract.ThrowIfNull(expr);

            expr.Access = expr.Access.WithRead(CoreTypes.Boolean);

            var place = PlaceOrNull(expr);
            var type = TryEmitVariableSpecialize(place, expr.TypeRefMask);
            if (type != null)
            {
                EmitConvertToBool(type, 0);
            }
            else
            {
                EmitConvertToBool(Emit(expr), expr.TypeRefMask);
            }
        }

        public TypeSymbol EmitConvertToPhpValue(BoundExpression expr)
        {
            if (expr == null || expr.ConstantValue.IsNull())
            {
                return Emit_PhpValue_Null();
            }
            else if (expr.ConstantValue.IsBool(out var b))
            {
                return b ? Emit_PhpValue_True() : Emit_PhpValue_False();
            }
            else
            {
                return EmitConvertToPhpValue(Emit(expr), expr.TypeRefMask);
            }
        }

        public TypeSymbol EmitConvertToPhpValue(TypeSymbol from, TypeRefMask fromHint)
        {
            // Nullable<T> -> HasValue ? T : NULL
            if (from.IsNullableType())
            {
                from = EmitNullableCastToNull(from, false); // (HasValue ? Value : NULL)
            }

            var conv = DeclaringCompilation.ClassifyCommonConversion(from, CoreTypes.PhpValue.Symbol);
            if (conv.IsImplicit)
            {
                this.EmitConversion(conv, from, CoreTypes.PhpValue.Symbol);
            }
            else
            {
                // some conversion we did not implement as operator yet:

                if (from.IsReferenceType)
                {
                    EmitCall(ILOpCode.Call, CoreMethods.PhpValue.FromClass_Object).Expect(CoreTypes.PhpValue);
                }
                else if (from.SpecialType == SpecialType.System_Void)
                {
                    // PhpValue.Void
                    Emit_PhpValue_Void();
                }
                else
                {
                    // box & wrap to PhpValue.Object
                    // Template: PhpValue.FromStruct<T>( STACK )
                    EmitCall(ILOpCode.Call, CoreMethods.PhpValue.FromStruct_T.Symbol.Construct(from)).Expect(CoreTypes.PhpValue);
                }
            }
            //
            return CoreTypes.PhpValue;
        }

        public void EmitConvertToPhpNumber(TypeSymbol from, TypeRefMask fromHint)
        {
            if (from != CoreTypes.PhpNumber)
            {
                from = EmitSpecialize(from, fromHint);
            }

            this.EmitImplicitConversion(from, CoreTypes.PhpNumber);
        }

        /// <summary>
        /// In case there is <c>Int32</c> or <c>bool</c> on the top of evaluation stack,
        /// converts it to <c>Int64</c>.
        /// </summary>
        /// <param name="stack">New type on top of stack.</param>
        /// <returns></returns>
        internal TypeSymbol EmitConvertIntToLong(TypeSymbol stack)
        {
            if (stack.SpecialType == SpecialType.System_Boolean ||
                stack.SpecialType == SpecialType.System_Byte ||
                stack.SpecialType == SpecialType.System_Int16 ||
                stack.SpecialType == SpecialType.System_UInt16 ||
                stack.SpecialType == SpecialType.System_Int32 ||
                stack.SpecialType == SpecialType.System_UInt32)
            {
                var int64 = CoreTypes.Long.Symbol;
                this.EmitImplicitConversion(stack, int64);
                stack = int64;
            }

            return stack;
        }

        internal TypeSymbol EmitPhpAliasDereference(ref TypeSymbol stack)
        {
            if (stack == CoreTypes.PhpAlias)
            {
                stack = Emit_PhpAlias_GetValue();
            }

            return stack;
        }

        /// <summary>
        /// In case there is <c>string</c> or <c>PhpString</c> on the top of evaluation stack,
        /// converts it to <c>PhpNumber</c>.
        /// </summary>
        /// <returns>New type on top of stack.</returns>
        internal TypeSymbol EmitConvertStringToPhpNumber(TypeSymbol stack)
        {
            if (stack.SpecialType == SpecialType.System_String ||
                stack == CoreTypes.PhpString)
            {
                this.EmitImplicitConversion(stack, CoreTypes.PhpNumber);
                return CoreTypes.PhpNumber;
            }

            return stack;
        }

        /// <summary>
        /// In case expression is of type <c>Int32</c> or <c>bool</c> or <c>PhpNumber</c>,
        /// converts it to <c>double</c> and leaves the result on evaluation stack. Otherwise
        /// just emits expression and leaves it on evaluation stack.
        /// </summary>
        internal TypeSymbol EmitExprConvertNumberToDouble(BoundExpression expr)
        {
            // emit number literal directly as double
            var constant = expr.ConstantValue;
            if (constant.HasValue)
            {
                if (constant.Value is long)
                {
                    _il.EmitDoubleConstant((long)constant.Value);
                    return this.CoreTypes.Double;
                }
                if (constant.Value is int)
                {
                    _il.EmitDoubleConstant((int)constant.Value);
                    return this.CoreTypes.Double;
                }
                if (constant.Value is bool)
                {
                    _il.EmitDoubleConstant((bool)constant.Value ? 1.0 : 0.0);
{'fraction_non_alphanumeric': 0.09558110541626264, 'fraction_numerical': 0.0034630835295747335, 'mean_word_length': 1.624318429661941, 'pattern_counts': {'":': 0, '<': 99, '<?xml version=': 0, '>': 109, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6886673', 'n_tokens_mistral': 3896, 'n_tokens_neox': 3754, 'n_words': 819}