Example #1
0
 /// <summary>
 /// Emits conversion to <c>PhpArray</c>.
 /// </summary>
 public TypeSymbol EmitConvertToPhpArray(TypeSymbol from, TypeRefMask fromHint)
 {
     if (from.IsOfType(CoreTypes.PhpArray))
     {
         return(from);
     }
     else if (from == CoreTypes.PhpAlias)
     {
         // Template: <PhpAlias>.Value.ToArray()
         this.Emit_PhpAlias_GetValueAddr();
         return(this.EmitCall(ILOpCode.Call, CoreMethods.PhpValue.ToArray));
     }
     else if (   // TODO: helper method for builtin types
         from.SpecialType != SpecialType.None ||
         from.IsOfType(CoreTypes.PhpResource) || from == CoreTypes.PhpNumber || from == CoreTypes.PhpString)
     {
         EmitConvertToPhpValue(from, fromHint);
         return(EmitCall(ILOpCode.Call, CoreMethods.PhpArray.New_PhpValue));
     }
     else
     {
         // Template: ToArray((PhpValue)<from>)
         EmitConvert(from, 0, CoreTypes.PhpValue);
         return(EmitCall(ILOpCode.Call, CoreMethods.Operators.ToArray_PhpValue));
     }
 }
Example #2
0
        /// <summary>
        /// Emits conversion to <c>PhpArray</c>.
        /// Anyting else than <c>NULL</c> and <c>array</c> causes an exception of type <see cref="InvalidCastException"/> in runtime.
        /// </summary>
        public TypeSymbol EmitConvertToPhpArray(TypeSymbol from, TypeRefMask fromHint)
        {
            if (from.IsOfType(CoreTypes.PhpArray))
            {
                return(from);
            }

            if (from == CoreTypes.PhpAlias)
            {
                // Template: <PhpAlias>.Value.GetArray()
                this.Emit_PhpAlias_GetValue();
                return(this.EmitCall(ILOpCode.Call, CoreMethods.Operators.ToArrayOrThrow_PhpValue));
            }

            if ((from.SpecialType != SpecialType.None && from.SpecialType != SpecialType.System_Object) ||
                (from.IsValueType && from != CoreTypes.PhpValue) ||
                from.IsOfType(CoreTypes.PhpResource))
            {
                // EXCEPTION:
                // TODO: diagnostics
                return(EmitCastClass(from, CoreTypes.PhpArray));
            }
            else if (from.IsReferenceType)
            {
                // Template: (PhpArray)<STACK>
                return(EmitCastClass(from, CoreTypes.PhpArray));
            }
            else
            {
                // Template: ((PhpValue)<from>).GetArray()
                EmitConvert(from, 0, CoreTypes.PhpValue);
                return(EmitCall(ILOpCode.Call, CoreMethods.Operators.ToArrayOrThrow_PhpValue));
            }
        }
        /// <summary>
        /// Merges two CLR types into one, according to PCHP type hierarchy.
        /// </summary>
        /// <param name="first">First type.</param>
        /// <param name="second">Second type.</param>
        /// <returns>One type convering both <paramref name="first"/> and <paramref name="second"/> types.</returns>
        internal TypeSymbol Merge(TypeSymbol first, TypeSymbol second)
        {
            Contract.ThrowIfNull(first);
            Contract.ThrowIfNull(second);

            Debug.Assert(first != CoreTypes.PhpAlias && second != CoreTypes.PhpAlias);

            // merge is not needed:
            if (first == second)
            {
                return(first);
            }

            if (first == CoreTypes.PhpValue || second == CoreTypes.PhpValue)
            {
                return(CoreTypes.PhpValue);
            }

            // a number (int | double)
            if (IsNumber(first) && IsNumber(second))
            {
                return(CoreTypes.PhpNumber);
            }

            // a string types unification
            if (IsAString(first) && IsAString(second))
            {
                return(CoreTypes.PhpString); // a string builder; if both are system.string, system.string is returned earlier
            }
            // TODO: simple array & PhpArray => PhpArray

            if (!IsAString(first) && !IsAString(second) &&
                !first.IsOfType(CoreTypes.PhpArray) && !second.IsOfType(CoreTypes.PhpArray))
            {
                // unify class types to the common one (lowest)
                if (first.IsReferenceType && second.IsReferenceType)
                {
                    // TODO: find common base
                    // TODO: otherwise find a common interface

                    if (first.IsOfType(second))
                    {
                        return(second);                         // A >> B -> B
                    }
                    if (second.IsOfType(first))
                    {
                        return(first);                          // A << B -> A
                    }
                    return(CoreTypes.Object);
                }
            }

            // most common PHP value type
            return(CoreTypes.PhpValue);
        }
Example #4
0
        private void EmitConvertToIPhpCallable(TypeSymbol from, TypeRefMask fromHint)
        {
            // dereference
            if (from == CoreTypes.PhpAlias)
            {
                from = Emit_PhpAlias_GetValue();
            }

            // (IPhpCallable)
            if (!from.IsOfType(CoreTypes.IPhpCallable))
            {
                if (from.SpecialType == SpecialType.System_String)
                {
                    EmitCallerTypeHandle();
                    EmitThisOrNull();
                    EmitCall(ILOpCode.Call, CoreMethods.Operators.AsCallable_String_RuntimeTypeHandle_Object);
                }
                else if (
                    from.SpecialType == SpecialType.System_Int64 ||
                    from.SpecialType == SpecialType.System_Boolean ||
                    from.SpecialType == SpecialType.System_Double)
                {
                    throw new ArgumentException($"{from.Name} cannot be converted to a class of type IPhpCallable!");  // TODO: ErrCode
                }
                else
                {
                    EmitConvertToPhpValue(from, fromHint);
                    EmitCallerTypeHandle();
                    EmitThisOrNull();
                    EmitCall(ILOpCode.Call, CoreMethods.Operators.AsCallable_PhpValue_RuntimeTypeHandle_Object);
                }
            }
        }
Example #5
0
        internal TypeSymbol EmitAsObject(TypeSymbol from, out bool isnull)
        {
            isnull = false;

            // dereference
            if (from == CoreTypes.PhpAlias)
            {
                // <alias>.Value.AsObject()
                Emit_PhpAlias_GetValueAddr();
                return(EmitCall(ILOpCode.Call, CoreMethods.PhpValue.AsObject));
            }

            // PhpValue -> object
            if (from == CoreTypes.PhpValue)
            {
                // Template: Operators.AsObject(value)
                return(EmitCall(ILOpCode.Call, CoreMethods.Operators.AsObject_PhpValue));
            }

            if (!from.IsReferenceType ||
                from == CoreTypes.PhpArray ||
                from.IsOfType(CoreTypes.PhpResource) ||
                from == CoreTypes.PhpString ||
                from.SpecialType == SpecialType.System_String)
            {
                EmitPop(from);
                _il.EmitNullConstant();
                isnull = true;
                return(CoreTypes.Object);
            }
            else
            {
                return(from);
            }
        }
Example #6
0
        /// <summary>
        /// Merges CLR type to be nullable.
        /// </summary>
        internal TypeSymbol MergeNull(TypeSymbol type, bool asSystemNullable)
        {
            if (type == null || type.IsVoid())
            {
                return(CoreTypes.Object);
            }

            if (type.IsValueType || type.IsOfType(CoreTypes.IPhpArray)) // TODO: remove IPhpArray and check for null in emitted code
            {
                if (asSystemNullable)
                {
                    // nullable bool|int|long|double|string
                    if (type.SpecialType == SpecialType.System_Boolean ||
                        type.SpecialType == SpecialType.System_Int32 ||
                        type.SpecialType == SpecialType.System_Int64 ||
                        type.SpecialType == SpecialType.System_Double ||
                        type.Is_PhpString())
                    {
                        return(this.GetSpecialType(SpecialType.System_Nullable_T).Construct(ImmutableArray.Create(type)));
                    }
                }

                return(CoreTypes.PhpValue);    // anything else -> PhpValue
            }

            return(type);
        }
Example #7
0
        public void EmitConvertToInt(TypeSymbol from, TypeRefMask fromHint)
        {
            Contract.ThrowIfNull(from);

            // dereference
            if (from == CoreTypes.PhpAlias)
            {
                Emit_PhpAlias_GetValue();
                from = CoreTypes.PhpValue;
            }

            //
            from = EmitSpecialize(from, fromHint);

            switch (from.SpecialType)
            {
            case SpecialType.System_Int32:
                return;

            default:

                if (from.IsOfType(CoreTypes.IPhpArray))
                {
                    // IPhpArray.Count
                    EmitCall(ILOpCode.Callvirt, CoreMethods.IPhpArray.get_Count);
                }
                else
                {
                    EmitConvertToLong(from, 0);
                    _il.EmitOpCode(ILOpCode.Conv_i4);       // Int64 -> Int32
                }
                return;
            }
        }
