/// <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)); } }
/// <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); }
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); } } }
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); } }
/// <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); }
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; } }
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(); } } }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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}"); } }
/// <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); }
/// <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(); } }
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(); } }
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(); } } }
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(); } }
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); }
/// <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}"); }
/// <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}"); }
/// <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}"); } }
/// <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(); } }
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); }
/// <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(); } }
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); }
/// <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); }
/// <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}'"); } } }
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(); } } }
/// <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}'"); } } } }
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; }