EmitCastClass() private méthode

private EmitCastClass ( TypeSymbol type ) : void
type TypeSymbol
Résultat void
        /// <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.IsNullable)
            {
                if (from.IsNullableType(out var ttype))
                {
                    if (op != null)
                    {
                        // TODO
                        throw new ArgumentException(nameof(op));
                    }

                    var lbltrue = new NamedLabel("has value");
                    var lblend  = new NamedLabel("end");
                    var tmp     = cg.GetTemporaryLocal(from, true);
                    cg.Builder.EmitLocalStore(tmp);

                    // Template: tmp.HasValue ? convert(tmp.Value) : default
                    cg.Builder.EmitLocalAddress(tmp);
                    cg.EmitCall(ILOpCode.Call, cg.DeclaringCompilation.System_Nullable_T_HasValue(from));
                    cg.Builder.EmitBranch(ILOpCode.Brtrue, lbltrue);

                    // default:
                    cg.EmitLoadDefault(to);
                    cg.Builder.EmitBranch(ILOpCode.Br, lblend);
                    // cg.Builder.AdjustStack(-1); // ?

                    // lbltrue:
                    cg.Builder.MarkLabel(lbltrue);
                    // Template: convert( tmp.GetValueOrDefault() )
                    cg.Builder.EmitLocalAddress(tmp);
                    cg.EmitCall(ILOpCode.Call, cg.DeclaringCompilation.System_Nullable_T_GetValueOrDefault(from)).Expect(ttype);
                    EmitConversion(cg, conversion.WithIsNullable(false), ttype, to, op, @checked);

                    // lblend:
                    cg.Builder.MarkLabel(lblend);

                    return;
                }

                if (to.IsNullableType(out ttype)) // NOTE: not used yet
                {
                    // new Nullable<TType>( convert(from) )
                    EmitConversion(cg, conversion.WithIsNullable(false), from, ttype, op, @checked);
                    cg.EmitCall(ILOpCode.Newobj, ((NamedTypeSymbol)to).InstanceConstructors[0]); // new Nullable<T>( STACK )
                    return;
                }

                throw Roslyn.Utilities.ExceptionUtilities.Unreachable;
            }

            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 || from.IsVoid())
                        {
                            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 (!ps[0].Type.IsAssignableFrom(from))
                            {
                                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();
            }
        }
        /// <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 (!ps[0].Type.IsAssignableFrom(from))
                            {
                                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();
            }
        }