Example #8
0
        public TypeSymbol EmitConvertToDouble(TypeSymbol from, TypeRefMask fromHint)
        {
            Contract.ThrowIfNull(from);

            // dereference
            if (from == CoreTypes.PhpAlias)
            {
                Emit_PhpAlias_GetValue();
                from = CoreTypes.PhpValue;
            }

            from = EmitSpecialize(from, fromHint);
            var dtype = CoreTypes.Double.Symbol;

            switch (from.SpecialType)
            {
            case SpecialType.System_Int32:
                _il.EmitOpCode(ILOpCode.Conv_r8);       // Int32 -> Double
                return(dtype);

            case SpecialType.System_Int64:
                _il.EmitOpCode(ILOpCode.Conv_r8);       // Int64 -> Double
                return(dtype);

            case SpecialType.System_Single:
                _il.EmitOpCode(ILOpCode.Conv_r8);       // float -> Double
                return(dtype);

            case SpecialType.System_Double:
                // nop
                return(dtype);

            case SpecialType.System_String:
                return(EmitCall(ILOpCode.Call, CoreMethods.Operators.ToDouble_String)
                       .Expect(SpecialType.System_Double));

            default:
                if (from == CoreTypes.PhpNumber)
                {
                    EmitPhpNumberAddr();
                    EmitCall(ILOpCode.Call, CoreMethods.PhpNumber.ToDouble);
                    return(dtype);
                }
                else if (from.IsOfType(CoreTypes.IPhpArray))
                {
                    // (double)IPhpArray.Count
                    EmitCall(ILOpCode.Callvirt, CoreMethods.IPhpArray.get_Count);
                    _il.EmitOpCode(ILOpCode.Conv_r8);       // Int32 -> Double
                    return(dtype);
                }
                else if (from == CoreTypes.PhpValue)
                {
                    return(EmitCall(ILOpCode.Call, CoreMethods.Operators.ToDouble_PhpValue));
                }
                else
                {
                    throw new NotImplementedException();
                }
            }
        }
Example #9
0
        /// <summary>
        /// Resolves a <see cref="TypeSymbol"/> that both given types share.
        /// Gets <c>System.Object</c> in worst case.
        /// </summary>
        internal TypeSymbol FindCommonBase(TypeSymbol a, TypeSymbol b)
        {
            Debug.Assert(a != null && b != null);

            if (a.IsReferenceType && b.IsReferenceType)
            {
                if (a.SpecialType != SpecialType.System_Object &&
                    b.SpecialType != SpecialType.System_Object)
                {
                    if (a.IsOfType(b))
                    {
                        return(b);                  // A >> B -> B
                    }
                    if (b.IsOfType(a))
                    {
                        return(a);                  // A << B -> A
                    }
                    // find common base
                    // find a common interface

                    var set = new HashSet <TypeSymbol>();

                    // walk through "a" and remember all the base types
                    for (var ax = a.BaseType; ax != null && ax.SpecialType != SpecialType.System_Object; ax = ax.BaseType)
                    {
                        set.Add(ax);
                    }
                    foreach (var ax in a.AllInterfaces)
                    {
                        set.Add(ax);
                    }

                    // walk through "b" and find something in the hierarchy shared by "a",
                    // base types first
                    for (var ax = b.BaseType; ax != null && ax.SpecialType != SpecialType.System_Object; ax = ax.BaseType)
                    {
                        if (set.Contains(ax))
                        {
                            return(ax); // a common base
                        }
                    }
                    foreach (var ax in b.AllInterfaces)
                    {
                        if (set.Contains(ax))
                        {
                            return(ax);  // a common interface
                        }
                    }
                }

                //
                return(CoreTypes.Object);
            }

            // dunno
            return(null);
        }
Example #10
0
        /// <summary>
        /// Merges two CLR types into one, according to PCHP type hierarchy.
        /// </summary>
        /// <param name="first">First type.</param>
        /// <param name="second">Second type.</param>
        /// <returns>One type convering both <paramref name="first"/> and <paramref name="second"/> types.</returns>
        internal TypeSymbol Merge(TypeSymbol first, TypeSymbol second)
        {
            Contract.ThrowIfNull(first);
            Contract.ThrowIfNull(second);

            // merge is not needed:
            if (first == second)
            {
                return(first);
            }

            if (first == CoreTypes.PhpValue || second == CoreTypes.PhpValue ||
                first == CoreTypes.PhpAlias || second == CoreTypes.PhpAlias)
            {
                return(CoreTypes.PhpValue);
            }

            // an integer (int | long)
            if (IsIntegerNumber(first) && IsIntegerNumber(second))
            {
                return(CoreTypes.Long);
            }

            // float|double
            if (IsFloatNumber(first) && IsFloatNumber(second))
            {
                return(CoreTypes.Double);
            }

            // a number (int | double)
            if (IsNumber(first) && IsNumber(second))
            {
                return(CoreTypes.PhpNumber);
            }

            // a string types unification
            if (IsAString(first) && IsAString(second))
            {
                return(CoreTypes.PhpString); // a string builder; if both are system.string, system.string is returned earlier
            }
            // TODO: simple array & PhpArray => PhpArray

            if (!IsAString(first) && !IsAString(second) &&
                !first.IsOfType(CoreTypes.PhpArray) && !second.IsOfType(CoreTypes.PhpArray))
            {
                // unify class types to the common one (lowest)
                if (first.IsReferenceType && second.IsReferenceType)
                {
                    return(FindCommonBase(first, second));
                }
            }

            // most common PHP value type
            return(CoreTypes.PhpValue);
        }
Example #11
0
        /// <summary>
        /// Merges CLR type to be nullable.
        /// </summary>
        internal TypeSymbol MergeNull(TypeSymbol type)
        {
            if (type == null || type.IsVoid())
            {
                return(CoreTypes.Object);
            }

            if (type.IsValueType || type.IsOfType(CoreTypes.IPhpArray)) // TODO: remove IPhpArray and check for null in emitted code
            {
                return(CoreTypes.PhpValue);                             // Nullable bool|long|double -> PhpValue
            }

            return(type);
        }
Example #12
0
        /// <summary>
        /// Merges CLR type to be nullable.
        /// </summary>
        internal TypeSymbol MergeNull(TypeSymbol type)
        {
            if (type == null || type.SpecialType == SpecialType.System_Void)
            {
                return(CoreTypes.Object);
            }

            if (type.IsValueType || IsAString(type) || type.IsOfType(CoreTypes.IPhpArray))
            {
                return(CoreTypes.PhpValue);    // Nullable bool|long|double -> PhpValue
            }

            return(type);
        }
Example #13
0
 /// <summary>
 /// Emits conversion to <c>PhpArray</c>.
 /// </summary>
 public void EmitConvertToPhpArray(TypeSymbol from, TypeRefMask fromHint)
 {
     if (from.IsOfType(CoreTypes.PhpArray))
     {
         return;
     }
     else if (from == CoreTypes.PhpValue)
     {
         EmitCall(ILOpCode.Call, CoreMethods.Operators.AsArray_PhpValue); // TODO: ToArray(), not AsArray()
     }
     else if (                                                            // TODO: helper method for builtin types
         from.SpecialType != SpecialType.None ||
         from.IsOfType(CoreTypes.PhpResource) || from == CoreTypes.PhpNumber || from == CoreTypes.PhpString)
     {
         EmitConvertToPhpValue(from, fromHint);
         EmitCall(ILOpCode.Call, CoreMethods.PhpArray.New_PhpValue);
     }
     else
     {
         // TODO: object to array (copy its fields to new instance)
         throw new NotImplementedException($"(array){from.Name}");
     }
 }
Example #14
0
        /// <summary>
        /// Merges CLR type to be nullable.
        /// </summary>
        internal TypeSymbol MergeNull(TypeSymbol type)
        {
            Contract.ThrowIfNull(type);

            if (type.IsVoid())
            {
                return(CoreTypes.Object);
            }

            if (type.IsValueType || type.IsOfType(CoreTypes.IPhpArray))
            {
                return(CoreTypes.PhpValue);    // Nullable bool|long|double -> PhpValue
            }

            return(type);
        }
