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