Exemple #3
0
        public TypeSymbol EmitLoad(CodeGenerator cg)
        {
            Debug.Assert(_access.IsRead);

            var type = _place.TypeOpt;

            // Ensure Object ($x->.. =)
            if (_access.EnsureObject)
            {
                if (type == cg.CoreTypes.PhpAlias)
                {
                    _place.EmitLoad(cg.Builder);
                    return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpAlias.EnsureObject)
                        .Expect(SpecialType.System_Object);
                }
                else if (type == cg.CoreTypes.PhpValue)
                {
                    _place.EmitLoadAddress(cg.Builder);
                    cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpValue.EnsureObject)
                        .Expect(SpecialType.System_Object);

                    if (_thint.IsSingleType && cg.IsClassOnly(_thint))
                    {
                        var tref = cg.Routine.TypeRefContext.GetTypes(_thint)[0];
                        var clrtype = (TypeSymbol)cg.DeclaringCompilation.GetTypeByMetadataName(tref.QualifiedName.ClrName());
                        if (clrtype != null && !clrtype.IsErrorType() && clrtype != cg.CoreTypes.Object)
                        {
                            cg.EmitCastClass(clrtype);
                            return clrtype;
                        }
                    }

                    return cg.CoreTypes.Object;
                }
                else if (type.IsOfType(cg.CoreTypes.IPhpArray))
                {
                    // PhpArray -> stdClass
                    // PhpString -> stdClass (?)
                    // otherwise keep the instance on stack
                    throw new NotImplementedException();
                }
                else
                {
                    if (type.IsReferenceType)
                    {
                        if (type == cg.CoreTypes.Object)
                        {
                            // Operators.EnsureObject(ref <place>)
                            _place.EmitLoadAddress(cg.Builder);
                            return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.EnsureObject_ObjectRef)
                                .Expect(SpecialType.System_Object);
                        }
                        else
                        {
                            // <place>
                            return _place.EmitLoad(cg.Builder);
                        }
                    }
                    else
                    {
                        // return new stdClass(ctx)
                        throw new NotImplementedException();
                    }
                }
            }
            // Ensure Array ($x[] =)
            else if (_access.EnsureArray)
            {
                if (type == cg.CoreTypes.PhpAlias)
                {
                    // <place>.EnsureArray()
                    _place.EmitLoad(cg.Builder);
                    return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpAlias.EnsureArray)
                        .Expect(cg.CoreTypes.IPhpArray);
                }
                else if (type == cg.CoreTypes.PhpValue)
                {
                    if (cg.IsArrayOnly(_thint))
                    {
                        // uses typehint and accesses .Array directly if possible
                        // <place>.Array
                        _place.EmitLoadAddress(cg.Builder);
                        return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpValue.get_Array)
                            .Expect(cg.CoreTypes.PhpArray);
                    }
                    else
                    {
                        // <place>.EnsureArray()
                        _place.EmitLoadAddress(cg.Builder);
                        return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpValue.EnsureArray)
                            .Expect(cg.CoreTypes.IPhpArray);
                    }
                }
                else if (type.IsOfType(cg.CoreTypes.IPhpArray))
                {
                    // Operators.EnsureArray(ref <place>)
                    _place.EmitLoadAddress(cg.Builder);

                    if (type == cg.CoreTypes.PhpArray)
                    {
                        return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.EnsureArray_PhpArrayRef)
                            .Expect(cg.CoreTypes.PhpArray);
                    }
                    else
                    {
                        return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.EnsureArray_IPhpArrayRef)
                            .Expect(cg.CoreTypes.IPhpArray);
                    }
                }

                throw new NotImplementedException("EnsureArray(" + type.Name + ")");
            }
            // Ensure Alias (&$x)
            else if (_access.IsReadRef)
            {
                if (type == cg.CoreTypes.PhpAlias)
                {
                    // TODO: <place>.AddRef()
                    return _place.EmitLoad(cg.Builder);
                }
                else if (type == cg.CoreTypes.PhpValue)
                {
                    // return <place>.EnsureAlias()
                    _place.EmitLoadAddress(cg.Builder);
                    return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpValue.EnsureAlias)
                        .Expect(cg.CoreTypes.PhpAlias);
                }
                else if (type == cg.CoreTypes.PhpNumber)
                {
                    throw new NotImplementedException();
                }
                else
                {
                    Debug.Assert(false, "value cannot be aliased");

                    // new PhpAlias((PhpValue)<place>, 1)
                    cg.EmitConvertToPhpValue(_place.EmitLoad(cg.Builder), 0);
                    return cg.Emit_PhpValue_MakeAlias();
                }
            }
            // Read Value & Dereference eventually
            else
            {
                if (type == cg.CoreTypes.PhpAlias)
                {
                    _place.EmitLoad(cg.Builder);

                    if (_access.TargetType == cg.CoreTypes.PhpArray)
                    {
                        // <place>.Value.ToArray()
                        cg.Builder.EmitOpCode(ILOpCode.Ldflda);
                        cg.EmitSymbolToken(cg.CoreMethods.PhpAlias.Value, null);
                        return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpValue.ToArray)
                            .Expect(cg.CoreTypes.PhpArray);
                    }

                    return cg.Emit_PhpAlias_GetValue();
                }
                else if (type == cg.CoreTypes.PhpValue)
                {
                    if (_access.TargetType == cg.CoreTypes.PhpArray)
                    {
                        // <place>.ToArray()
                        _place.EmitLoadAddress(cg.Builder);
                        return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpValue.ToArray)
                            .Expect(cg.CoreTypes.PhpArray);
                    }

                    // TODO: dereference if applicable (=> PhpValue.Alias.Value)
                    return _place.EmitLoad(cg.Builder);
                }
                else
                {
                    return _place.EmitLoad(cg.Builder);
                }
            }
        }