Example #15
0
        /// <summary>
        /// Emits conversion to a class object.
        /// </summary>
        /// <param name="from">Type of value on top of the evaluation stack.</param>
        /// <param name="fromHint">Hint in case of multitype value.</param>
        /// <param name="to">Target type.</param>
        private void EmitConvertToClass(TypeSymbol from, TypeRefMask fromHint, TypeSymbol to)
        {
            Contract.ThrowIfNull(from);
            Contract.ThrowIfNull(to);
            Debug.Assert(to.IsReferenceType);   // TODO: structs other than primitive types
            Debug.Assert(to != CoreTypes.PhpAlias);

            // dereference
            if (from == CoreTypes.PhpAlias)
            {
                Emit_PhpAlias_GetValue();
                from = CoreTypes.PhpValue;
            }

            if (from == to)
                return;

            Debug.Assert(to != CoreTypes.PhpArray && to != CoreTypes.PhpString && to != CoreTypes.PhpAlias);

            if (to == CoreTypes.IPhpCallable)
            {
                // (IPhpCallable)
                if (!from.IsEqualToOrDerivedFrom(CoreTypes.IPhpCallable))
                {
                    if (from.SpecialType == SpecialType.System_String)
                    {
                        EmitCall(ILOpCode.Call, CoreMethods.Operators.AsCallable_String);
                    }
                    else if (
                        from.SpecialType == SpecialType.System_Int64 ||
                        from.SpecialType == SpecialType.System_Boolean ||
                        from.SpecialType == SpecialType.System_Double)
                    {
                        throw new ArgumentException($"{from.Name} cannot be converted to a class of type {to.Name}!");  // TODO: ErrCode
                    }
                    else
                    {
                        EmitConvertToPhpValue(from, fromHint);
                        EmitCall(ILOpCode.Call, CoreMethods.Operators.AsCallable_PhpValue);
                    }
                }
                return;
            }

            switch (from.SpecialType)
            {
                case SpecialType.System_Void:
                case SpecialType.System_Int32:
                case SpecialType.System_Int64:
                case SpecialType.System_Boolean:
                case SpecialType.System_Double:
                case SpecialType.System_String:
                    if (to == CoreTypes.Object)
                    {
                        from = EmitConvertToPhpValue(from, fromHint);
                        goto default;
                    }
                    else
                    {
                        throw new ArgumentException($"{from.Name} cannot be converted to a class of type {to.Name}!");  // TODO: ErrCode
                    }
                default:
                    if (from == CoreTypes.PhpValue)
                    {
                        // Convert.ToClass( value )
                        EmitCall(ILOpCode.Call, CoreMethods.Operators.ToClass_PhpValue)
                            .Expect(SpecialType.System_Object);

                        // (T)
                        EmitCastClass(to);
                        return;
                    }
                    if (from == CoreTypes.PhpNumber)
                    {
                        // Object
                        EmitPhpNumberAddr();
                        EmitCall(ILOpCode.Call, CoreMethods.PhpNumber.ToClass)
                            .Expect(SpecialType.System_Object);

                        // (T)
                        EmitCastClass(to);
                        return;
                    }
                    else if (from.IsOfType(CoreTypes.PhpArray))
                    {
                        // (T)PhpArray.ToClass();
                        EmitCastClass(EmitCall(ILOpCode.Call, CoreMethods.PhpArray.ToClass), to);
                        return;
                    }
                    else if (from.IsOfType(CoreTypes.IPhpArray))
                    {
                        // (T)Convert.ToClass(IPhpArray)
                        EmitCastClass(EmitCall(ILOpCode.Call, CoreMethods.Operators.ToClass_IPhpArray), to);
                        return;
                    }
                    else if (from.IsReferenceType)
                    {
                        Debug.Assert(from != CoreTypes.PhpAlias);
                        // (T)obj   // let .NET deal with eventual cast error for now
                        EmitCastClass(from, to);
                        return;
                    }
                    throw new NotImplementedException();
            }
        }
Example #16
0
        public void EmitConvertToBool(TypeSymbol from, TypeRefMask fromHint, bool negation = false)
        {
            // TODO: use {fromHint} to emit casting in compile time

            // dereference
            if (from == CoreTypes.PhpAlias)
            {
                // <PhpAlias>.Value.ToBoolean()
                Emit_PhpAlias_GetValueRef();
                EmitCall(ILOpCode.Call, CoreMethods.PhpValue.ToBoolean);

                // !
                if (negation)
                {
                    EmitLogicNegation();
                }

                //
                return;
            }

            //
            from = EmitSpecialize(from, fromHint);

            //
            switch (from.SpecialType)
            {
                case SpecialType.System_Void:
                    _il.EmitBoolConstant(negation ? true : false);  // (bool)void == false
                    return;

                case SpecialType.System_Boolean:
                case SpecialType.System_Int32:
                    break; // nop

                case SpecialType.System_Int64:
                    _il.EmitOpCode(ILOpCode.Ldc_i4_0, 1);
                    _il.EmitOpCode(ILOpCode.Conv_i8, 0);
                    _il.EmitOpCode(negation ? ILOpCode.Ceq : ILOpCode.Cgt_un);
                    return;

                case SpecialType.System_Double:

                    // r8 == 0.0
                    _il.EmitDoubleConstant(0.0);
                    _il.EmitOpCode(ILOpCode.Ceq);

                    if (!negation)
                    {
                        // !<i4>
                        EmitLogicNegation();
                    }

                    return;

                case SpecialType.System_String:
                    // Convert.ToBoolean(string)
                    EmitCall(ILOpCode.Call, CoreMethods.Operators.ToBoolean_String);
                    break;

                case SpecialType.System_Object:
                    EmitCall(ILOpCode.Call, CoreMethods.Operators.ToBoolean_Object);
                    break;

                case SpecialType.None:
                    if (from == CoreTypes.PhpValue)
                    {
                        // (bool)value
                        EmitCall(ILOpCode.Call, CoreMethods.Operators.ToBoolean_PhpValue);
                        break;
                    }
                    else if (from == CoreTypes.PhpNumber)
                    {
                        EmitPhpNumberAddr();
                        EmitCall(ILOpCode.Call, CoreMethods.PhpNumber.ToBoolean);
                        break;
                    }
                    // TODO: IsOfType(IPhpConvertible) -> (IPhpConvertible).ToBoolean()
                    else if (from == CoreTypes.PhpString)
                    {
                        EmitCall(ILOpCode.Call, CoreMethods.PhpString.ToBoolean);
                        break;
                    }
                    else if (from.IsOfType(CoreTypes.IPhpArray))
                    {
                        // IPhpArray.Count != 0
                        EmitCall(ILOpCode.Callvirt, CoreMethods.IPhpArray.get_Count);
                        _il.EmitOpCode(ILOpCode.Ldc_i4_0, 1);
                        _il.EmitOpCode(negation ? ILOpCode.Ceq : ILOpCode.Cgt_un);
                        return; // negation handled
                    }
                    else if (from.IsReferenceType)
                    {
                        goto case SpecialType.System_Object;
                    }

                    goto default;

                default:
                    throw new NotImplementedException($"(bool){from.Name}");
            }

            // !<i4>
            if (negation)
            {
                EmitLogicNegation();
            }
        }
Example #17
0
        public void EmitConvertToLong(TypeSymbol from, TypeRefMask fromHint)
        {
            Contract.ThrowIfNull(from);

            // dereference
            if (from == CoreTypes.PhpAlias)
            {
                Emit_PhpAlias_GetValue();
                from = CoreTypes.PhpValue;
            }

            //
            from = EmitSpecialize(from, fromHint);

            switch (from.SpecialType)
            {
            case SpecialType.System_Boolean:
                _il.EmitOpCode(ILOpCode.Conv_i8);       // bool -> Int64
                return;

            case SpecialType.System_Int32:
                _il.EmitOpCode(ILOpCode.Conv_i8);       // Int32 -> Int64
                return;

            case SpecialType.System_Int64:
                // nop
                return;

            case SpecialType.System_Double:
                _il.EmitOpCode(ILOpCode.Conv_i8);       // double -> int64
                break;

            case SpecialType.System_String:
                EmitCall(ILOpCode.Call, CoreMethods.Operators.ToLong_String)
                .Expect(SpecialType.System_Int64);
                break;

            default:
                if (from == CoreTypes.PhpNumber)
                {
                    EmitPhpNumberAddr();
                    EmitCall(ILOpCode.Call, CoreMethods.PhpNumber.ToLong);
                    return;
                }
                else if (from.IsOfType(CoreTypes.IPhpArray))
                {
                    // (long)IPhpArray.Count
                    EmitCall(ILOpCode.Callvirt, CoreMethods.IPhpArray.get_Count);
                    _il.EmitOpCode(ILOpCode.Conv_i8);       // Int32 -> Int64
                    return;
                }
                else if (from == CoreTypes.PhpValue)
                {
                    EmitCall(ILOpCode.Call, CoreMethods.Operators.ToLong_PhpValue);
                    return;
                }
                else
                {
                    throw new NotImplementedException();
                }
            }
        }
