private EmitCall ( ILOpCode code, |
||
code | ILOpCode | Call op code, Call, Callvirt, Calli. |
method | Method reference. | |
리턴 | TypeSymbol |
/// <summary> /// Emits load of <c>PhpTypeInfo</c>. /// </summary> /// <param name="cg">Code generator instance.</param> /// <param name="throwOnError">Emits PHP error in case type is not declared.</param> /// <remarks>Emits <c>NULL</c> in case type is not declared.</remarks> internal void EmitLoadTypeInfo(CodeGenerator cg, bool throwOnError = false) { Debug.Assert(cg != null); Debug.Assert(throwOnError == false, "Not Implemented!"); // TODO: if (throwOnError) { if (DUP == null) PhpException.TypeNotDeclared(<typename>) if (this.ResolvedType != null) { // CALL GetPhpTypeInfo<T>() cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Dynamic.GetPhpTypeInfo_T.Symbol.Construct(this.ResolvedType)); } else { // CALL <ctx>.GetDeclaredType(<typename>) cg.EmitLoadContext(); this.EmitClassName(cg); cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Context.GetDeclaredType_string); } }
/// <summary> /// Emits load of statics holder. /// </summary> internal TypeSymbol EmitLoadStatics(CodeGenerator cg) { var statics = TryGetStatics(); if (statics != null && statics.GetMembers().OfType<IFieldSymbol>().Any()) { // Template: <ctx>.GetStatics<_statics>() cg.EmitLoadContext(); return cg.EmitCall(ILOpCode.Callvirt, cg.CoreMethods.Context.GetStatic_T.Symbol.Construct(statics)) .Expect(statics); } return null; }
/// <summary> /// Emits subtraction operator. /// </summary> internal static TypeSymbol EmitSub(CodeGenerator cg, TypeSymbol xtype, BoundExpression right, TypeSymbol resultTypeOpt = null) { var il = cg.Builder; xtype = cg.EmitConvertIntToLong(xtype); // int|bool -> int64 TypeSymbol ytype; switch (xtype.SpecialType) { case SpecialType.System_Int64: ytype = cg.EmitConvertIntToLong(cg.Emit(right)); if (ytype.SpecialType == SpecialType.System_Int64) { // i8 - i8 : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Subtract_long_long) .Expect(cg.CoreTypes.PhpNumber); } else if (ytype.SpecialType == SpecialType.System_Double) { // i8 - r8 : r8 return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Subtract_long_double) .Expect(cg.CoreTypes.Double); } else if (ytype == cg.CoreTypes.PhpNumber) { // i8 - number : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Subtract_long_number) .Expect(cg.CoreTypes.PhpNumber); } else { ytype = cg.EmitConvertToPhpValue(ytype, 0); // i8 - value : value return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Subtract_long_value) .Expect(cg.CoreTypes.PhpNumber); } case SpecialType.System_Double: ytype = cg.EmitConvertNumberToDouble(right); // bool|int|long|number -> double if (ytype.SpecialType == SpecialType.System_Double) { // r8 - r8 : r8 il.EmitOpCode(ILOpCode.Sub); return cg.CoreTypes.Double; } throw new NotImplementedException($"Sub(double, {ytype.Name})"); default: if (xtype == cg.CoreTypes.PhpNumber) { ytype = cg.EmitConvertIntToLong(cg.Emit(right)); if (ytype.SpecialType == SpecialType.System_Int64) { // number - i8 : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Subtract_number_long) .Expect(cg.CoreTypes.PhpNumber); } else if (ytype.SpecialType == SpecialType.System_Double) { // number - r8 : double return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Subtract_number_double) .Expect(SpecialType.System_Double); } else if (ytype == cg.CoreTypes.PhpNumber) { // number - number : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Subtract_number_number) .Expect(cg.CoreTypes.PhpNumber); } throw new NotImplementedException($"Sub(PhpNumber, {ytype.Name})"); } else if (xtype == cg.CoreTypes.PhpValue) { ytype = cg.EmitConvertIntToLong(cg.Emit(right)); if (ytype.SpecialType == SpecialType.System_Int64) { // value - i8 : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Subtract_value_long) .Expect(cg.CoreTypes.PhpNumber); } else if (ytype.SpecialType == SpecialType.System_Double) { // value - r8 : r8 return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Subtract_value_double) .Expect(SpecialType.System_Double); } else if (ytype == cg.CoreTypes.PhpNumber) { // value - number : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Subtract_value_number) .Expect(cg.CoreTypes.PhpNumber); } else if (ytype == cg.CoreTypes.PhpValue) { // value - value : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Subtract_value_value) .Expect(cg.CoreTypes.PhpNumber); } throw new NotImplementedException($"Sub(PhpValue, {ytype.Name})"); } throw new NotImplementedException($"Sub({xtype.Name},...)"); } }
internal override TypeSymbol Emit(CodeGenerator cg) { cg.EmitConvert(this.Variable, cg.CoreTypes.PhpValue); return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.IsEmpty_PhpValue) .Expect(SpecialType.System_Boolean); }
internal override TypeSymbol Emit(CodeGenerator cg) { switch (this.Type) { case PseudoConstUse.Types.File: // <ctx>.FilePath<TScript>() cg.EmitLoadContext(); return cg.EmitCall(ILOpCode.Callvirt, cg.CoreMethods.Context.ScriptPath_TScript.Symbol.Construct(cg.Routine.ContainingFile)) .Expect(SpecialType.System_String); default: // the other pseudoconstants should be resolved by flow analysis throw ExceptionUtilities.Unreachable; } }
void IBoundReference.EmitStore(CodeGenerator cg, TypeSymbol valueType) { // Template: array[index] var isphparr = (this.Array.ResultType == cg.CoreTypes.PhpArray); // whether the target is instance of PhpArray, otherwise it is an IPhpArray and we have to use .callvirt if (Access.IsWriteRef) { // PhpAlias if (valueType != cg.CoreTypes.PhpAlias) { cg.EmitConvertToPhpValue(valueType, 0); cg.Emit_PhpValue_MakeAlias(); } // .SetItemAlias(key, alias) or .AddValue(PhpValue.Create(alias)) if (this.Index != null) { if (isphparr) cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpArray.SetItemAlias_IntStringKey_PhpAlias); else cg.EmitCall(ILOpCode.Callvirt, cg.CoreMethods.IPhpArray.SetItemAlias_IntStringKey_PhpAlias); } else { cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpValue.Create_PhpAlias); if (isphparr) cg.EmitCall(ILOpCode.Callvirt, cg.CoreMethods.IPhpArray.AddValue_PhpValue); else cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpArray.AddValue_PhpValue); } } else if (Access.IsUnset) { if (this.Index == null) throw new InvalidOperationException(); // .RemoveKey(key) if (isphparr) cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpArray.RemoveKey_IntStringKey); else cg.EmitCall(ILOpCode.Callvirt, cg.CoreMethods.IPhpArray.RemoveKey_IntStringKey); } else { Debug.Assert(Access.IsWrite); cg.EmitConvertToPhpValue(valueType, 0); // .SetItemValue(key, value) or .AddValue(value) if (this.Index != null) { if (isphparr) cg.EmitCall(ILOpCode.Callvirt, cg.CoreMethods.IPhpArray.SetItemValue_IntStringKey_PhpValue); else cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpArray.SetItemValue_IntStringKey_PhpValue); } else { if (isphparr) cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpArray.AddValue_PhpValue); else cg.EmitCall(ILOpCode.Callvirt, cg.CoreMethods.IPhpArray.AddValue_PhpValue); } } }
void EmitArrayPrepare(CodeGenerator cg, InstanceCacheHolder instanceOpt) { InstanceCacheHolder.EmitInstance(instanceOpt, cg, Array); if (Array.ResultType.IsOfType(cg.CoreTypes.IPhpArray)) { // ok } else if (Array.ResultType == cg.CoreTypes.PhpValue) { // Convert.ToArray() cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.ToArray_PhpValue); } else if (Array.ResultType == cg.CoreTypes.String) { // new PhpString(string) cg.EmitCall(ILOpCode.Newobj, cg.CoreMethods.Ctors.PhpString_string); } else if (Array.ResultType == cg.CoreTypes.Void) { // TODO: uninitialized value, report error Debug.WriteLine("Use of uninitialized value."); cg.EmitCall(ILOpCode.Newobj, cg.CoreMethods.Ctors.PhpString); } else { throw new NotImplementedException(); // TODO: emit convert as PhpArray } }
static TypeSymbol EmitAppend(CodeGenerator cg, TypeSymbol xtype, BoundExpression y) { if (xtype == cg.CoreTypes.PhpString) { // x.Append(y); return x; cg.Builder.EmitOpCode(ILOpCode.Dup); cg.EmitConvert(y, cg.CoreTypes.String); cg.EmitCall(ILOpCode.Callvirt, cg.CoreMethods.PhpString.Append_String); // return xtype; } else { // concat(x, y) cg.EmitConvert(xtype, 0, cg.CoreTypes.String); cg.EmitConvert(y, cg.CoreTypes.String); return cg.EmitCall(ILOpCode.Newobj, cg.CoreMethods.Ctors.PhpString_string_string); } }
internal override TypeSymbol Emit(CodeGenerator cg) { MethodSymbol ctorsymbol; if (_arguments.Length == 0) { // <ctx>.Exit(); ctorsymbol = cg.CoreMethods.Ctors.ScriptDiedException; } else { // LOAD <status> var t = cg.Emit(_arguments[0].Value); switch (t.SpecialType) { case SpecialType.System_Int32: cg.Builder.EmitOpCode(ILOpCode.Conv_i8); // i4 -> i8 goto case SpecialType.System_Int64; case SpecialType.System_Int64: ctorsymbol = cg.CoreMethods.Ctors.ScriptDiedException_Long; break; default: cg.EmitConvertToPhpValue(t, 0); ctorsymbol = cg.CoreMethods.Ctors.ScriptDiedException_PhpValue; break; } } // cg.EmitCall(ILOpCode.Newobj, ctorsymbol); cg.Builder.EmitThrow(false); // return cg.CoreTypes.Void; }
TypeSymbol EmitBitNot(CodeGenerator cg) { var il = cg.Builder; var t = cg.Emit(this.Operand); switch (t.SpecialType) { case SpecialType.System_Double: case SpecialType.System_Int32: // r8|i4 -> i8 il.EmitOpCode(ILOpCode.Conv_i8); goto case SpecialType.System_Int64; case SpecialType.System_Int64: il.EmitOpCode(ILOpCode.Not); // ~i64 : i64 return cg.CoreTypes.Long; case SpecialType.System_Boolean: throw new NotImplementedException(); // Err default: if (t == cg.CoreTypes.PhpArray) { // ERR } // ~ PhpValue cg.EmitConvert(t, Operand.TypeRefMask, cg.CoreTypes.PhpValue); return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.BitwiseNot_PhpValue); } }
TypeSymbol EmitMinus(CodeGenerator cg) { // Template: 0L - Operand var il = cg.Builder; var t = cg.Emit(this.Operand); switch (t.SpecialType) { case SpecialType.System_Double: // -r8 il.EmitOpCode(ILOpCode.Neg); return t; case SpecialType.System_Int32: // -(i8)i4 il.EmitOpCode(ILOpCode.Conv_i8); // i4 -> i8 il.EmitOpCode(ILOpCode.Neg); // result will fit into long for sure return cg.CoreTypes.Long; case SpecialType.System_Int64: // PhpNumber.Minus(i8) : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Negation_long) .Expect(cg.CoreTypes.PhpNumber); default: if (t != cg.CoreTypes.PhpNumber) { cg.EmitConvertToPhpNumber(t, 0); } return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Negation) .Expect(cg.CoreTypes.PhpNumber); } }
internal static TypeSymbol EmitPow(CodeGenerator cg, TypeSymbol xtype, FlowAnalysis.TypeRefMask xtype_hint, BoundExpression right) { var il = cg.Builder; TypeSymbol ytype; xtype = cg.EmitConvertIntToLong(xtype); // int|bool -> long switch (xtype.SpecialType) { case SpecialType.System_Int64: ytype = cg.EmitConvertIntToLong(cg.Emit(right)); // int|bool -> long if (ytype.SpecialType == SpecialType.System_Int64) { // i8 ** i8 : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Pow_long_long); } else if (ytype.SpecialType == SpecialType.System_Double) { // i8 ** r8 : r8 return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Pow_long_double); } else if (ytype == cg.CoreTypes.PhpNumber) { // i8 ** number : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Pow_long_number); } // y -> PhpValue cg.EmitConvert(ytype, right.TypeRefMask, cg.CoreTypes.PhpValue); ytype = cg.CoreTypes.PhpValue; // i8 ** value : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Pow_long_value); case SpecialType.System_Double: ytype = cg.EmitConvertNumberToDouble(right); // int|bool|long|number -> double if (ytype.SpecialType == SpecialType.System_Double) { // r8 ** r8 : r8 return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Pow_double_double); } // y -> PhpValue cg.EmitConvert(ytype, right.TypeRefMask, cg.CoreTypes.PhpValue); ytype = cg.CoreTypes.PhpValue; // r8 ** value : r8 return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Pow_double_value); default: if (xtype == cg.CoreTypes.PhpNumber) { ytype = cg.EmitConvertIntToLong(cg.Emit(right)); // int|bool -> long if (ytype == cg.CoreTypes.Double) { // number ** r8 : r8 return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Pow_number_double); } if (ytype.SpecialType == SpecialType.System_Int64) { // y -> number ytype = cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Create_Long); } if (ytype == cg.CoreTypes.PhpNumber) { // number ** number : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Pow_number_number); } // y -> PhpValue ytype = cg.EmitConvertToPhpValue(ytype, right.TypeRefMask); // number ** value : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Pow_number_value); } // x -> PhpValue xtype = cg.EmitConvertToPhpValue(xtype, xtype_hint); cg.EmitConvert(right, cg.CoreTypes.PhpValue); ytype = cg.CoreTypes.PhpValue; // value ** value : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Pow_value_value); } }
public static void EmitNumericConversion(this CodeGenerator cg, TypeSymbol from, TypeSymbol to, bool @checked = false) { if (to.IsEnumType()) { to = to.GetEnumUnderlyingType(); } if (from.IsEnumType()) { from = from.GetEnumUnderlyingType(); } if (from.SpecialType == SpecialType.System_Decimal) { // explicit numeric conversion, treating decimal as double // (double)System.Decimal cg.EmitCall( ILOpCode.Call, (MethodSymbol)cg.DeclaringCompilation.GetSpecialTypeMember(SpecialMember.System_Decimal__op_Explicit_ToDouble)); cg.Builder.EmitOpCode(ILOpCode.Conv_r8); // from = cg.CoreTypes.Double; } if (to.SpecialType == SpecialType.System_Decimal) { EmitNumericConversionToDecimal(cg, from, @checked); return; } var fromcode = from.PrimitiveTypeCode; var tocode = to.PrimitiveTypeCode; if (fromcode == tocode) { return; } if (tocode == Microsoft.Cci.PrimitiveTypeCode.Boolean) { switch (fromcode) { case Microsoft.Cci.PrimitiveTypeCode.Float32: // Template: !(STACK == 0.0f) cg.Builder.EmitSingleConstant(0.0f); cg.Builder.EmitOpCode(ILOpCode.Ceq); cg.EmitLogicNegation(); return; case Microsoft.Cci.PrimitiveTypeCode.Float64: // Template: !(STACK == 0.0) cg.Builder.EmitDoubleConstant(0.0); cg.Builder.EmitOpCode(ILOpCode.Ceq); cg.EmitLogicNegation(); return; } // otherwise, // treat boolean as to int32 conversion tocode = Microsoft.Cci.PrimitiveTypeCode.Int32; } if (fromcode == Microsoft.Cci.PrimitiveTypeCode.Boolean) { fromcode = Microsoft.Cci.PrimitiveTypeCode.Int32; } // cg.Builder.EmitNumericConversion(fromcode, tocode, @checked); }
/// <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 check for values equality. /// Lefts <c>bool</c> on top of evaluation stack. /// </summary> internal static TypeSymbol EmitEquality(CodeGenerator cg, TypeSymbol xtype, BoundExpression right) { TypeSymbol ytype; switch (xtype.SpecialType) { case SpecialType.System_Boolean: // bool == y.ToBoolean() cg.EmitConvert(right, cg.CoreTypes.Boolean); cg.Builder.EmitOpCode(ILOpCode.Ceq); return cg.CoreTypes.Boolean; case SpecialType.System_Int32: // i4 -> i8 cg.Builder.EmitOpCode(ILOpCode.Conv_i8); goto case SpecialType.System_Int64; case SpecialType.System_Int64: ytype = cg.Emit(right); // if (ytype.SpecialType == SpecialType.System_Int32) { cg.Builder.EmitOpCode(ILOpCode.Conv_i8); // i4 -> i8 ytype = cg.CoreTypes.Long; } // if (ytype.SpecialType == SpecialType.System_Int64) { // i8 == i8 cg.Builder.EmitOpCode(ILOpCode.Ceq); return cg.CoreTypes.Boolean; } else if (ytype.SpecialType == SpecialType.System_Double) { // i8 == r8 return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Ceq_long_double) .Expect(SpecialType.System_Boolean); } else if (ytype.SpecialType == SpecialType.System_Boolean) { // i8 == bool return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Ceq_long_bool) .Expect(SpecialType.System_Boolean); } else if (ytype.SpecialType == SpecialType.System_String) { // i8 == string return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Ceq_long_string) .Expect(SpecialType.System_Boolean); } // value ytype = cg.EmitConvertToPhpValue(ytype, 0); // compare(i8, value) == 0 cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Compare_long_value); cg.EmitLogicNegation(); return cg.CoreTypes.Boolean; case SpecialType.System_Double: ytype = cg.EmitConvertNumberToDouble(right); // bool|long|int -> double if (ytype.SpecialType == SpecialType.System_Double) { // r8 == r8 cg.Builder.EmitOpCode(ILOpCode.Ceq); return cg.CoreTypes.Boolean; } else if (ytype.SpecialType == SpecialType.System_String) { // r8 == string return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Ceq_double_string) .Expect(SpecialType.System_Boolean); } // value ytype = cg.EmitConvertToPhpValue(ytype, 0); // compare(double, value) == 0 cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Compare_double_value); cg.EmitLogicNegation(); return cg.CoreTypes.Boolean; case SpecialType.System_String: ytype = cg.Emit(right); if (ytype.SpecialType == SpecialType.System_Int32) { // i4 -> i8 cg.Builder.EmitOpCode(ILOpCode.Conv_i8); ytype = cg.CoreTypes.Long; } if (ytype.SpecialType == SpecialType.System_Int64) { // string == i8 return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Ceq_string_long) .Expect(SpecialType.System_Boolean); } else if (ytype.SpecialType == SpecialType.System_Boolean) { // string == bool return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Ceq_string_bool) .Expect(SpecialType.System_Boolean); } else if (ytype.SpecialType == SpecialType.System_Double) { // string == r8 return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Ceq_string_double) .Expect(SpecialType.System_Boolean); } else if (ytype.SpecialType == SpecialType.System_String) { // compare(string, string) == 0 cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Compare_string_string).Expect(SpecialType.System_Int32); cg.EmitLogicNegation(); return cg.CoreTypes.Boolean; } // value ytype = cg.EmitConvertToPhpValue(ytype, 0); // compare(string, value) == 0 cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Compare_string_value); cg.EmitLogicNegation(); return cg.CoreTypes.Boolean; //case SpecialType.System_Object: // goto default; default: if (xtype == cg.CoreTypes.PhpNumber) { ytype = cg.EmitConvertIntToLong(cg.Emit(right)); if (ytype.SpecialType == SpecialType.System_Int64) { // number == i8 return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Eq_number_long) .Expect(SpecialType.System_Boolean); } else if (ytype.SpecialType == SpecialType.System_Double) { // number == r8 return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Eq_number_double) .Expect(SpecialType.System_Boolean); } else if (ytype == cg.CoreTypes.PhpNumber) { // number == number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Eq_number_number) .Expect(SpecialType.System_Boolean); } else { ytype = cg.EmitConvertToPhpValue(ytype, 0); // number == value return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Eq_number_PhpValue) .Expect(SpecialType.System_Boolean); } } else { // TODO: xtype: PhpArray, ... xtype = cg.EmitConvertToPhpValue(xtype, 0); // TODO: overloads for type of <right> ytype = cg.EmitConvertToPhpValue(cg.Emit(right), right.TypeRefMask); // value == value return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpValue.Eq_PhpValue_PhpValue) .Expect(SpecialType.System_Boolean); } } }
/// <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, bool @checked = false) { // {from} is loaded on stack // if (conversion.Exists == false) { throw cg.NotImplementedException($"Conversion from '{from}' to '{to}' does not exist."); } if (conversion.IsIdentity) { if (to.SpecialType == SpecialType.System_Void) { // POP cg.EmitPop(from); } // nop } else if (conversion.IsNumeric) { EmitNumericConversion(cg, from, to, @checked: @checked); } else if (conversion.IsReference) { // TODO: ensure from/to is 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) { cg.EmitStructAddr(from); } } else { if (ps[0].RefKind != RefKind.None) { throw new InvalidOperationException(); } EmitImplicitConversion(cg, from, ps[0].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(); } }
void IBoundReference.EmitStore(CodeGenerator cg, TypeSymbol valueType) { var rtype = cg.CoreTypes.IPhpArray; cg.EmitConvert(valueType, 0, rtype); var tmp = cg.GetTemporaryLocal(rtype); cg.Builder.EmitLocalStore(tmp); // NOTE: since PHP7, variables are assigned from left to right var vars = this.Variables; for (int i = 0; i < vars.Length; i++) { var target = vars[i]; if (target == null) continue; var boundtarget = target.BindPlace(cg); boundtarget.EmitStorePrepare(cg); // LOAD IPhpArray.GetItemValue(IntStringKey{i}) cg.Builder.EmitLocalLoad(tmp); cg.EmitIntStringKey(i); var itemtype = cg.EmitCall(ILOpCode.Callvirt, cg.CoreMethods.IPhpArray.GetItemValue_IntStringKey); // STORE vars[i] boundtarget.EmitStore(cg, itemtype); } // cg.ReturnTemporaryLocal(tmp); }
/// <summary> /// Emits <c>+</c> operator suitable for actual operands. /// </summary> internal static TypeSymbol EmitAdd(CodeGenerator cg, TypeSymbol xtype, BoundExpression Right, TypeSymbol resultTypeOpt = null) { var il = cg.Builder; xtype = cg.EmitConvertIntToLong(xtype); // int|bool -> long // if (xtype == cg.CoreTypes.PhpNumber) { var ytype = cg.EmitConvertIntToLong(cg.Emit(Right)); // int|bool -> long if (ytype == cg.CoreTypes.PhpNumber) { // number + number : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Add_number_number) .Expect(cg.CoreTypes.PhpNumber); } else if (ytype.SpecialType == SpecialType.System_Double) { // number + r8 : r8 return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Add_number_double) .Expect(SpecialType.System_Double); } else if (ytype.SpecialType == SpecialType.System_Int64) { // number + long : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Add_number_long) .Expect(cg.CoreTypes.PhpNumber); } else if (ytype == cg.CoreTypes.PhpValue) { // number + value : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Add_number_value) .Expect(cg.CoreTypes.PhpNumber); } // throw new NotImplementedException($"Add(number, {ytype.Name})"); } else if (xtype.SpecialType == SpecialType.System_Double) { var ytype = cg.EmitConvertNumberToDouble(Right); // bool|int|long|number -> double if (ytype.SpecialType == SpecialType.System_Double) { // r8 + r8 : r8 il.EmitOpCode(ILOpCode.Add); return cg.CoreTypes.Double; } else if (ytype == cg.CoreTypes.PhpValue) { // r8 + value : r8 return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Add_double_value) .Expect(SpecialType.System_Double); } // throw new NotImplementedException($"Add(double, {ytype.Name})"); } else if (xtype.SpecialType == SpecialType.System_Int64) { var ytype = cg.EmitConvertIntToLong(cg.Emit(Right)); // int|bool -> long if (ytype.SpecialType == SpecialType.System_Int64) { if (resultTypeOpt != null) { if (resultTypeOpt.SpecialType == SpecialType.System_Int64) { // (long)(i8 + i8 : number) il.EmitOpCode(ILOpCode.Add); return cg.CoreTypes.Long; } } // i8 + i8 : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Add_long_long) .Expect(cg.CoreTypes.PhpNumber); } else if (ytype.SpecialType == SpecialType.System_Double) { // i8 + r8 : r8 return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Add_long_double) .Expect(SpecialType.System_Double); } else if (ytype == cg.CoreTypes.PhpNumber) { // i8 + number : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Add_long_number) .Expect(cg.CoreTypes.PhpNumber); } else if (ytype == cg.CoreTypes.PhpValue) { // i8 + value : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Add_long_value) .Expect(cg.CoreTypes.PhpNumber); } // throw new NotImplementedException($"Add(int64, {ytype.Name})"); } else if (xtype == cg.CoreTypes.PhpValue) { var ytype = cg.EmitConvertIntToLong(cg.Emit(Right)); // int|bool -> long // PhpString -> String if (ytype == cg.CoreTypes.PhpString) { cg.EmitConvertToString(ytype, 0); // continue ... } if (ytype.SpecialType == SpecialType.System_Int64) { // value + i8 : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Add_value_long) .Expect(cg.CoreTypes.PhpNumber); } else if (ytype.SpecialType == SpecialType.System_Double) { // value + r8 : r8 return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Add_value_double) .Expect(SpecialType.System_Double); } else if (ytype == cg.CoreTypes.String) { // value + string : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Add_value_string) .Expect(cg.CoreTypes.PhpNumber); } else if (ytype == cg.CoreTypes.PhpNumber) { // value + number : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Add_value_number) .Expect(cg.CoreTypes.PhpNumber); } else if (ytype == cg.CoreTypes.PhpValue) { // value + value : value return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Add_value_value) .Expect(cg.CoreTypes.PhpValue); } // throw new NotImplementedException($"Add(PhpValue, {ytype.Name})"); } // throw new NotImplementedException($"Add({xtype.Name}, ...)"); }
internal virtual TypeSymbol EmitDirectCall(CodeGenerator cg, ILOpCode opcode, MethodSymbol method) { // TODO: emit check the routine is declared // <ctx>.AssertFunctionDeclared var arguments = _arguments.Select(a => a.Value).ToImmutableArray(); return cg.EmitCall(opcode, method, this.Instance, arguments); }
internal override TypeSymbol Emit(CodeGenerator cg) { // new PhpArray(count) cg.Builder.EmitIntConstant(_items.Length); var result = cg.EmitCall(ILOpCode.Newobj, cg.CoreMethods.Ctors.PhpArray_int) .Expect(cg.CoreTypes.PhpArray); foreach (var x in _items) { // <PhpArray> cg.Builder.EmitOpCode(ILOpCode.Dup); // key if (x.Key != null) { cg.EmitIntStringKey(x.Key); } // value | alias Debug.Assert(x.Value != null); var byref = x.Value.Access.IsReadRef; var valuetype = byref ? cg.CoreTypes.PhpAlias : cg.CoreTypes.PhpValue; cg.EmitConvert(x.Value, valuetype); if (x.Key != null) { if (byref) // .SetItemAlias( key, PhpAlias ) cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpArray.SetItemAlias_IntStringKey_PhpAlias); else // .SetItemValue( key, PhpValue ) cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpArray.SetItemValue_IntStringKey_PhpValue); } else { if (byref) // PhpValue.Create( PhpAlias ) cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpValue.Create_PhpAlias); // .AddValue( PhpValue ) cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpArray.AddValue_PhpValue); } } // return result; }
internal virtual TypeSymbol EmitCallsiteCall(CodeGenerator cg) { // callsite var nameOpt = this.CallsiteName; var callsite = cg.Factory.StartCallSite("call_" + nameOpt); var callsiteargs = new List<TypeSymbol>(_arguments.Length); var return_type = this.Access.IsRead ? this.Access.IsReadRef ? cg.CoreTypes.PhpAlias.Symbol : (this.Access.TargetType ?? cg.CoreTypes.PhpValue.Symbol) : cg.CoreTypes.Void.Symbol; // callsite var fldPlace = callsite.Place; // LOAD callsite.Target callsite.EmitLoadTarget(cg.Builder); // LOAD callsite arguments // (callsite, [target], ctx, [name], ...) fldPlace.EmitLoad(cg.Builder); if (Instance != null) { callsiteargs.Add(cg.Emit(Instance)); // instance } else if (TypeNameExpr != null) { cg.EmitConvert(TypeNameExpr, cg.CoreTypes.String); callsiteargs.Add(cg.CoreTypes.String); // type } callsiteargs.Add(cg.EmitLoadContext()); // ctx if (RoutineNameExpr != null) { callsiteargs.Add(cg.Emit(RoutineNameExpr)); // name } foreach (var a in _arguments) { callsiteargs.Add(cg.Emit(a.Value)); } // Target() var functype = cg.Factory.GetCallSiteDelegateType( null, RefKind.None, callsiteargs.AsImmutable(), default(ImmutableArray<RefKind>), null, return_type); cg.EmitCall(ILOpCode.Callvirt, functype.DelegateInvokeMethod); // Create CallSite ... callsite.Construct(functype, cctor_cg => BuildCallsiteCreate(cctor_cg, return_type)); // return return_type; }
TypeSymbol IBoundReference.EmitLoad(CodeGenerator cg) { // Template: array[index] var isphparr = (this.Array.ResultType == cg.CoreTypes.PhpArray); // whether the target is instance of PhpArray, otherwise it is an IPhpArray and we have to use .callvirt if (Access.EnsureObject) { // <array>.EnsureItemObject(<key>) return isphparr ? cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpArray.EnsureItemObject_IntStringKey) : cg.EmitCall(ILOpCode.Callvirt, cg.CoreMethods.IPhpArray.EnsureItemObject_IntStringKey); } else if (Access.EnsureArray) { return isphparr ? cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpArray.EnsureItemArray_IntStringKey) : cg.EmitCall(ILOpCode.Callvirt, cg.CoreMethods.IPhpArray.EnsureItemArray_IntStringKey); } else if (Access.IsReadRef) { return isphparr ? cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpArray.EnsureItemAlias_IntStringKey) : cg.EmitCall(ILOpCode.Callvirt, cg.CoreMethods.IPhpArray.EnsureItemAlias_IntStringKey); } else { Debug.Assert(Access.IsRead); return isphparr ? cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpArray.GetItemValue_IntStringKey) : cg.EmitCall(ILOpCode.Callvirt, cg.CoreMethods.IPhpArray.GetItemValue_IntStringKey); } }
internal override TypeSymbol EmitCallsiteCall(CodeGenerator cg) { if (_name.IsDirect) { return base.EmitCallsiteCall(cg); } else { Debug.Assert(_name.NameExpression != null); // faster to emit PhpCallback.Invoke // NameExpression.AsCallback().Invoke(Context, PhpValue[]) cg.EmitConvert(_name.NameExpression, cg.CoreTypes.IPhpCallable); // (IPhpCallable)Name cg.EmitLoadContext(); // Context cg.Emit_NewArray(cg.CoreTypes.PhpValue, _arguments.Select(a => a.Value).ToArray()); // PhpValue[] return cg.EmitCall(ILOpCode.Callvirt, cg.CoreTypes.IPhpCallable.Symbol.LookupMember<MethodSymbol>("Invoke")); } }
internal override TypeSymbol Emit(CodeGenerator cg) { Debug.Assert(Access.IsRead || Access.IsNone); var type = cg.Emit(Operand); // if (Access.IsNone) { cg.EmitPop(type); return cg.CoreTypes.Void; } // dereference if (type == cg.CoreTypes.PhpAlias) { // <alias>.Value.AsObject() cg.Emit_PhpAlias_GetValueRef(); type = cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpValue.AsObject); } // PhpValue -> object if (type == cg.CoreTypes.PhpValue) { // Template: Operators.AsObject(value) is T type = cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.AsObject_PhpValue); } // if (AsType.ResolvedType != null) { if (type.IsReferenceType && type != cg.CoreTypes.PhpArray && type != cg.CoreTypes.PhpString) { // Template: value is T : object cg.Builder.EmitOpCode(ILOpCode.Isinst); cg.EmitSymbolToken(AsType.ResolvedType, null); // object != null cg.Builder.EmitNullConstant(); // .ldnull cg.Builder.EmitOpCode(ILOpCode.Cgt_un); // .cgt.un } else { cg.EmitPop(type); // Operand is never an object instance // FALSE cg.Builder.EmitBoolConstant(false); } // return cg.CoreTypes.Boolean; } else { AsType.EmitLoadTypeInfo(cg, false); // Template: Operators.IsInstanceOf(value, type); return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.IsInstanceOf_Object_PhpTypeInfo); } throw new NotImplementedException(); }
internal override void BuildCallsiteCreate(CodeGenerator cg, TypeSymbol returntype) { cg.Builder.EmitStringConstant(CallsiteName); cg.Builder.EmitStringConstant(_nameOpt.HasValue ? _nameOpt.Value.ToString() : null); cg.EmitLoadToken(returntype, null); cg.Builder.EmitIntConstant(0); cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Dynamic.CallBinderFactory_Function); }
internal override TypeSymbol Emit(CodeGenerator cg) { Debug.Assert(!Access.IsWrite); if (this.Access.IsNone) { return cg.CoreTypes.Void; } if (this.ConstantValue.HasValue) { return cg.EmitLoadConstant(this.ConstantValue.Value, this.Access.TargetType); } if (_boundExpressionOpt != null) { _boundExpressionOpt.EmitLoadPrepare(cg); return _boundExpressionOpt.EmitLoad(cg); } var idxfield = cg.Module.SynthesizedManager .GetOrCreateSynthesizedField(cg.Module.ScriptType, cg.CoreTypes.Int32, $"c<{this.Name}>idx", Accessibility.Internal, true, false); // <ctx>.GetConstant(<name>, ref <Index of constant>) cg.EmitLoadContext(); cg.Builder.EmitStringConstant(this.Name); cg.EmitFieldAddress(idxfield); return cg.EmitCall(ILOpCode.Callvirt, cg.CoreMethods.Context.GetConstant_string_int32) .Expect(cg.CoreTypes.PhpValue); }
internal override void BuildCallsiteCreate(CodeGenerator cg, TypeSymbol returntype) { cg.EmitLoadToken(_typeRef.ResolvedType, null); // type cg.Builder.EmitStringConstant(CallsiteName); // name cg.EmitLoadToken(cg.CallerType, null); // class context cg.EmitLoadToken(returntype, null); // return type cg.Builder.EmitIntConstant(0); // generic params count cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Dynamic.CallBinderFactory_StaticFunction); }
internal override TypeSymbol Emit(CodeGenerator cg) { var end_label = new object(); var vars = this.VarReferences; for (int i = 0; i < vars.Length; i++) { if (i > 0) { cg.Builder.EmitOpCode(ILOpCode.Pop); } var t = cg.Emit(vars[i]); // t.IsSet if (t == cg.CoreTypes.PhpValue) { // IsSet(value) cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.IsSet_PhpValue); } else if (t.IsReferenceType) { // object != null cg.Builder.EmitNullConstant(); // .ldnull cg.Builder.EmitOpCode(ILOpCode.Cgt_un); // .cgt.un } else { // value type => true cg.EmitPop(t); cg.Builder.EmitBoolConstant(true); } if (i + 1 < vars.Length) { // if (result == false) goto end_label; cg.Builder.EmitOpCode(ILOpCode.Dup); cg.Builder.EmitBranch(ILOpCode.Brfalse, end_label); } } // cg.Builder.MarkLabel(end_label); // return cg.CoreTypes.Boolean; }
internal override TypeSymbol Emit(CodeGenerator cg) { if (TargetMethod != null) { return EmitDirectCall(cg, ILOpCode.Newobj, TargetMethod); } else { if (_typeref.ResolvedType != null) { // context.Create<T>(params) var create_t = cg.CoreTypes.Context.Symbol.GetMembers("Create") .OfType<MethodSymbol>() .Where(s => s.Arity == 1 && s.ParameterCount == 1 && s.Parameters[0].IsParams) .Single() .Construct(_typeref.ResolvedType); cg.EmitLoadContext(); // Context cg.Emit_NewArray(cg.CoreTypes.PhpValue, _arguments.Select(a => a.Value).ToArray()); // PhpValue[] return cg.EmitCall(ILOpCode.Call, create_t); } else { // ctx.Create(classname, params) var create = cg.CoreTypes.Context.Symbol.GetMembers("Create") .OfType<MethodSymbol>() .Where(s => s.Arity == 0 && s.ParameterCount == 2 && s.Parameters[0].Type.PrimitiveTypeCode == Microsoft.Cci.PrimitiveTypeCode.String && s.Parameters[1].IsParams && ((ArrayTypeSymbol)s.Parameters[1].Type).ElementType == cg.CoreTypes.PhpValue) .Single(); cg.EmitLoadContext(); // Context _typeref.EmitClassName(cg); // String cg.Emit_NewArray(cg.CoreTypes.PhpValue, _arguments.Select(a => a.Value).ToArray()); // PhpValue[] return cg.EmitCall(ILOpCode.Call, create); } } }
internal static TypeSymbol EmitBitOr(CodeGenerator cg, TypeSymbol xtype, BoundExpression right) { switch (xtype.SpecialType) { case SpecialType.System_Void: case SpecialType.System_Int32: case SpecialType.System_Boolean: case SpecialType.System_Double: cg.EmitConvert(xtype, 0, cg.CoreTypes.Long); goto case SpecialType.System_Int64; case SpecialType.System_Int64: cg.EmitConvert(right, cg.CoreTypes.Long); cg.Builder.EmitOpCode(ILOpCode.Or); return cg.CoreTypes.Long; case SpecialType.System_String: throw new NotImplementedException(); // string | string or string | long default: if (right.ResultType != null && right.ResultType.SpecialType != SpecialType.System_String) { // value | !string -> long | long -> long cg.EmitConvert(xtype, 0, cg.CoreTypes.Long); goto case SpecialType.System_Int64; } cg.EmitConvert(xtype, 0, cg.CoreTypes.PhpValue); cg.EmitConvert(right, cg.CoreTypes.PhpValue); return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.BitwiseOr_PhpValue_PhpValue) .Expect(cg.CoreTypes.PhpValue); } }
internal override TypeSymbol Emit(CodeGenerator cg) { // new PhpString() cg.EmitCall(ILOpCode.Newobj, cg.CoreMethods.Ctors.PhpString); // TODO: overload for 2, 3, 4 parameters directly // <PhpString>.Append(<expr>) foreach (var x in this.ArgumentsInSourceOrder) { var expr = x.Value; if (IsEmpty(expr)) continue; // cg.Builder.EmitOpCode(ILOpCode.Dup); // PhpString var t = cg.Emit(expr); if (t == cg.CoreTypes.PhpString) { cg.EmitCall(ILOpCode.Callvirt, cg.CoreMethods.PhpString.Append_PhpString); } else { // TODO: PhpValue -> PhpString (instead of String) cg.EmitConvert(t, 0, cg.CoreTypes.String); cg.EmitCall(ILOpCode.Callvirt, cg.CoreMethods.PhpString.Append_String); } // cg.Builder.EmitOpCode(ILOpCode.Nop); } // return cg.CoreTypes.PhpString; }
internal static TypeSymbol EmitStrictEquality(CodeGenerator cg, TypeSymbol xtype, BoundExpression right) { TypeSymbol ytype; switch (xtype.SpecialType) { case SpecialType.System_Boolean: ytype = cg.Emit(right); if (ytype.SpecialType == SpecialType.System_Boolean) { // bool == bool cg.Builder.EmitOpCode(ILOpCode.Ceq); return cg.CoreTypes.Boolean; } else if ( ytype.SpecialType == SpecialType.System_Double || ytype.SpecialType == SpecialType.System_Int32 || ytype.SpecialType == SpecialType.System_Int64 || ytype.SpecialType == SpecialType.System_String || ytype.IsOfType(cg.CoreTypes.IPhpArray) || ytype == cg.CoreTypes.PhpString || ytype == cg.CoreTypes.Object) { // bool == something else => false cg.EmitPop(ytype); cg.EmitPop(xtype); cg.Builder.EmitBoolConstant(false); return cg.CoreTypes.Boolean; } else { // bool == PhpValue cg.EmitConvertToPhpValue(ytype, 0); return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.StrictCeq_bool_PhpValue) .Expect(SpecialType.System_Boolean); } case SpecialType.System_Int32: cg.Builder.EmitOpCode(ILOpCode.Conv_i8); // i4 -> i8 goto case SpecialType.System_Int64; case SpecialType.System_Int64: ytype = cg.Emit(right); if (ytype.SpecialType == SpecialType.System_Int32) { cg.Builder.EmitOpCode(ILOpCode.Conv_i8); // i4 -> i8 ytype = cg.CoreTypes.Long; } if (ytype.SpecialType == SpecialType.System_Int64) { // i8 == i8 cg.Builder.EmitOpCode(ILOpCode.Ceq); return cg.CoreTypes.Boolean; } else if ( ytype.SpecialType == SpecialType.System_Boolean || ytype.SpecialType == SpecialType.System_String || ytype.SpecialType == SpecialType.System_Double || ytype.IsOfType(cg.CoreTypes.IPhpArray) || ytype == cg.CoreTypes.Object || ytype == cg.CoreTypes.PhpString) { // i8 == something else => false cg.EmitPop(ytype); cg.EmitPop(xtype); cg.Builder.EmitBoolConstant(false); return cg.CoreTypes.Boolean; } else { // i8 == PhpValue cg.EmitConvertToPhpValue(ytype, 0); return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.StrictCeq_long_PhpValue) .Expect(SpecialType.System_Boolean); } case SpecialType.System_Double: ytype = cg.Emit(right); if (ytype.SpecialType == SpecialType.System_Double) { // r8 == r8 cg.Builder.EmitOpCode(ILOpCode.Ceq); return cg.CoreTypes.Boolean; } else if ( ytype.SpecialType == SpecialType.System_Boolean || ytype.SpecialType == SpecialType.System_String || ytype.SpecialType == SpecialType.System_Int64 || ytype.SpecialType == SpecialType.System_Int32 || ytype.IsOfType(cg.CoreTypes.IPhpArray) || ytype == cg.CoreTypes.Object || ytype == cg.CoreTypes.PhpString) { // r8 == something else => false cg.EmitPop(ytype); cg.EmitPop(xtype); cg.Builder.EmitBoolConstant(false); return cg.CoreTypes.Boolean; } else { // r8 == PhpValue cg.EmitConvertToPhpValue(ytype, 0); return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.StrictCeq_double_PhpValue) .Expect(SpecialType.System_Boolean); } default: // TODO: PhpArray, Object === ... xtype = cg.EmitConvertToPhpValue(xtype, 0); ytype = cg.Emit(right); if (ytype.SpecialType == SpecialType.System_Boolean) { // PhpValue == bool return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.StrictCeq_PhpValue_bool) .Expect(SpecialType.System_Boolean); } else { ytype = cg.EmitConvertToPhpValue(ytype, 0); // PhpValue == PhpValue return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.StrictCeq_PhpValue_PhpValue) .Expect(SpecialType.System_Boolean); } } }
internal override TypeSymbol Emit(CodeGenerator cg) { TypeSymbol result; var isvoid = this.Access.IsNone; Debug.Assert(_arguments.Length == 1); Debug.Assert(_arguments[0].Value.Access.IsRead); Debug.Assert(Access.IsRead || Access.IsNone); var method = this.Target; if (method != null) // => IsResolved { // emit condition for include_once/require_once if (IsOnceSemantic) { var tscript = method.ContainingType; result = isvoid ? cg.CoreTypes.Void.Symbol : cg.DeclaringCompilation.GetTypeFromTypeRef(cg.Routine.TypeRefContext, this.TypeRefMask); // Template: (<ctx>.CheckIncludeOnce<TScript>()) ? <Main>() : TRUE // Template<isvoid>: if (<ctx>.CheckIncludeOnce<TScript>()) <Main>() var falseLabel = new object(); var endLabel = new object(); cg.EmitLoadContext(); cg.EmitCall(ILOpCode.Callvirt, cg.CoreMethods.Context.CheckIncludeOnce_TScript.Symbol.Construct(tscript)); cg.Builder.EmitBranch(ILOpCode.Brfalse, falseLabel); // ? (PhpValue)<Main>(...) cg.EmitCallMain(method); if (isvoid) { cg.EmitPop(method.ReturnType); } else { cg.EmitConvert(method.ReturnType, 0, result); } cg.Builder.EmitBranch(ILOpCode.Br, endLabel); if (!isvoid) { cg.Builder.AdjustStack(-1); // workarounds assert in ILBuilder.MarkLabel, we're doing something wrong with ILBuilder } // : PhpValue.Create(true) cg.Builder.MarkLabel(falseLabel); if (!isvoid) { cg.Builder.EmitBoolConstant(true); cg.EmitConvert(cg.CoreTypes.Boolean, 0, result); } // cg.Builder.MarkLabel(endLabel); } else { // <Main> result = cg.EmitCallMain(method); } } else { Debug.Assert(cg.LocalsPlaceOpt != null); // Template: <ctx>.Include(dir, path, locals, @this, bool once = false, bool throwOnError = false) cg.EmitLoadContext(); cg.Builder.EmitStringConstant(cg.Routine.ContainingFile.DirectoryRelativePath); cg.EmitConvert(_arguments[0].Value, cg.CoreTypes.String); cg.LocalsPlaceOpt.EmitLoad(cg.Builder); // scope of local variables, corresponds to $GLOBALS in global scope. cg.EmitThisOrNull(); // $this cg.Builder.EmitBoolConstant(IsOnceSemantic); cg.Builder.EmitBoolConstant(IsRequireSemantic); return cg.EmitCall(ILOpCode.Callvirt, cg.CoreMethods.Context.Include_string_string_PhpArray_object_bool_bool); } // return result; }