public void Dispose() { if (_loc != null) { _cg.ReturnTemporaryLocal(_loc); _loc = null; } else if (_tempName != null) { // <temporary>.RemoveKey(name) Debug.Assert(_cg.TemporalLocalsPlace != null); _cg.TemporalLocalsPlace.EmitLoad(_cg.Builder); _cg.EmitIntStringKey(_tempName); _cg.EmitPop(_cg.EmitCall(System.Reflection.Metadata.ILOpCode.Callvirt, _cg.CoreMethods.PhpArray.RemoveKey_IntStringKey)); _tempName = null; } }
internal override void Emit(CodeGenerator cg) { cg.EmitSequencePoint(this.PhpSyntax); var rtype = cg.Routine.ReturnType; var rvoid = rtype.SpecialType == SpecialType.System_Void; // if (this.Returned == null) { if (rvoid) { // <void> } else { // <default> cg.EmitLoadDefault(rtype, cg.Routine.ResultTypeMask); } } else { if (rvoid) { // <expr>; cg.EmitPop(this.Returned.Emit(cg)); } else { // return (T)<expr>; cg.EmitConvert(this.Returned, rtype); } } // .ret cg.EmitRet(rtype); }
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) { 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 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; }
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; }
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 void Emit(CodeGenerator cg) { cg.EmitSequencePoint(this.PhpSyntax); cg.EmitPop(this.Expression.Emit(cg)); }
void EmitPhpNew(SynthesizedPhpNewMethodSymbol phpnew, Emit.PEModuleBuilder module) { if (phpnew == null) return; // static class module.SetMethodBody(phpnew, MethodGenerator.GenerateMethodBody(module, phpnew, (Action<Microsoft.CodeAnalysis.CodeGen.ILBuilder>)(il => { Debug.Assert(SpecialParameterSymbol.IsContextParameter(phpnew.Parameters[0])); var cg = new CodeGenerator(il, module, DiagnosticBag.GetInstance(), OptimizationLevel.Release, false, this, new ParamPlace(phpnew.Parameters[0]), new ArgPlace(this, 0)); // initialize <ctx> field, // if field is declared within this type var ctxField = this.ContextStore; if (ctxField != null && object.ReferenceEquals((object)ctxField.ContainingType, this)) { var ctxFieldPlace = new FieldPlace(cg.ThisPlaceOpt, (IFieldSymbol)ctxField); // Debug.Assert(<ctx> != null) cg.EmitDebugAssertNotNull(cg.ContextPlaceOpt, "Context cannot be null."); // <this>.<ctx> = <ctx> ctxFieldPlace.EmitStorePrepare(il); cg.EmitLoadContext(); ctxFieldPlace.EmitStore(il); } // initialize class fields foreach (var fld in this.GetFieldsToEmit().OfType<SourceFieldSymbol>().Where(fld => !fld.RequiresHolder && !fld.IsStatic && !fld.IsConst)) { fld.EmitInit(cg); } // base..phpnew ?? base..ctor var basenew = phpnew.BasePhpNew; Debug.Assert(basenew != null); cg.EmitPop(cg.EmitThisCall(basenew, phpnew)); Debug.Assert(phpnew.ReturnsVoid); cg.EmitRet(phpnew.ReturnType); }), null, DiagnosticBag.GetInstance(), false)); }
/// <summary> /// Emits the given conversion. 'from' and 'to' matches the classified conversion. /// </summary> public static void EmitConversion(this CodeGenerator cg, CommonConversion conversion, TypeSymbol from, TypeSymbol to, TypeSymbol op = null, bool @checked = false) { // {from}, {op} is loaded on stack // if (conversion.Exists == false) { throw cg.NotImplementedException($"Conversion from '{from}' to '{to}' "); } if (conversion.IsIdentity) { if (op != null) { throw new ArgumentException(nameof(op)); } if (to.SpecialType == SpecialType.System_Void) { // POP cg.EmitPop(from); } // nop } else if (conversion.IsNumeric) { if (op != null) { throw new ArgumentException(nameof(op)); } EmitNumericConversion(cg, from, to, @checked: @checked); } else if (conversion.IsReference) { if (op != null) { throw new ArgumentException(nameof(op)); } // TODO: ensure from/to is a valid reference type cg.EmitCastClass(to); } else if (conversion.IsUserDefined) { var method = (MethodSymbol)conversion.MethodSymbol; var ps = method.Parameters; int pconsumed = 0; if (method.HasThis) { if (from.IsValueType) { if (op != null) { throw new ArgumentException(nameof(op)); } cg.EmitStructAddr(from); } } else { if (ps[0].RefKind != RefKind.None) { throw new InvalidOperationException(); } if (from != ps[0].Type) { if (op != null) { if (!ps[0].Type.IsAssignableFrom(from)) { throw new ArgumentException(nameof(op)); } } else { EmitImplicitConversion(cg, from, ps[0].Type, @checked: @checked); } } pconsumed++; } if (op != null) { if (ps.Length > pconsumed) { EmitImplicitConversion(cg, op, ps[pconsumed].Type, @checked: @checked); } pconsumed++; } // Context ctx, if (ps.Length > pconsumed && SpecialParameterSymbol.IsContextParameter(ps[pconsumed])) { cg.EmitLoadContext(); pconsumed++; } if (ps.Length != pconsumed) { throw new InvalidOperationException(); } EmitImplicitConversion(cg, cg.EmitCall(method.IsVirtual ? ILOpCode.Callvirt : ILOpCode.Call, method), to, @checked: true); } else { throw new NotImplementedException(); } }
/// <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(); } }
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); }
/// <summary> /// Emits instance of the field containing class. /// </summary> protected virtual void EmitLoadFieldInstance(CodeGenerator cg, InstanceCacheHolder instanceOpt) { // instance var instancetype = InstanceCacheHolder.EmitInstance(instanceOpt, cg, Instance); // if (Field.IsStatic && Instance != null) cg.EmitPop(instancetype); else if (!Field.IsStatic && Instance == null) throw new NotImplementedException(); }
internal override TypeSymbol Emit(CodeGenerator cg) { Debug.Assert(this.Access.IsRead || this.Access.IsNone); // TypeSymbol returned_type; if (UsesOperatorMethod) { throw new NotImplementedException(); // call this.Operator(Left, Right) } switch (this.Operation) { #region Arithmetic Operations case Operations.Add: returned_type = (cg.IsLongOnly(this.TypeRefMask)) ? cg.CoreTypes.Long.Symbol : this.Access.TargetType; returned_type = EmitAdd(cg, Left, Right, returned_type); break; case Operations.Sub: //Template: "x - y" Operators.Subtract(x,y) [overloads] returned_type = EmitSub(cg, Left, Right, this.Access.TargetType); break; case Operations.Div: //Template: "x / y" returned_type = EmitDivision(cg); break; case Operations.Mul: //Template: "x * y" returned_type = EmitMultiply(cg); break; case Operations.Pow: //Template: "x ** y" returned_type = EmitPow(cg); break; case Operations.Mod: //Template: "x % y" Operators.Remainder(x,y) //codeGenerator.EmitBoxing(node.LeftExpr.Emit(codeGenerator)); //ro_typecode = node.RightExpr.Emit(codeGenerator); //switch (ro_typecode) //{ // case PhpTypeCode.Integer: // returned_typecode = codeGenerator.EmitMethodCall(Methods.Operators.Remainder.Object_Int32); // break; // default: // codeGenerator.EmitBoxing(ro_typecode); // returned_typecode = codeGenerator.EmitMethodCall(Methods.Operators.Remainder.Object_Object); // break; //} //break; throw new NotImplementedException(); case Operations.ShiftLeft: //// LOAD Operators.ShiftLeft(box left, box right); //codeGenerator.EmitBoxing(node.LeftExpr.Emit(codeGenerator)); //codeGenerator.EmitBoxing(node.RightExpr.Emit(codeGenerator)); //returned_typecode = codeGenerator.EmitMethodCall(Methods.Operators.ShiftLeft); //break; throw new NotImplementedException(); case Operations.ShiftRight: //// LOAD Operators.ShiftRight(box left, box right); //codeGenerator.EmitBoxing(node.LeftExpr.Emit(codeGenerator)); //codeGenerator.EmitBoxing(node.RightExpr.Emit(codeGenerator)); //returned_typecode = codeGenerator.EmitMethodCall(Methods.Operators.ShiftRight); //break; throw new NotImplementedException(); #endregion #region Boolean and Bitwise Operations case Operations.And: returned_type = EmitBinaryBooleanOperation(cg, true); break; case Operations.Or: returned_type = EmitBinaryBooleanOperation(cg, false); break; case Operations.Xor: returned_type = EmitBinaryXor(cg); break; case Operations.BitAnd: returned_type = EmitBitAnd(cg, Left, Right); break; case Operations.BitOr: returned_type = EmitBitOr(cg, Left, Right); break; case Operations.BitXor: //returned_typecode = EmitBitOperation(node, codeGenerator, Operators.BitOp.Xor); //break; throw new NotImplementedException(); #endregion #region Comparing Operations case Operations.Equal: returned_type = EmitEquality(cg); break; case Operations.NotEqual: EmitEquality(cg); cg.EmitLogicNegation(); returned_type = cg.CoreTypes.Boolean; break; case Operations.GreaterThan: returned_type = EmitLtGt(cg, false); break; case Operations.LessThan: returned_type = EmitLtGt(cg, true); break; case Operations.GreaterThanOrEqual: // template: !(LessThan) returned_type = EmitLtGt(cg, true); cg.EmitLogicNegation(); break; case Operations.LessThanOrEqual: // template: !(GreaterThan) returned_type = EmitLtGt(cg, false); cg.EmitLogicNegation(); break; case Operations.Identical: // Left === Right returned_type = EmitStrictEquality(cg); break; case Operations.NotIdentical: // ! (Left === Right) returned_type = EmitStrictEquality(cg); cg.EmitLogicNegation(); break; #endregion default: throw ExceptionUtilities.Unreachable; } // switch (Access.Flags) { case AccessMask.Read: // Result is read, do nothing. Debug.Assert(returned_type.SpecialType != SpecialType.System_Void); break; case AccessMask.None: // Result is not read, pop the result cg.EmitPop(returned_type); returned_type = cg.CoreTypes.Void; break; } // return returned_type; }
void EmitPhpCtor(MethodSymbol ctor, Emit.PEModuleBuilder module) { if (ctor == null) return; // static class Debug.Assert(ctor.MethodKind == MethodKind.Constructor); module.SetMethodBody(ctor, MethodGenerator.GenerateMethodBody(module, ctor, il => { Debug.Assert(SpecialParameterSymbol.IsContextParameter(ctor.Parameters[0])); var cg = new CodeGenerator(il, module, DiagnosticBag.GetInstance(), OptimizationLevel.Release, false, this, new ParamPlace(ctor.Parameters[0]), new ArgPlace(this, 0)); // call .phpnew var phpnew = this.InitializeInstanceMethod; cg.EmitPop(cg.EmitThisCall(phpnew, ctor)); // call __construct var phpctor = this.ResolvePhpCtor(true); cg.EmitPop(cg.EmitThisCall(phpctor, ctor)); Debug.Assert(ctor.ReturnsVoid); cg.EmitRet(ctor.ReturnType); }, null, DiagnosticBag.GetInstance(), false)); }
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> /// Stores value from top of the evaluation stack to a temporary variable which will be returned from the exit block. /// </summary> internal void EmitTmpRet(CodeGenerator cg, Symbols.TypeSymbol stack) { // lazy initialize if (_retlbl == null) { _retlbl = new object(); } if (_rettmp == null) { var rtype = cg.Routine.ReturnType; if (rtype.SpecialType != SpecialType.System_Void) { _rettmp = cg.GetTemporaryLocal(rtype); } } // <rettmp> = <stack>; if (_rettmp != null) { cg.EmitConvert(stack, 0, (Symbols.TypeSymbol)_rettmp.Type); cg.Builder.EmitLocalStore(_rettmp); cg.Builder.EmitBranch(ILOpCode.Br, _retlbl); } else { cg.EmitPop(stack); } }