Example #18
0
        public void EmitConvertToBool(TypeSymbol from, TypeRefMask fromHint, bool negation = false)
        {
            // TODO: use {fromHint} to emit casting in compile time

            // dereference
            if (from == CoreTypes.PhpAlias)
            {
                // <PhpAlias>.Value.ToBoolean()
                Emit_PhpAlias_GetValueAddr();
                EmitCall(ILOpCode.Call, CoreMethods.PhpValue.ToBoolean);

                // !
                if (negation)
                {
                    EmitLogicNegation();
                }

                //
                return;
            }

            //
            from = EmitSpecialize(from, fromHint);

            //
            switch (from.SpecialType)
            {
            case SpecialType.System_Void:
                _il.EmitBoolConstant(negation ? true : false);      // (bool)void == false
                return;

            case SpecialType.System_Boolean:
            case SpecialType.System_Int32:
                break;     // nop

            case SpecialType.System_Int64:
                _il.EmitOpCode(ILOpCode.Ldc_i4_0, 1);
                _il.EmitOpCode(ILOpCode.Conv_i8, 0);
                _il.EmitOpCode(negation ? ILOpCode.Ceq : ILOpCode.Cgt_un);
                return;

            case SpecialType.System_Double:

                // r8 == 0.0
                _il.EmitDoubleConstant(0.0);
                _il.EmitOpCode(ILOpCode.Ceq);

                if (!negation)
                {
                    // !<i4>
                    EmitLogicNegation();
                }

                return;

            case SpecialType.System_String:
                // Convert.ToBoolean(string)
                EmitCall(ILOpCode.Call, CoreMethods.Operators.ToBoolean_String);
                break;

            case SpecialType.System_Object:
                EmitCall(ILOpCode.Call, CoreMethods.Operators.ToBoolean_Object);
                break;

            case SpecialType.None:
                if (from == CoreTypes.PhpValue)
                {
                    // (bool)value
                    EmitCall(ILOpCode.Call, CoreMethods.Operators.ToBoolean_PhpValue);
                    break;
                }
                else if (from == CoreTypes.PhpNumber)
                {
                    EmitPhpNumberAddr();
                    EmitCall(ILOpCode.Call, CoreMethods.PhpNumber.ToBoolean);
                    break;
                }
                else if (from.IsOfType(CoreTypes.IPhpConvertible))
                {
                    // (IPhpConvertible).ToBoolean()
                    if (CanBeNull(fromHint))
                    {
                        // Template: <value> != null && <value>.ToBoolean()
                        EmitCall(ILOpCode.Call, CoreMethods.Operators.ToBoolean_IPhpConvertible);
                    }
                    else
                    {
                        // Template: <value>.ToBoolean()
                        EmitCall(ILOpCode.Callvirt, CoreMethods.IPhpConvertible.ToBoolean)
                        .Expect(SpecialType.System_Boolean);
                    }
                    break;
                }
                //else if (from == CoreTypes.PhpString)
                //{
                //    EmitCall(ILOpCode.Call, CoreMethods.PhpString.ToBoolean);
                //    break;
                //}
                //else if (from.IsOfType(CoreTypes.IPhpArray))
                //{
                //    // TODO: != null && .Count != 0
                //    // IPhpArray.Count != 0
                //    EmitCall(ILOpCode.Callvirt, CoreMethods.IPhpArray.get_Count);
                //    _il.EmitOpCode(ILOpCode.Ldc_i4_0, 1);
                //    _il.EmitOpCode(negation ? ILOpCode.Ceq : ILOpCode.Cgt_un);
                //    return; // negation handled
                //}
                else if (from.IsReferenceType)
                {
                    goto case SpecialType.System_Object;
                }

                goto default;

            default:
                throw new NotImplementedException($"(bool){from.Name}");
            }

            // !<i4>
            if (negation)
            {
                EmitLogicNegation();
            }
        }
Example #19
0
        public static TypeSymbol EmitConvertToPhpValue(TypeSymbol from, TypeRefMask fromHint, ILBuilder il, Emit.PEModuleBuilder module, DiagnosticBag diagnostic)
        {
            Contract.ThrowIfNull(from);

            var compilation = module.Compilation;

            switch (from.SpecialType)
            {
            case SpecialType.System_Boolean:
                il.EmitCall(module, diagnostic, ILOpCode.Call, compilation.CoreMethods.PhpValue.Create_Boolean);
                break;

            case SpecialType.System_Int32:
                il.EmitOpCode(ILOpCode.Conv_i8);       // Int32 -> Int64
                goto case SpecialType.System_Int64;    // PhpValue.Create((long)<stack>)

            case SpecialType.System_Int64:
                il.EmitCall(module, diagnostic, ILOpCode.Call, compilation.CoreMethods.PhpValue.Create_Long);
                break;

            case SpecialType.System_Double:
                il.EmitCall(module, diagnostic, ILOpCode.Call, compilation.CoreMethods.PhpValue.Create_Double);
                break;

            case SpecialType.System_Void:
                Emit_PhpValue_Void(il, module, diagnostic);
                break;

            case SpecialType.System_String:
                il.EmitCall(module, diagnostic, ILOpCode.Call, compilation.CoreMethods.PhpValue.Create_String)
                .Expect(compilation.CoreTypes.PhpValue);
                break;

            default:
                if (from == compilation.CoreTypes.PhpAlias)
                {
                    il.EmitCall(module, diagnostic, ILOpCode.Call, compilation.CoreMethods.PhpValue.Create_PhpAlias)
                    .Expect(compilation.CoreTypes.PhpValue);
                    break;
                }
                else if (from == compilation.CoreTypes.PhpValue)
                {
                    // nop
                    break;
                }
                else if (from == compilation.CoreTypes.PhpString)
                {
                    il.EmitCall(module, diagnostic, ILOpCode.Call, compilation.CoreMethods.PhpValue.Create_PhpString)
                    .Expect(compilation.CoreTypes.PhpValue);
                    break;
                }
                else if (from == compilation.CoreTypes.PhpNumber)
                {
                    il.EmitCall(module, diagnostic, ILOpCode.Call, compilation.CoreMethods.PhpValue.Create_PhpNumber)
                    .Expect(compilation.CoreTypes.PhpValue);
                    break;
                }
                else if (from.IsOfType(compilation.CoreTypes.PhpArray))
                {
                    il.EmitCall(module, diagnostic, ILOpCode.Call, compilation.CoreMethods.PhpValue.Create_PhpArray)
                    .Expect(compilation.CoreTypes.PhpValue);
                    break;
                }
                else if (from == compilation.CoreTypes.IntStringKey)
                {
                    il.EmitCall(module, diagnostic, ILOpCode.Call, compilation.CoreMethods.PhpValue.Create_IntStringKey)
                    .Expect(compilation.CoreTypes.PhpValue);
                    break;
                }
                else if (from.IsReferenceType)
                {
                    il.EmitCall(module, diagnostic, ILOpCode.Call, compilation.CoreMethods.PhpValue.FromClass_Object)
                    .Expect(compilation.CoreTypes.PhpValue);
                    break;
                }
                else
                {
                    throw new NotImplementedException($"{from.Name}");
                }
            }

            //
            return(compilation.CoreTypes.PhpValue);
        }
Example #20
0
        /// <summary>
        /// Emits conversion from one CLR type to another using PHP conventions.
        /// </summary>
        /// <param name="from">Type of value on top of evaluation stack.</param>
        /// <param name="fromHint">Type hint in case of a multityple type choices (like PhpValue or PhpNumber or PhpAlias).</param>
        /// <param name="to">Target CLR type.</param>
        public void EmitConvert(TypeSymbol from, TypeRefMask fromHint, TypeSymbol to)
        {
            Contract.ThrowIfNull(from);
            Contract.ThrowIfNull(to);

            // conversion is not needed:
            if (from.SpecialType == to.SpecialType &&
                (from == to || (to.SpecialType != SpecialType.System_Object && from.IsOfType(to))))
            {
                return;
            }

            //
            from = EmitSpecialize(from, fromHint);

            // specialized conversions:
            switch (to.SpecialType)
            {
                case SpecialType.System_Void:
                    EmitPop(from);
                    return;
                case SpecialType.System_Boolean:
                    EmitConvertToBool(from, fromHint);
                    return;
                case SpecialType.System_Int32:
                    EmitConvertToInt(from, fromHint);
                    return;
                case SpecialType.System_Int64:
                    EmitConvertToLong(from, fromHint);
                    return;
                case SpecialType.System_Double:
                    EmitConvertToDouble(from, fromHint);
                    return;
                case SpecialType.System_String:
                    EmitConvertToString(from, fromHint);
                    return;
                case SpecialType.System_Object:
                    EmitConvertToClass(from, fromHint, to);
                    return;
                default:
                    if (to == CoreTypes.PhpValue)
                    {
                        EmitConvertToPhpValue(from, fromHint);
                    }
                    else if (to == CoreTypes.PhpAlias)
                    {
                        EmitConvertToPhpValue(from, fromHint);
                        Emit_PhpValue_MakeAlias();
                    }
                    else if (to == CoreTypes.PhpNumber)
                    {
                        EmitConvertToPhpNumber(from, fromHint);
                    }
                    else if (to == CoreTypes.PhpArray || to == CoreTypes.IPhpEnumerable || to == CoreTypes.IPhpArray)    // TODO: merge into IPhpArray
                    {
                        EmitConvertToPhpArray(from, fromHint);
                    }
                    else if (to == CoreTypes.PhpString)
                    {
                        EmitConvertToPhpString(from, fromHint);
                    }
                    else if (to.IsReferenceType)
                    {
                        EmitConvertToClass(from, fromHint, to);
                    }
                    else if (to.IsEnumType())
                    {
                        EmitConvertToEnum(from, (NamedTypeSymbol)to);
                    }
                    else if (to == CoreTypes.IntStringKey)
                    {
                        EmitConvertToIntStringKey(from, fromHint);
                    }
                    else
                    {
                        break;
                    }
                    return;
            }

            //
            throw new NotImplementedException($"{to}");
        }
