internal override void Generate(CodeGenerator cg) { Debug.Assert(this.Enumeree != null); // get the enumerator, // bind actual MoveNext() and CurrentValue and CurrentKey // Template: using( // a) enumerator = enumeree.GetEnumerator() // b) enumerator = Operators.GetEnumerator(enumeree) // ) ... cg.EmitSequencePoint(this.Enumeree.PhpSyntax); var enumereeType = cg.Emit(this.Enumeree); Debug.Assert(enumereeType.SpecialType != SpecialType.System_Void); var getEnumeratorMethod = enumereeType.LookupMember<MethodSymbol>(WellKnownMemberNames.GetEnumeratorMethodName); TypeSymbol enumeratorType; if (enumereeType.IsOfType(cg.CoreTypes.PhpArray)) { cg.Builder.EmitBoolConstant(_aliasedValues); // PhpArray.GetForeachtEnumerator(bool) enumeratorType = cg.EmitCall(ILOpCode.Callvirt, cg.CoreMethods.PhpArray.GetForeachEnumerator_Boolean); // TODO: IPhpArray } // TODO: IPhpEnumerable // TODO: IPhpArray // TODO: Iterator else if (getEnumeratorMethod != null && getEnumeratorMethod.ParameterCount == 0 && enumereeType.IsReferenceType) { // enumeree.GetEnumerator() enumeratorType = cg.EmitCall(getEnumeratorMethod.IsVirtual ? ILOpCode.Callvirt : ILOpCode.Call, getEnumeratorMethod); } else { cg.EmitConvertToPhpValue(enumereeType, 0); cg.Builder.EmitBoolConstant(_aliasedValues); cg.EmitCallerRuntimeTypeHandle(); // Operators.GetForeachEnumerator(PhpValue, bool, RuntimeTypeHandle) enumeratorType = cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.GetForeachEnumerator_PhpValue_Bool_RuntimeTypeHandle); } // _current = enumeratorType.LookupMember<PropertySymbol>(WellKnownMemberNames.CurrentPropertyName); // TODO: Err if no Current _currentValue = enumeratorType.LookupMember<PropertySymbol>(_aliasedValues ? "CurrentValueAliased" : "CurrentValue"); _currentKey = enumeratorType.LookupMember<PropertySymbol>("CurrentKey"); _disposeMethod = enumeratorType.LookupMember<MethodSymbol>("Dispose", m => m.ParameterCount == 0 && !m.IsStatic); // _enumeratorLoc = cg.GetTemporaryLocal(enumeratorType); cg.Builder.EmitLocalStore(_enumeratorLoc); // bind methods _moveNextMethod = enumeratorType.LookupMember<MethodSymbol>(WellKnownMemberNames.MoveNextMethodName); // TODO: Err if there is no MoveNext() Debug.Assert(_moveNextMethod.ReturnType.SpecialType == SpecialType.System_Boolean); Debug.Assert(_moveNextMethod.IsStatic == false); if (_disposeMethod != null) { /* Template: try { body } finally { enumerator.Dispose } */ // try { cg.Builder.AssertStackEmpty(); cg.Builder.OpenLocalScope(ScopeType.TryCatchFinally); cg.Builder.OpenLocalScope(ScopeType.Try); // EmitBody(cg); // } cg.Builder.CloseLocalScope(); // /Try // finally { cg.Builder.OpenLocalScope(ScopeType.Finally); // enumerator.Dispose() & cleanup EmitDisposeAndClean(cg); // } cg.Builder.CloseLocalScope(); // /Finally cg.Builder.CloseLocalScope(); // /TryCatchFinally } else { EmitBody(cg); EmitDisposeAndClean(cg); } }
internal static TypeSymbol EmitBitOr(CodeGenerator cg, BoundExpression left, BoundExpression right) { // most common cases: if (cg.IsLongOnly(left.TypeRefMask) || cg.IsLongOnly(right.TypeRefMask)) { // i64 | i64 : i64 cg.EmitConvert(left, cg.CoreTypes.Long); cg.EmitConvert(right, cg.CoreTypes.Long); cg.Builder.EmitOpCode(ILOpCode.Or); return cg.CoreTypes.Long; } // return EmitBitOr(cg, cg.Emit(left), right); }
internal static TypeSymbol EmitStrictEquality(CodeGenerator cg, BoundExpression left, BoundExpression right) => EmitStrictEquality(cg, cg.Emit(left), right);
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(); }
/// <summary> /// Emits subtraction operator. /// </summary> internal static TypeSymbol EmitSub(CodeGenerator cg, BoundExpression left, BoundExpression right, TypeSymbol resultTypeOpt = null) { return EmitSub(cg, cg.Emit(left), right, resultTypeOpt); }
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; }
internal override TypeSymbol Emit(CodeGenerator cg) { var target_place = this.Target.BindPlace(cg); Debug.Assert(target_place != null); Debug.Assert(target_place.TypeOpt == null || target_place.TypeOpt.SpecialType != SpecialType.System_Void); // T tmp; // in case access is Read var t_value = target_place.TypeOpt; if (t_value == cg.CoreTypes.PhpAlias || t_value == cg.CoreTypes.PhpValue) t_value = null; // no inplace conversion LocalDefinition tmp = null; // <target> = <value> target_place.EmitStorePrepare(cg); // TODO: load value & dereference eventually if (t_value != null) cg.EmitConvert(this.Value, t_value); // TODO: do not convert here yet else t_value = cg.Emit(this.Value); switch (this.Access.Flags) { case AccessMask.Read: tmp = cg.GetTemporaryLocal(t_value, false); cg.Builder.EmitOpCode(ILOpCode.Dup); cg.Builder.EmitLocalStore(tmp); break; case AccessMask.None: break; default: throw ExceptionUtilities.UnexpectedValue(this.Access); } target_place.EmitStore(cg, t_value); // switch (this.Access.Flags) { case AccessMask.None: t_value = cg.CoreTypes.Void; break; case AccessMask.Read: cg.Builder.EmitLocalLoad(tmp); break; default: throw ExceptionUtilities.UnexpectedValue(this.Access); } if (tmp != null) { cg.ReturnTemporaryLocal(tmp); } // return t_value; }
/// <summary> /// Emits <c>/</c> operator. /// </summary> TypeSymbol EmitDivision(CodeGenerator cg) => EmitDiv(cg, cg.Emit(Left), Right);
internal static TypeSymbol EmitDiv(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_Double: ytype = cg.EmitConvertNumberToDouble(right); // bool|int|long|number -> double if (ytype.SpecialType == SpecialType.System_Double) { il.EmitOpCode(ILOpCode.Div); return xtype; // r8 } // double / value : double cg.EmitConvertToPhpValue(ytype, 0); return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Div_double_PhpValue); case SpecialType.System_Int64: ytype = cg.EmitConvertIntToLong(cg.Emit(right)); // bool|int -> long if (ytype == cg.CoreTypes.PhpNumber) { // long / number : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Division_long_number) .Expect(cg.CoreTypes.PhpNumber); } // long / value : number cg.EmitConvertToPhpValue(ytype, 0); return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Div_long_PhpValue); default: if (xtype == cg.CoreTypes.PhpNumber) { ytype = cg.EmitConvertIntToLong(cg.Emit(right)); // bool|int -> long if (ytype == cg.CoreTypes.PhpNumber) { // nmumber / number : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Division_number_number) .Expect(cg.CoreTypes.PhpNumber); } } // x -> PhpValue xtype = cg.EmitConvertToPhpValue(xtype, 0); cg.EmitConvert(right, cg.CoreTypes.PhpValue); ytype = cg.CoreTypes.PhpValue; // value / value : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Div_PhpValue_PhpValue); } }
/// <summary> /// Emits <c>*</c> operation. /// </summary> TypeSymbol EmitMultiply(CodeGenerator cg) => EmitMul(cg, cg.Emit(Left), Right);
internal static TypeSymbol EmitMul(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_Double: ytype = cg.EmitConvertNumberToDouble(right); // bool|int|long|number -> double if (ytype.SpecialType == SpecialType.System_Double) { // r8 * r8 : r8 il.EmitOpCode(ILOpCode.Mul); return xtype; // r8 } else if (ytype == cg.CoreTypes.PhpValue) { // r8 * value : r8 return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Mul_double_value) .Expect(SpecialType.System_Double); } // throw new NotImplementedException($"Mul(double, {ytype.Name})"); 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.Mul_long_long) .Expect(cg.CoreTypes.PhpNumber); } else if (ytype.SpecialType == SpecialType.System_Double) { // i8 * r8 : r8 return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Mul_long_double) .Expect(SpecialType.System_Double); } else if (ytype == cg.CoreTypes.PhpNumber) { // i8 * number : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Mul_long_number) .Expect(cg.CoreTypes.PhpNumber); } else if (ytype == cg.CoreTypes.PhpValue) { // i8 * value : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Mul_long_value) .Expect(cg.CoreTypes.PhpNumber); } // throw new NotImplementedException($"Mul(int64, {ytype.Name})"); default: if (xtype == cg.CoreTypes.PhpNumber) { ytype = cg.EmitConvertIntToLong(cg.Emit(right)); if (ytype.SpecialType == SpecialType.System_Int64) { return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Mul_number_long) .Expect(cg.CoreTypes.PhpNumber); } else if (ytype.SpecialType == SpecialType.System_Double) { return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Mul_number_double) .Expect(cg.CoreTypes.Double); } else if (ytype == cg.CoreTypes.PhpNumber) { // number * number : number cg.EmitConvertToPhpNumber(ytype, right.TypeRefMask); return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Mul_number_number) .Expect(cg.CoreTypes.PhpNumber); } else if (ytype == cg.CoreTypes.PhpValue) { // number * value : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Mul_number_value) .Expect(cg.CoreTypes.PhpNumber); } else { // TODO: unconvertible // number * number : number cg.EmitConvertToPhpNumber(ytype, right.TypeRefMask); return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Mul_number_number) .Expect(cg.CoreTypes.PhpNumber); } // throw new NotImplementedException($"Mul(PhpNumber, {ytype.Name})"); } else if (xtype == cg.CoreTypes.PhpValue) { ytype = cg.EmitConvertIntToLong(cg.Emit(right)); // bool|int -> long if (ytype == cg.CoreTypes.PhpValue) { // value * value : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Mul_value_value) .Expect(cg.CoreTypes.PhpNumber); } else if (ytype == cg.CoreTypes.PhpNumber) { // value * number : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Mul_value_number) .Expect(cg.CoreTypes.PhpNumber); } else if (ytype == cg.CoreTypes.Long) { // value * i8 : number return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Mul_value_long) .Expect(cg.CoreTypes.PhpNumber); } else if (ytype == cg.CoreTypes.Double) { // value * r8 : double return cg.EmitCall(ILOpCode.Call, cg.CoreMethods.PhpNumber.Mul_value_double) .Expect(SpecialType.System_Double); } // throw new NotImplementedException($"Mul(PhpValue, {ytype.Name})"); } // throw new NotImplementedException($"Mul({xtype.Name}, ...)"); } }
/// <summary> /// Emits comparison operator pushing <c>bool</c> (<c>i4</c> of value <c>0</c> or <c>1</c>) onto the evaluation stack. /// </summary> /// <returns>Resulting type code pushed onto the top of evaliuation stack.</returns> internal static TypeSymbol EmitLtGt(CodeGenerator cg, TypeSymbol xtype, BoundExpression right, bool lt) { TypeSymbol ytype; var il = cg.Builder; switch (xtype.SpecialType) { case SpecialType.System_Void: // Operators.CompareNull(value) throw new NotImplementedException(); case SpecialType.System_Int32: // i4 -> i8 il.EmitOpCode(ILOpCode.Conv_i8); goto case SpecialType.System_Int64; case SpecialType.System_Int64: ytype = cg.EmitConvertIntToLong(cg.Emit(right)); // bool|int -> long if (ytype.SpecialType == SpecialType.System_Int64) { il.EmitOpCode(lt ? ILOpCode.Clt : ILOpCode.Cgt); } else if (ytype.SpecialType == SpecialType.System_Double) { // i8 <> r8 return cg.EmitCall(ILOpCode.Call, lt ? cg.CoreMethods.Operators.Clt_long_double : cg.CoreMethods.Operators.Cgt_long_double); } else { ytype = cg.EmitConvertToPhpValue(ytype, 0); // compare(i8, value) <> 0 cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Compare_long_value); il.EmitOpCode(ILOpCode.Ldc_i4_0, 1); il.EmitOpCode(lt ? ILOpCode.Clt : ILOpCode.Cgt); } return cg.CoreTypes.Boolean; case SpecialType.System_Double: ytype = cg.EmitConvertNumberToDouble(right); // bool|int|long|number -> double if (ytype.SpecialType == SpecialType.System_Double) { // r8 <> r8 il.EmitOpCode(lt ? ILOpCode.Clt : ILOpCode.Cgt); } else { // compare(r8, value) ytype = cg.EmitConvertToPhpValue(ytype, 0); cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Compare_double_value); // <> 0 il.EmitOpCode(ILOpCode.Ldc_i4_0, 1); il.EmitOpCode(lt ? ILOpCode.Clt : ILOpCode.Cgt); } return cg.CoreTypes.Boolean; case SpecialType.System_String: ytype = cg.Emit(right); if (ytype.SpecialType == SpecialType.System_String) { // compare(string, string) cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Compare_string_string); } else if (ytype.SpecialType == SpecialType.System_Int64) { // compare(string, long) cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Compare_string_long); } else if (ytype.SpecialType == SpecialType.System_Double) { // compare(string, double) cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Compare_string_double); } else { // compare(string, value) ytype = cg.EmitConvertToPhpValue(ytype, 0); cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Compare_string_value); } // <> 0 il.EmitOpCode(ILOpCode.Ldc_i4_0, 1); il.EmitOpCode(lt ? ILOpCode.Clt : ILOpCode.Cgt); return cg.CoreTypes.Boolean; case SpecialType.System_Boolean: cg.EmitConvert(right, cg.CoreTypes.Boolean); ytype = cg.CoreTypes.Boolean; // compare(bool, bool) cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Compare_bool_bool); // <> 0 il.EmitOpCode(ILOpCode.Ldc_i4_0, 1); il.EmitOpCode(lt ? ILOpCode.Clt : ILOpCode.Cgt); return cg.CoreTypes.Boolean; default: if (xtype == cg.CoreTypes.PhpNumber) { ytype = cg.EmitConvertIntToLong(cg.Emit(right)); // bool|int -> long if (ytype.SpecialType == SpecialType.System_Int64) { // number <> i8 return cg.EmitCall(ILOpCode.Call, lt ? cg.CoreMethods.PhpNumber.lt_number_long : cg.CoreMethods.PhpNumber.gt_number_long) .Expect(SpecialType.System_Boolean); } else if (ytype.SpecialType == SpecialType.System_Double) { // number <> r8 return cg.EmitCall(ILOpCode.Call, lt ? cg.CoreMethods.PhpNumber.lt_number_double : cg.CoreMethods.PhpNumber.gt_number_double) .Expect(SpecialType.System_Boolean); } else if (ytype == cg.CoreTypes.PhpNumber) { // number <> number return cg.EmitCall(ILOpCode.Call, lt ? cg.CoreMethods.PhpNumber.lt_number_number : cg.CoreMethods.PhpNumber.gt_number_number) .Expect(SpecialType.System_Boolean); } else { ytype = cg.EmitConvertToPhpValue(ytype, 0); // compare(number, value) cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Compare_number_value); // <> 0 il.EmitOpCode(ILOpCode.Ldc_i4_0, 1); // +1 on stack il.EmitOpCode(lt ? ILOpCode.Clt : ILOpCode.Cgt); return cg.CoreTypes.Boolean; } } else { xtype = cg.EmitConvertToPhpValue(xtype, 0); ytype = cg.Emit(right); // TODO: if (ytype.SpecialType == SpecialType.System_Boolean) ... // TODO: if (ytype.SpecialType == SpecialType.System_Int64) ... // TODO: if (ytype.SpecialType == SpecialType.System_String) ... // TODO: if (ytype.SpecialType == SpecialType.System_Double) ... // compare(value, value) ytype = cg.EmitConvertToPhpValue(ytype, right.TypeRefMask); cg.EmitCall(ILOpCode.Call, cg.CoreMethods.Operators.Compare_value_value); // <> 0 il.EmitOpCode(ILOpCode.Ldc_i4_0, 1); il.EmitOpCode(lt ? ILOpCode.Clt : ILOpCode.Cgt); return cg.CoreTypes.Boolean; } } }
/// <summary> /// Emits comparison operator pushing <c>bool</c> (<c>i4</c> of value <c>0</c> or <c>1</c>) onto the evaluation stack. /// </summary> /// <param name="cg">Code generator helper.</param> /// <param name="lt">True for <c>clt</c> (less than) otherwise <c>cgt</c> (greater than).</param> /// <returns>Resulting type code pushed onto the top of evaliuation stack.</returns> TypeSymbol EmitLtGt(CodeGenerator cg, bool lt) => EmitLtGt(cg, cg.Emit(Left), Right, lt);
internal override void Generate(CodeGenerator cg) { // four cases: // 1. just single or none case label that can be replaced with single IF // 2. switch over integers, using native CIL switch // 3. switch over strings, using C# static Dictionary and CIL switch // 4. PHP style switch which is just a bunch of IFs if (this.CaseBlocks.Length == 0 || this.CaseBlocks[0].IsDefault) { Debug.Assert(this.CaseBlocks.Length <= 1); // no SWITCH or IF needed cg.EmitPop(this.SwitchValue.WithAccess(BoundAccess.None).Emit(cg)); // None Access, also using BoundExpression.Emit directly to avoid CodeGenerator type specialization which is not needed if (this.CaseBlocks.Length == 1) { cg.GenerateScope(this.CaseBlocks[0], NextBlock.Ordinal); } } else { // CIL Switch: bool allconsts = this.CaseBlocks.All(c => c.IsDefault || c.CaseValue.ConstantValue.HasValue); bool allconstints = allconsts && this.CaseBlocks.All(c => c.IsDefault || IsInt32(c.CaseValue.ConstantValue.Value)); //bool allconststrings = allconsts && this.CaseBlocks.All(c => c.IsDefault || IsString(c.CaseValue.ConstantValue.Value)); var default_block = this.DefaultBlock; // <switch_loc> = <SwitchValue>; TypeSymbol switch_type; LocalDefinition switch_loc; // Switch Header if (allconstints) { switch_type = cg.CoreTypes.Int32; cg.EmitSequencePoint(this.SwitchValue.PhpSyntax); cg.EmitConvert(this.SwitchValue, switch_type); switch_loc = cg.GetTemporaryLocal(switch_type); cg.Builder.EmitLocalStore(switch_loc); // switch (labels) cg.Builder.EmitIntegerSwitchJumpTable(GetSwitchCaseLabels(CaseBlocks), default_block ?? NextBlock, switch_loc, switch_type.PrimitiveTypeCode); } //else if (allconststrings) //{ //} else { // legacy jump table // IF (case_i) GOTO label_i; cg.EmitSequencePoint(this.SwitchValue.PhpSyntax); switch_type = cg.Emit(this.SwitchValue); switch_loc = cg.GetTemporaryLocal(switch_type); cg.Builder.EmitLocalStore(switch_loc); // for (int i = 0; i < this.CaseBlocks.Length; i++) { var this_block = this.CaseBlocks[i]; if (this_block.CaseValue != null) { // <CaseValue>: cg.EmitSequencePoint(this_block.CaseValue.PhpSyntax); // if (<switch_loc> == c.CaseValue) goto this_block; cg.Builder.EmitLocalLoad(switch_loc); BoundBinaryEx.EmitEquality(cg, switch_type, this_block.CaseValue); cg.Builder.EmitBranch(ILOpCode.Brtrue, this_block); } } // default: cg.Builder.EmitBranch(ILOpCode.Br, default_block ?? NextBlock); } // FREE <switch_loc> cg.ReturnTemporaryLocal(switch_loc); // Switch Body this.CaseBlocks.ForEach((i, this_block) => { var next_case = (i + 1 < this.CaseBlocks.Length) ? this.CaseBlocks[i + 1] : null; // { cg.GenerateScope(this_block, (next_case ?? NextBlock).Ordinal); // } }); } // cg.Scope.ContinueWith(NextBlock); }
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; }
/// <summary> /// Emits <c>pow</c> operator. /// </summary> TypeSymbol EmitPow(CodeGenerator cg) { return EmitPow(cg, cg.Emit(Left), Left.TypeRefMask, Right); }
/// <summary> /// Emits <c>+</c> operator suitable for actual operands. /// </summary> private static TypeSymbol EmitAdd(CodeGenerator cg, BoundExpression left, BoundExpression right, TypeSymbol resultTypeOpt = null) { // Template: x + y return EmitAdd(cg, cg.Emit(left), right, resultTypeOpt); }
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); } }
/// <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 override TypeSymbol Emit(CodeGenerator cg) { Debug.Assert(Access.IsRead || Access.IsNone); TypeSymbol returned_type; switch (this.Operation) { case Operations.AtSign: // special arrangement // Template: // context.DisableErrorReporting(); // s; // context.EnableErrorReporting(); returned_type = cg.EmitWithDisabledErrorReporting(Operand); break; case Operations.BitNegation: //Template: "~x" Operators.BitNot(x) returned_type = EmitBitNot(cg); break; case Operations.Clone: // Template: clone x Debug.WriteLine("TODO: clone(object)"); returned_type = cg.Emit(Operand); // TODO: clone break; case Operations.LogicNegation: //Template: !(bool)(x); cg.EmitConvertToBool(this.Operand, true); returned_type = cg.CoreTypes.Boolean; break; case Operations.Minus: //Template: "-x" returned_type = EmitMinus(cg); break; case Operations.Plus: //Template: "+x" returned_type = EmitPlus(cg); break; case Operations.ObjectCast: //Template: "(object)x" cg.EmitConvert(this.Operand, cg.CoreTypes.Object); returned_type = cg.CoreTypes.Object; break; case Operations.Print: cg.EmitEcho(this.Operand); if (Access.IsRead) { // Always returns 1 cg.Builder.EmitLongConstant(1); returned_type = cg.CoreTypes.Long; } else { // nobody reads the result anyway returned_type = cg.CoreTypes.Void; } break; case Operations.BoolCast: //Template: "(bool)x" cg.EmitConvert(this.Operand, cg.CoreTypes.Boolean); returned_type = cg.CoreTypes.Boolean; break; case Operations.Int8Cast: case Operations.Int16Cast: case Operations.Int32Cast: case Operations.UInt8Cast: case Operations.UInt16Cast: case Operations.UInt64Cast: case Operations.UInt32Cast: case Operations.Int64Cast: cg.EmitConvert(this.Operand, cg.CoreTypes.Long); returned_type = cg.CoreTypes.Long; break; case Operations.DecimalCast: case Operations.DoubleCast: case Operations.FloatCast: cg.EmitConvert(this.Operand, cg.CoreTypes.Double); returned_type = cg.CoreTypes.Double; break; case Operations.UnicodeCast: // TODO case Operations.StringCast: // (string)x cg.EmitConvert(this.Operand, cg.CoreTypes.String); // TODO: to String or PhpString ? to not corrupt single-byte string return cg.CoreTypes.String; case Operations.BinaryCast: //if ((returned_typecode = node.Expr.Emit(codeGenerator)) != PhpTypeCode.PhpBytes) //{ // codeGenerator.EmitBoxing(returned_typecode); // //codeGenerator.EmitLoadClassContext(); // il.Emit(OpCodes.Call, Methods.Convert.ObjectToPhpBytes); // returned_typecode = PhpTypeCode.PhpBytes; //} //break; throw new NotImplementedException(); case Operations.ArrayCast: //Template: "(array)x" cg.EmitConvert(this.Operand, cg.CoreTypes.PhpArray); // TODO: EmitArrayCast() returned_type = cg.CoreTypes.PhpArray; break; case Operations.UnsetCast: // Template: "(unset)x" null cg.EmitPop(cg.Emit(this.Operand)); if (this.Access.IsRead) { cg.Builder.EmitNullConstant(); returned_type = cg.CoreTypes.Object; } else { returned_type = cg.CoreTypes.Void; } break; default: throw ExceptionUtilities.Unreachable; } switch (Access.Flags) { case AccessMask.Read: Debug.Assert(returned_type.SpecialType != SpecialType.System_Void); // do nothing break; case AccessMask.None: // pop operation's result value from stack cg.EmitPop(returned_type); returned_type = cg.CoreTypes.Void; break; default: throw ExceptionUtilities.UnexpectedValue(Access); } return returned_type; }
internal override TypeSymbol Emit(CodeGenerator cg) { var result_type = cg.DeclaringCompilation.GetTypeFromTypeRef(cg.Routine, this.TypeRefMask); if (this.IfTrue != null) { object trueLbl = new object(); object endLbl = new object(); // Cond ? True : False cg.EmitConvert(this.Condition, cg.CoreTypes.Boolean); // i4 cg.Builder.EmitBranch(ILOpCode.Brtrue, trueLbl); // false: cg.EmitConvert(this.IfFalse, result_type); cg.Builder.EmitBranch(ILOpCode.Br, endLbl); cg.Builder.AdjustStack(-1); // workarounds assert in ILBuilder.MarkLabel, we're doing something wrong with ILBuilder // trueLbl: cg.Builder.MarkLabel(trueLbl); cg.EmitConvert(this.IfTrue, result_type); // endLbl: cg.Builder.MarkLabel(endLbl); } else { object trueLbl = new object(); object endLbl = new object(); // Cond ?: False // <stack> = <cond_var> = Cond var cond_type = cg.Emit(this.Condition); var cond_var = cg.GetTemporaryLocal(cond_type); cg.Builder.EmitOpCode(ILOpCode.Dup); cg.Builder.EmitLocalStore(cond_var); cg.EmitConvertToBool(cond_type, this.Condition.TypeRefMask); cg.Builder.EmitBranch(ILOpCode.Brtrue, trueLbl); // false: cg.EmitConvert(this.IfFalse, result_type); cg.Builder.EmitBranch(ILOpCode.Br, endLbl); cg.Builder.AdjustStack(-1); // workarounds assert in ILBuilder.MarkLabel, we're doing something wrong with ILBuilder // trueLbl: cg.Builder.MarkLabel(trueLbl); cg.Builder.EmitLocalLoad(cond_var); cg.EmitConvert(cond_type, this.Condition.TypeRefMask, result_type); // endLbl: cg.Builder.MarkLabel(endLbl); // cg.ReturnTemporaryLocal(cond_var); } // if (Access.IsNone) { cg.EmitPop(result_type); result_type = cg.CoreTypes.Void; } // return result_type; }
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 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; }
TypeSymbol EmitPlus(CodeGenerator cg) { // Template: 0L + Operand // convert value to a number var il = cg.Builder; var t = cg.Emit(this.Operand); switch (t.SpecialType) { case SpecialType.System_Double: case SpecialType.System_Int32: case SpecialType.System_Int64: return t; case SpecialType.System_Boolean: // (long)(int)bool il.EmitOpCode(ILOpCode.Conv_i4); il.EmitOpCode(ILOpCode.Conv_i8); return cg.CoreTypes.Long; default: if (t != cg.CoreTypes.PhpNumber) { cg.EmitConvertToPhpNumber(t, 0); } return cg.CoreTypes.PhpNumber; } }
/// <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},...)"); } }
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); } }
/// <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); } } }
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; }
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); } } }
/// <summary> /// Emits instance. Caches the result if holder is provided, or loads evaluated instance if holder was initialized already. /// </summary> public static TypeSymbol EmitInstance(InstanceCacheHolder holderOrNull, CodeGenerator cg, BoundExpression instance) { return (instance != null) ? EmitInstance(holderOrNull, cg, () => cg.Emit(instance)) : null; }