Example #21
0
        /// <summary>
        /// Emits conversion from one CLR type to another using PHP conventions.
        /// </summary>
        /// <param name="from">Type of value on top of evaluation stack.</param>
        /// <param name="fromHint">Type hint in case of a multityple type choices (like PhpValue or PhpNumber or PhpAlias).</param>
        /// <param name="to">Target CLR type.</param>
        public void EmitConvert(TypeSymbol from, TypeRefMask fromHint, TypeSymbol to)
        {
            Contract.ThrowIfNull(from);
            Contract.ThrowIfNull(to);

            // conversion is not needed:
            if (from.SpecialType == to.SpecialType &&
                (from == to || (to.SpecialType != SpecialType.System_Object && from.IsOfType(to))))
            {
                return;
            }

            //
            from = EmitSpecialize(from, fromHint);

            // specialized conversions:
            switch (to.SpecialType)
            {
            case SpecialType.System_Void:
                EmitPop(from);
                return;

            case SpecialType.System_Boolean:
                EmitConvertToBool(from, fromHint);
                return;

            case SpecialType.System_Int32:
                EmitConvertToInt(from, fromHint);
                return;

            case SpecialType.System_Int64:
                EmitConvertToLong(from, fromHint);
                return;

            case SpecialType.System_Single:
                EmitConvertToDouble(from, fromHint);
                _il.EmitOpCode(ILOpCode.Conv_r4);
                return;

            case SpecialType.System_Double:
                EmitConvertToDouble(from, fromHint);
                return;

            case SpecialType.System_String:
                EmitConvertToString(from, fromHint);
                return;

            case SpecialType.System_Object:
                EmitConvertToClass(from, fromHint, to);
                return;

            default:
                if (to == CoreTypes.PhpValue)
                {
                    EmitConvertToPhpValue(from, fromHint);
                }
                else if (to == CoreTypes.PhpAlias)
                {
                    EmitConvertToPhpValue(from, fromHint);
                    Emit_PhpValue_MakeAlias();
                }
                else if (to == CoreTypes.PhpNumber)
                {
                    EmitConvertToPhpNumber(from, fromHint);
                }
                else if (CoreTypes.PhpArray.Symbol.IsOfType(to))
                {
                    EmitConvertToPhpArray(from, fromHint);
                }
                else if (to == CoreTypes.PhpString)
                {
                    EmitConvertToPhpString(from, fromHint);
                }
                else if (to.IsReferenceType)
                {
                    EmitConvertToClass(from, fromHint, to);
                }
                else if (to.IsEnumType())
                {
                    EmitConvertToEnum(from, (NamedTypeSymbol)to);
                }
                else if (to == CoreTypes.IntStringKey)
                {
                    EmitConvertToIntStringKey(from, fromHint);
                }
                else
                {
                    break;
                }
                return;
            }

            //
            throw new NotImplementedException($"{to}");
        }
Example #22
0
 /// <summary>
 /// Emits conversion to <c>PhpArray</c>.
 /// </summary>
 public void EmitConvertToPhpArray(TypeSymbol from, TypeRefMask fromHint)
 {
     if (from.IsOfType(CoreTypes.PhpArray))
     {
         return;
     }
     else if (from == CoreTypes.PhpValue)
     {
         EmitCall(ILOpCode.Call, CoreMethods.Operators.ToArray_PhpValue);        // TODO: ToArray(), not AsArray()
     }
     else if (   // TODO: helper method for builtin types
         from.SpecialType != SpecialType.None ||
         from.IsOfType(CoreTypes.PhpResource) || from == CoreTypes.PhpNumber || from == CoreTypes.PhpString)
     {
         EmitConvertToPhpValue(from, fromHint);
         EmitCall(ILOpCode.Call, CoreMethods.PhpArray.New_PhpValue);
     }
     else
     {
         // TODO: object to array (copy its fields to new instance)
         throw new NotImplementedException($"(array){from.Name}");
     }
 }
Example #23
0
        /// <summary>
        /// Emits the given conversion. 'from' and 'to' matches the classified conversion.
        /// </summary>
        public static void EmitConversion(this CodeGenerator cg, CommonConversion conversion, TypeSymbol from, TypeSymbol to, TypeSymbol op = null, bool @checked = false)
        {
            // {from}, {op} is loaded on stack

            //

            if (conversion.Exists == false)
            {
                throw cg.NotImplementedException($"Conversion from '{from}' to '{to}' ");
            }

            if (conversion.IsIdentity)
            {
                if (op != null)
                {
                    throw new ArgumentException(nameof(op));
                }

                if (to.SpecialType == SpecialType.System_Void)
                {
                    // POP
                    cg.EmitPop(from);
                }

                // nop
            }
            else if (conversion.IsNumeric)
            {
                if (op != null)
                {
                    throw new ArgumentException(nameof(op));
                }

                EmitNumericConversion(cg, from, to, @checked: @checked);
            }
            else if (conversion.IsReference)
            {
                if (op != null)
                {
                    throw new ArgumentException(nameof(op));
                }

                // TODO: ensure from/to is a valid reference type
                cg.EmitCastClass(to);
            }
            else if (conversion.IsUserDefined)
            {
                var method    = (MethodSymbol)conversion.MethodSymbol;
                var ps        = method.Parameters;
                int pconsumed = 0;

                if (method.HasThis)
                {
                    if (from.IsValueType)
                    {
                        if (op != null)
                        {
                            throw new ArgumentException(nameof(op));
                        }

                        cg.EmitStructAddr(from);
                    }
                }
                else
                {
                    if (ps[0].RefKind != RefKind.None)
                    {
                        throw new InvalidOperationException();
                    }
                    if (from != ps[0].Type)
                    {
                        if (op != null)
                        {
                            if (!from.IsOfType(ps[0].Type))
                            {
                                throw new ArgumentException(nameof(op));
                            }
                        }
                        else
                        {
                            EmitImplicitConversion(cg, from, ps[0].Type, @checked: @checked);
                        }
                    }
                    pconsumed++;
                }

                if (op != null)
                {
                    if (ps.Length > pconsumed)
                    {
                        EmitImplicitConversion(cg, op, ps[pconsumed].Type, @checked: @checked);
                    }
                    pconsumed++;
                }

                // Context ctx,
                if (ps.Length > pconsumed && SpecialParameterSymbol.IsContextParameter(ps[pconsumed]))
                {
                    cg.EmitLoadContext();
                    pconsumed++;
                }

                if (ps.Length != pconsumed)
                {
                    throw new InvalidOperationException();
                }

                EmitImplicitConversion(cg, cg.EmitCall(method.IsVirtual ? ILOpCode.Callvirt : ILOpCode.Call, method), to, @checked: true);
            }
            else
            {
                throw new NotImplementedException();
            }
        }
Example #24
0
        public CommonConversion ClassifyConversion(TypeSymbol from, TypeSymbol to, ConversionKind kinds)
        {
            if (from == to)
            {
                return(IdentityConversion);
            }

            // implicit conversions handled by 'EmitConversion':
            if (to.SpecialType == SpecialType.System_Void)
            {
                return(IdentityConversion);
            }

            // object cast possible implicitly:
            if ((kinds & ConversionKind.Reference) == ConversionKind.Reference)
            {
                if (from.IsReferenceType && to.IsReferenceType && from.IsOfType(to))
                {
                    // (PHP) string, resource, array, alias -> object: NoConversion

                    if (to.SpecialType != SpecialType.System_Object || !IsSpecialReferenceType(from))
                    {
                        return(ReferenceConversion);
                    }
                }

                if (to.SpecialType == SpecialType.System_Object && (from.IsInterfaceType() || (from.IsReferenceType && from.IsTypeParameter())))
                {
                    return(ReferenceConversion);
                }
            }

            // resolve conversion operator method:
            if ((kinds & ConversionKind.Numeric) == ConversionKind.Numeric)
            {
                var conv = ClassifyNumericConversion(from, to);
                if (conv.Exists)
                {
                    return(conv);
                }
            }

            // strict:
            if ((kinds & ConversionKind.Strict) == ConversionKind.Strict)
            {
                var op = ResolveOperator(from, false, ImplicitConversionOpNames(to), new[] { _compilation.CoreTypes.StrictConvert.Symbol }, target: to);
                if (op != null)
                {
                    return(new CommonConversion(true, false, false, false, true, op));
                }
            }

            // implicit
            if ((kinds & ConversionKind.Implicit) == ConversionKind.Implicit)
            {
                var op = TryWellKnownImplicitConversion(from, to) ?? ResolveOperator(from, false, ImplicitConversionOpNames(to), new[] { to, _compilation.CoreTypes.Convert.Symbol }, target: to);
                if (op != null)
                {
                    return(new CommonConversion(true, false, false, false, true, op));
                }
            }

            // explicit:
            if ((kinds & ConversionKind.Explicit) == ConversionKind.Explicit)
            {
                var op = ResolveOperator(from, false, ExplicitConversionOpNames(to), new[] { to, _compilation.CoreTypes.Convert.Symbol }, target: to);
                if (op != null)
                {
                    return(new CommonConversion(true, false, false, false, false, op));
                }
                // explicit reference conversion (reference type -> reference type)
                else if (
                    from.IsReferenceType && to.IsReferenceType &&
                    !IsSpecialReferenceType(from) && !IsSpecialReferenceType(to) &&
                    !from.IsArray() && !to.IsArray())
                {
                    return(ExplicitReferenceConversion);
                }
            }

            //
            return(NoConversion);
        }
Example #25
0
        /// <summary>
        /// Emits conversion to a class object.
        /// </summary>
        /// <param name="from">Type of value on top of the evaluation stack.</param>
        /// <param name="fromHint">Hint in case of multitype value.</param>
        /// <param name="to">Target type.</param>
        private void EmitConvertToClass(TypeSymbol from, TypeRefMask fromHint, TypeSymbol to)
        {
            Contract.ThrowIfNull(from);
            Contract.ThrowIfNull(to);
            Debug.Assert(to.IsReferenceType);   // TODO: structs other than primitive types
            Debug.Assert(to != CoreTypes.PhpAlias);

            // dereference
            if (from == CoreTypes.PhpAlias)
            {
                Emit_PhpAlias_GetValue();
                from = CoreTypes.PhpValue;
            }

            if (from == to)
            {
                return;
            }

            Debug.Assert(to != CoreTypes.PhpArray && to != CoreTypes.PhpString && to != CoreTypes.PhpAlias);

            if (to == CoreTypes.IPhpCallable)
            {
                // (IPhpCallable)
                if (!from.IsEqualToOrDerivedFrom(CoreTypes.IPhpCallable))
                {
                    if (from.SpecialType == SpecialType.System_String)
                    {
                        EmitCall(ILOpCode.Call, CoreMethods.Operators.AsCallable_String);
                    }
                    else if (
                        from.SpecialType == SpecialType.System_Int64 ||
                        from.SpecialType == SpecialType.System_Boolean ||
                        from.SpecialType == SpecialType.System_Double)
                    {
                        throw new ArgumentException($"{from.Name} cannot be converted to a class of type {to.Name}!");  // TODO: ErrCode
                    }
                    else
                    {
                        EmitConvertToPhpValue(from, fromHint);
                        EmitCall(ILOpCode.Call, CoreMethods.Operators.AsCallable_PhpValue);
                    }
                }
                return;
            }

            if (to.IsArray())
            {
                var arrt = (ArrayTypeSymbol)to;
                if (arrt.IsSZArray)
                {
                    if (arrt.ElementType.SpecialType == SpecialType.System_Byte)
                    {
                        // byte[]

                        // Template: (PhpString).ToBytes(Context)
                        EmitConvertToPhpString(from, fromHint); // PhpString
                        this.EmitLoadContext();                 // Context
                        EmitCall(ILOpCode.Call, CoreMethods.PhpString.ToBytes_Context)
                        .Expect(to);                            // ToBytes()
                        return;
                    }
                }

                throw new NotImplementedException($"Conversion from {from.Name} to {to.Name} is not implemented.");
            }

            switch (from.SpecialType)
            {
            case SpecialType.System_Void:
            case SpecialType.System_Int32:
            case SpecialType.System_Int64:
            case SpecialType.System_Boolean:
            case SpecialType.System_Double:
            case SpecialType.System_String:
                if (to == CoreTypes.Object)
                {
                    from = EmitConvertToPhpValue(from, fromHint);
                    goto default;
                }
                else
                {
                    throw new ArgumentException($"{from.Name} cannot be converted to a class of type {to.Name}!");      // TODO: ErrCode
                }

            default:
                if (from == CoreTypes.PhpValue)
                {
                    if (!fromHint.IsRef && IsClassOnly(fromHint))
                    {
                        // <value>.Object
                        EmitPhpValueAddr();
                        from = EmitCall(ILOpCode.Call, CoreMethods.PhpValue.Object.Getter)
                               .Expect(SpecialType.System_Object);
                    }
                    else
                    {
                        // Convert.ToClass( value )
                        from = EmitCall(ILOpCode.Call, CoreMethods.Operators.ToClass_PhpValue)
                               .Expect(SpecialType.System_Object);
                    }

                    // (T)
                    EmitCastClass(from, to);
                    return;
                }
                if (from == CoreTypes.PhpNumber)
                {
                    // Object
                    EmitPhpNumberAddr();
                    EmitCall(ILOpCode.Call, CoreMethods.PhpNumber.ToClass)
                    .Expect(SpecialType.System_Object);

                    // (T)
                    EmitCastClass(to);
                    return;
                }
                else if (from.IsOfType(CoreTypes.PhpArray))
                {
                    // (T)PhpArray.ToClass();
                    EmitCastClass(EmitCall(ILOpCode.Call, CoreMethods.PhpArray.ToClass), to);
                    return;
                }
                else if (from.IsOfType(CoreTypes.IPhpArray))
                {
                    // (T)Convert.ToClass(IPhpArray)
                    EmitCastClass(EmitCall(ILOpCode.Call, CoreMethods.Operators.ToClass_IPhpArray), to);
                    return;
                }
                else if (from.IsReferenceType)
                {
                    Debug.Assert(from != CoreTypes.PhpAlias);
                    // (T)obj   // let .NET deal with eventual cast error for now
                    EmitCastClass(from, to);
                    return;
                }
                throw new NotImplementedException();
            }
        }
Example #26
0
        public CommonConversion ClassifyConversion(TypeSymbol from, TypeSymbol to, bool checkimplicit = true, bool checkexplicit = true)
        {
            if (from == to)
            {
                return(IdentityConversion);
            }

            if (from.IsReferenceType && to.IsReferenceType && from.IsOfType(to))
            {
                // (PHP) string, resource, array, alias -> object: NoConversion

                if (to.SpecialType != SpecialType.System_Object || !IsSpecialReferenceType(from))
                {
                    return(ReferenceConversion);
                }
            }

            if (to.SpecialType == SpecialType.System_Object && (from.IsInterfaceType() || (from.IsReferenceType && from.IsTypeParameter())))
            {
                return(ReferenceConversion);
            }

            // implicit conversions handled by 'EmitConversion':

            if (to.SpecialType == SpecialType.System_Void)
            {
                return(IdentityConversion);
            }

            // resolve conversion operator method:

            var conv = ClassifyNumericConversion(from, to);

            if (!conv.Exists)
            {
                // TODO: cache result

                var op = checkimplicit
                    ? TryWellKnownImplicitConversion(from, to) ?? ResolveOperator(from, false, ImplicitConversionOpNames(to), new[] { to, _compilation.CoreTypes.Convert.Symbol }, target: to)
                    : null;

                if (op != null)
                {
                    conv = new CommonConversion(true, false, false, false, true, op);
                }
                else if (checkexplicit)
                {
                    op = ResolveOperator(from, false, ExplicitConversionOpNames(to), new[] { to, _compilation.CoreTypes.Convert.Symbol }, target: to);

                    if (op != null)
                    {
                        conv = new CommonConversion(true, false, false, false, false, op);
                    }
                    // explicit reference conversion (reference type -> reference type)
                    else if (
                        from.IsReferenceType && to.IsReferenceType &&
                        !IsSpecialReferenceType(from) && !IsSpecialReferenceType(to) &&
                        !from.IsArray() && !to.IsArray())
                    {
                        conv = ExplicitReferenceConversion;
                    }
                }
            }

            return(conv);
        }
Example #27
0
        /// <summary>
        /// Emits conversion to an object of given type.
        /// </summary>
        /// <param name="from">Type of value on top of the evaluation stack.</param>
        /// <param name="fromHint">Hint in case of multitype value.</param>
        /// <param name="to">Target type.</param>
        private void EmitConvertToClass(TypeSymbol from, TypeRefMask fromHint, TypeSymbol to)
        {
            Contract.ThrowIfNull(from);
            Contract.ThrowIfNull(to);
            Debug.Assert(to.IsReferenceType);
            Debug.Assert(to != CoreTypes.PhpAlias);
            Debug.Assert(!to.IsErrorType(), "Trying to convert to an ErrorType");

            // -> IPhpCallable
            if (to == CoreTypes.IPhpCallable)
            {
                EmitConvertToIPhpCallable(from, fromHint);
                return;
            }

            // -> System.Array
            if (to.IsArray())
            {
                var arrt = (ArrayTypeSymbol)to;
                if (arrt.IsSZArray)
                {
                    // byte[]
                    if (arrt.ElementType.SpecialType == SpecialType.System_Byte)
                    {
                        // Template: (PhpString).ToBytes(Context)
                        EmitConvertToPhpString(from, fromHint); // PhpString
                        EmitPhpStringAddr();
                        this.EmitLoadContext();                 // Context
                        EmitCall(ILOpCode.Call, CoreMethods.PhpString.ToBytes_Context)
                        .Expect(to);                            // ToBytes()
                        return;
                    }

                    throw this.NotImplementedException($"Conversion from {from.Name} to {arrt.ElementType.Name}[] is not implemented.");
                }

                throw this.NotImplementedException($"Conversion from {from.Name} to array {to.Name} is not implemented.");
            }

            // dereference
            if (from == CoreTypes.PhpAlias)
            {
                // <alias>.Value.AsObject() : object
                Emit_PhpAlias_GetValueAddr();
                from = EmitCall(ILOpCode.Call, CoreMethods.PhpValue.AsObject)
                       .Expect(SpecialType.System_Object);
            }

            if (from.IsReferenceType && from.IsOfType(to))
            {
                return;
            }

            Debug.Assert(to != CoreTypes.PhpArray && to != CoreTypes.PhpString && to != CoreTypes.PhpAlias);

            switch (from.SpecialType)
            {
            case SpecialType.System_Void:
            case SpecialType.System_Int32:
            case SpecialType.System_Int64:
            case SpecialType.System_Boolean:
            case SpecialType.System_Double:
            case SpecialType.System_String:
                // Template: null
                EmitPop(from);
                _il.EmitNullConstant();
                return;

            default:

                Debug.Assert(from != CoreTypes.PhpAlias);

                if (from.IsValueType)
                {
                    if (from == CoreTypes.PhpValue)
                    {
                        if (IsClassOnly(fromHint))
                        {
                            // <STACK>.Object
                            EmitPhpValueAddr();
                            from = EmitCall(ILOpCode.Call, CoreMethods.PhpValue.Object.Getter)
                                   .Expect(SpecialType.System_Object);
                        }
                        else
                        {
                            // Convert.AsObject( <STACK> )
                            from = EmitCall(ILOpCode.Call, CoreMethods.Operators.AsObject_PhpValue)
                                   .Expect(SpecialType.System_Object);
                        }
                    }
                    else
                    {
                        // null
                        EmitPop(from);
                        _il.EmitNullConstant();
                        return;
                    }
                }

                //
                break;
            }

            // Template: (T)object
            EmitCastClass(from, to);
        }
Example #28
0
        public void EmitConvertToLong(TypeSymbol from, TypeRefMask fromHint)
        {
            Contract.ThrowIfNull(from);

            // dereference
            if (from == CoreTypes.PhpAlias)
            {
                Emit_PhpAlias_GetValue();
                from = CoreTypes.PhpValue;
            }

            //
            from = EmitSpecialize(from, fromHint);

            switch (from.SpecialType)
            {
                case SpecialType.System_Boolean:
                    _il.EmitOpCode(ILOpCode.Conv_i8);   // bool -> Int64
                    return;

                case SpecialType.System_Int32:
                    _il.EmitOpCode(ILOpCode.Conv_i8);   // Int32 -> Int64
                    return;

                case SpecialType.System_Int64:
                    // nop
                    return;

                case SpecialType.System_Double:
                    _il.EmitOpCode(ILOpCode.Conv_i8);   // double -> int64
                    break;

                case SpecialType.System_String:
                    EmitCall(ILOpCode.Call, CoreMethods.Operators.ToLong_String)
                        .Expect(SpecialType.System_Int64);
                    break;

                default:
                    if (from == CoreTypes.PhpNumber)
                    {
                        EmitPhpNumberAddr();
                        EmitCall(ILOpCode.Call, CoreMethods.PhpNumber.ToLong);
                        return;
                    }
                    else if (from.IsOfType(CoreTypes.IPhpArray))
                    {
                        // (long)IPhpArray.Count
                        EmitCall(ILOpCode.Callvirt, CoreMethods.IPhpArray.get_Count);
                        _il.EmitOpCode(ILOpCode.Conv_i8);   // Int32 -> Int64
                        return;
                    }
                    else if (from == CoreTypes.PhpValue)
                    {
                        EmitCall(ILOpCode.Call, CoreMethods.Operators.ToLong_PhpValue);
                        return;
                    }
                    else
                    {
                        throw new NotImplementedException();
                    }
            }
        }
Example #29
0
        public void EmitConvertToInt(TypeSymbol from, TypeRefMask fromHint)
        {
            Contract.ThrowIfNull(from);

            // dereference
            if (from == CoreTypes.PhpAlias)
            {
                Emit_PhpAlias_GetValue();
                from = CoreTypes.PhpValue;
            }

            //
            from = EmitSpecialize(from, fromHint);

            switch (from.SpecialType)
            {
                case SpecialType.System_Int32:
                    return;

                default:

                    if (from.IsOfType(CoreTypes.IPhpArray))
                    {
                        // IPhpArray.Count
                        EmitCall(ILOpCode.Callvirt, CoreMethods.IPhpArray.get_Count);
                    }
                    else
                    {
                        EmitConvertToLong(from, 0);
                        _il.EmitOpCode(ILOpCode.Conv_i4);   // Int64 -> Int32
                    }
                    return;
            }
        }
Example #30
0
        /// <summary>
        /// Emits conversion from one CLR type to another using PHP conventions.
        /// </summary>
        /// <param name="from">Type of value on top of evaluation stack.</param>
        /// <param name="fromHint">Type hint in case of a multityple type choices (like PhpValue or PhpNumber or PhpAlias).</param>
        /// <param name="to">Target CLR type.</param>
        public void EmitConvert(TypeSymbol from, TypeRefMask fromHint, TypeSymbol to)
        {
            Contract.ThrowIfNull(from);
            Contract.ThrowIfNull(to);

            Debug.Assert(!from.IsUnreachable);
            Debug.Assert(!to.IsUnreachable);
            Debug.Assert(!to.IsErrorType(), "Conversion to an error type.");

            // conversion is not needed:
            if (from.SpecialType == to.SpecialType &&
                (from == to || (to.SpecialType != SpecialType.System_Object && from.IsOfType(to))))
            {
                return;
            }

            if (from.SpecialType == SpecialType.System_Void)
            {
                // void -> T
                EmitLoadDefault(to);
                return;
            }

            //
            from = EmitSpecialize(from, fromHint);

            if (!this.TryEmitImplicitConversion(from, to))
            {
                // specialized conversions:
                if (to == CoreTypes.PhpValue)
                {
                    EmitConvertToPhpValue(from, fromHint);
                }
                else if (to == CoreTypes.PhpString)
                {
                    // -> PhpString
                    EmitConvertToPhpString(from, fromHint);
                }
                else if (to == CoreTypes.PhpAlias)
                {
                    EmitConvertToPhpValue(from, fromHint);
                    Emit_PhpValue_MakeAlias();
                }
                else if (to.IsReferenceType)
                {
                    if (to == CoreTypes.PhpArray || to == CoreTypes.IPhpArray || to == CoreTypes.IPhpEnumerable || to == CoreTypes.PhpHashtable)
                    {
                        // -> PhpArray
                        // TODO: try unwrap "value.Object as T"
                        EmitConvertToPhpArray(from, fromHint);
                    }
                    else
                    {
                        // -> Object, PhpResource
                        EmitConvertToClass(from, fromHint, to);
                    }
                }
                else if (to.IsNullableType(out var ttype))
                {
                    // Template: new Nullable<T>( (T)from )
                    EmitConvert(from, fromHint, ttype);
                    EmitCall(ILOpCode.Newobj, ((NamedTypeSymbol)to).InstanceConstructors[0]);
                }
                else if (to.SpecialType == SpecialType.System_DateTime)
                {
                    EmitConvertToDateTime(from);
                }
                else
                {
                    throw this.NotImplementedException($"Conversion from '{from}' to '{to}'");
                }
            }
        }
Example #31
0
        public TypeSymbol EmitConvertToDouble(TypeSymbol from, TypeRefMask fromHint)
        {
            Contract.ThrowIfNull(from);

            // dereference
            if (from == CoreTypes.PhpAlias)
            {
                Emit_PhpAlias_GetValue();
                from = CoreTypes.PhpValue;
            }

            from = EmitSpecialize(from, fromHint);
            var dtype = CoreTypes.Double.Symbol;

            switch (from.SpecialType)
            {
                case SpecialType.System_Int32:
                    _il.EmitOpCode(ILOpCode.Conv_r8);   // Int32 -> Double
                    return dtype;

                case SpecialType.System_Int64:
                    _il.EmitOpCode(ILOpCode.Conv_r8);   // Int64 -> Double
                    return dtype;

                case SpecialType.System_Double:
                    // nop
                    return dtype;

                case SpecialType.System_String:
                    return EmitCall(ILOpCode.Call, CoreMethods.Operators.ToDouble_String)
                        .Expect(SpecialType.System_Double);

                default:
                    if (from == CoreTypes.PhpNumber)
                    {
                        EmitPhpNumberAddr();
                        EmitCall(ILOpCode.Call, CoreMethods.PhpNumber.ToDouble);
                        return dtype;
                    }
                    else if (from.IsOfType(CoreTypes.IPhpArray))
                    {
                        // (double)IPhpArray.Count
                        EmitCall(ILOpCode.Callvirt, CoreMethods.IPhpArray.get_Count);
                        _il.EmitOpCode(ILOpCode.Conv_r8);   // Int32 -> Double
                        return dtype;
                    }
                    else if (from == CoreTypes.PhpValue)
                    {
                        return EmitCall(ILOpCode.Call, CoreMethods.Operators.ToDouble_PhpValue);
                    }
                    else
                    {
                        throw new NotImplementedException();
                    }
            }
        }
Example #32
0
        /// <summary>
        /// Emits conversion from one CLR type to another using PHP conventions.
        /// </summary>
        /// <param name="from">Type of value on top of evaluation stack.</param>
        /// <param name="fromHint">Type hint in case of a multityple type choices (like PhpValue or PhpNumber or PhpAlias).</param>
        /// <param name="to">Target CLR type.</param>
        /// <param name="conversion">Conversion semantic.</param>
        public void EmitConvert(TypeSymbol from, TypeRefMask fromHint, TypeSymbol to, ConversionKind conversion = ConversionKind.Implicit)
        {
            Contract.ThrowIfNull(from);
            Contract.ThrowIfNull(to);

            Debug.Assert(!from.IsUnreachable);
            Debug.Assert(!to.IsUnreachable);
            Debug.Assert(!to.IsErrorType(), "Conversion to an error type.");

            // conversion is not needed:
            if (from.SpecialType == to.SpecialType &&
                (from == to || (to.SpecialType != SpecialType.System_Object && from.IsOfType(to))))
            {
                return;
            }

            if (from.SpecialType == SpecialType.System_Void)
            {
                // void -> T
                EmitLoadDefault(to);
                return;
            }

            //
            from = EmitSpecialize(from, fromHint);

            if (from != to)
            {
                var conv = DeclaringCompilation.Conversions.ClassifyConversion(from, to, conversion);
                if (conv.Exists)
                {
                    ConversionsExtensions.EmitConversion(this, conv, from, to, @checked: false);
                }
                else
                {
                    // specialized conversions:
                    if (to == CoreTypes.PhpValue)
                    {
                        EmitConvertToPhpValue(from, fromHint);
                    }
                    else if (to == CoreTypes.PhpString)
                    {
                        // -> PhpString
                        EmitConvertToPhpString(from, fromHint);
                    }
                    else if (to == CoreTypes.PhpAlias)
                    {
                        EmitConvertToPhpValue(from, fromHint);
                        Emit_PhpValue_MakeAlias();
                    }
                    else if (to.IsReferenceType)
                    {
                        if (to == CoreTypes.PhpArray || to == CoreTypes.IPhpArray || to == CoreTypes.IPhpEnumerable || to == CoreTypes.PhpHashtable)
                        {
                            // -> PhpArray
                            // TODO: try unwrap "value.Object as T"
                            EmitConvertToPhpArray(from, fromHint);
                        }
                        else
                        {
                            // -> Object, PhpResource
                            EmitConvertToClass(from, fromHint, to);
                        }
                    }
                    else if (to.IsNullableType(out var ttype))
                    {
                        EmitConvertToNullable_T(from, fromHint, to, ttype, conversion);
                    }
                    else if (to.SpecialType == SpecialType.System_DateTime)
                    {
                        EmitConvertToDateTime(from);
                    }
                    else
                    {
                        throw this.NotImplementedException($"Conversion from '{from}' to '{to}'");
                    }
                }
            }
        }
Example #33
0
        public static TypeSymbol EmitConvertToPhpValue(TypeSymbol from, TypeRefMask fromHint, ILBuilder il, Emit.PEModuleBuilder module, DiagnosticBag diagnostic)
        {
            Contract.ThrowIfNull(from);

            var compilation = module.Compilation;

            switch (from.SpecialType)
            {
                case SpecialType.System_Boolean:
                    il.EmitCall(module, diagnostic, ILOpCode.Call, compilation.CoreMethods.PhpValue.Create_Boolean);
                    break;
                case SpecialType.System_Int32:
                    il.EmitOpCode(ILOpCode.Conv_i8);   // Int32 -> Int64
                    goto case SpecialType.System_Int64; // PhpValue.Create((long)<stack>)
                case SpecialType.System_Int64:
                    il.EmitCall(module, diagnostic, ILOpCode.Call, compilation.CoreMethods.PhpValue.Create_Long);
                    break;
                case SpecialType.System_Double:
                    il.EmitCall(module, diagnostic, ILOpCode.Call, compilation.CoreMethods.PhpValue.Create_Double);
                    break;
                case SpecialType.System_Void:
                    Emit_PhpValue_Void(il, module, diagnostic);
                    break;
                default:
                    if (from == compilation.CoreTypes.PhpAlias)
                    {
                        il.EmitCall(module, diagnostic, ILOpCode.Call, compilation.CoreMethods.PhpValue.Create_PhpAlias)
                            .Expect(compilation.CoreTypes.PhpValue);
                        break;
                    }
                    else if (from == compilation.CoreTypes.PhpValue)
                    {
                        // nop
                        break;
                    }
                    else if (from == compilation.CoreTypes.String)
                    {
                        il.EmitCall(module, diagnostic, ILOpCode.Call, compilation.CoreMethods.PhpValue.Create_String)
                            .Expect(compilation.CoreTypes.PhpValue);
                        break;
                    }
                    else if (from == compilation.CoreTypes.PhpString)
                    {
                        il.EmitCall(module, diagnostic, ILOpCode.Call, compilation.CoreMethods.PhpValue.Create_PhpString)
                            .Expect(compilation.CoreTypes.PhpValue);
                        break;
                    }
                    else if (from == compilation.CoreTypes.PhpNumber)
                    {
                        il.EmitCall(module, diagnostic, ILOpCode.Call, compilation.CoreMethods.PhpValue.Create_PhpNumber)
                            .Expect(compilation.CoreTypes.PhpValue);
                        break;
                    }
                    else if (from.IsOfType(compilation.CoreTypes.PhpArray))
                    {
                        il.EmitCall(module, diagnostic, ILOpCode.Call, compilation.CoreMethods.PhpValue.Create_PhpArray)
                            .Expect(compilation.CoreTypes.PhpValue);
                        break;
                    }
                    else if (from == compilation.CoreTypes.IntStringKey)
                    {
                        il.EmitCall(module, diagnostic, ILOpCode.Call, compilation.CoreMethods.PhpValue.Create_IntStringKey)
                            .Expect(compilation.CoreTypes.PhpValue);
                        break;
                    }
                    else if (from.IsReferenceType)
                    {
                        il.EmitCall(module, diagnostic, ILOpCode.Call, compilation.CoreMethods.PhpValue.FromClass_Object)
                            .Expect(compilation.CoreTypes.PhpValue);
                        break;
                    }
                    else
                    {
                        throw new NotImplementedException($"{from.Name}");
                    }
            }

            //
            return compilation.CoreTypes.PhpValue;
        }