internal static void EmitConstrainedCoercion(ILEmitter/*!*/ il, Type/*!*/ narrow, Type/*!*/ wide, object threshold) { Label else_label = il.DefineLabel(); Label endif_label = il.DefineLabel(); il.Emit(OpCodes.Dup); // IF (STACK <= threshold) THEN il.LoadLiteral(threshold); il.Emit(OpCodes.Bgt_S, else_label); // LOAD (narrow)STACK il.Conv(narrow, false); il.Emit(OpCodes.Box, narrow); il.Emit(OpCodes.Br_S, endif_label); // ELSE il.MarkLabel(else_label); // LOAD (wide)STACK il.Conv(wide, false); il.Emit(OpCodes.Box, wide); // ENDIF il.MarkLabel(endif_label); }
/// <summary> /// Converts object to CLR type /// </summary> private static bool EmitConvertObjectToClr(ILEmitter il, PhpTypeCode typeCode, Type formalType, LocalBuilder strictnessLocal) { MethodInfo convert_method = null; switch (Type.GetTypeCode(formalType)) { case TypeCode.Boolean: if (typeCode != PhpTypeCode.Boolean) convert_method = Methods.ConvertToClr.TryObjectToBoolean; break; case TypeCode.Int32: if (typeCode != PhpTypeCode.Integer) convert_method = Methods.ConvertToClr.TryObjectToInt32; break; case TypeCode.Int64: if (typeCode != PhpTypeCode.LongInteger) convert_method = Methods.ConvertToClr.TryObjectToInt64; break; case TypeCode.Double: if (typeCode != PhpTypeCode.Double) convert_method = Methods.ConvertToClr.TryObjectToDouble; break; case TypeCode.String: if (typeCode != PhpTypeCode.String) convert_method = Methods.ConvertToClr.TryObjectToString; break; case TypeCode.SByte: convert_method = Methods.ConvertToClr.TryObjectToInt8; break; case TypeCode.Int16: convert_method = Methods.ConvertToClr.TryObjectToInt16; break; case TypeCode.Byte: convert_method = Methods.ConvertToClr.TryObjectToUInt8; break; case TypeCode.UInt16: convert_method = Methods.ConvertToClr.TryObjectToUInt16; break; case TypeCode.UInt32: convert_method = Methods.ConvertToClr.TryObjectToUInt32; break; case TypeCode.UInt64: convert_method = Methods.ConvertToClr.TryObjectToUInt64; break; case TypeCode.Single: convert_method = Methods.ConvertToClr.TryObjectToSingle; break; case TypeCode.Decimal: convert_method = Methods.ConvertToClr.TryObjectToDecimal; break; case TypeCode.Char: convert_method = Methods.ConvertToClr.TryObjectToChar; break; case TypeCode.DateTime: convert_method = Methods.ConvertToClr.TryObjectToDateTime; break; case TypeCode.DBNull: convert_method = Methods.ConvertToClr.TryObjectToDBNull; break; case TypeCode.Object: { if (formalType.IsValueType) { if (formalType.IsGenericType && NullableType == formalType.GetGenericTypeDefinition()) { // This is an ugly corner case (using generic TryObjectToStruct wouldn't work, because // for nullables .IsValueType returns true, but it doesn't match "T : struct" constraint)! // We have to try converting object to Nullable<T> first and then to T // (which requires a new call to 'EmitConvertObjectToClr') Type nullableArg = formalType.GetGenericArguments()[0]; Type nullableType = NullableType.MakeGenericType(nullableArg); LocalBuilder tmpVar = il.DeclareLocal(typeof(object)); // This succeeds only for exact match il.Emit(OpCodes.Call, Methods.ConvertToClr.UnwrapNullable); il.Emit(OpCodes.Dup); il.Stloc(tmpVar); // <stack_0> = tmpVar = UnwrapNullable(...) // if (<stack_0> != null) Label lblNull = il.DefineLabel(), lblDone = il.DefineLabel(); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Beq, lblNull); // { // Convert tmpVar to T and wrap it into Nullable<T> il.Ldloc(tmpVar); bool ret = EmitConvertObjectToClr(il, typeCode, nullableArg, strictnessLocal); // TODO: use reflection cache? il.Emit(OpCodes.Newobj, nullableType.GetConstructors()[0]); il.Emit(OpCodes.Br, lblDone); // } else /* == null */ { il.MarkLabel(lblNull); // return (T?)null; LocalBuilder tmpNull = il.DeclareLocal(nullableType); il.Ldloca(tmpNull); il.Emit(OpCodes.Initobj, nullableType); il.Ldloc(tmpNull); // } il.MarkLabel(lblDone); return ret; } else convert_method = Methods.ConvertToClr.TryObjectToStruct.MakeGenericMethod(formalType); } else { if (formalType.IsArray) convert_method = Methods.ConvertToClr.TryObjectToArray.MakeGenericMethod(formalType.GetElementType()); else if (typeof(Delegate).IsAssignableFrom(formalType)) convert_method = Methods.ConvertToClr.TryObjectToDelegate.MakeGenericMethod(formalType); else convert_method = Methods.ConvertToClr.TryObjectToClass.MakeGenericMethod(formalType); } break; } default: Debug.Fail(); return true; } if (convert_method != null) { il.Ldloca(strictnessLocal); il.Emit(OpCodes.Call, convert_method); return false; } else return true; }
internal void EmitSetConversion(ILEmitter/*!*/ il, PhpTypeCode sourceTypeCode, Type/*!*/ targetType) { LocalBuilder strictness = il.GetTemporaryLocal(typeof(PHP.Core.ConvertToClr.ConversionStrictness)); if (!ClrOverloadBuilder.EmitConvertToClr(il, sourceTypeCode, targetType, strictness)) { Label label_ok = il.DefineLabel(); il.Ldloc(strictness); il.LdcI4((int)PHP.Core.ConvertToClr.ConversionStrictness.Failed); il.Emit(OpCodes.Ceq); il.Emit(OpCodes.Brfalse, label_ok); il.Emit(OpCodes.Ldstr, Property.DeclaringType.FullName); il.Emit(OpCodes.Ldstr, Property.FullName); il.Emit(OpCodes.Call, Methods.PhpException.PropertyTypeMismatch); il.MarkLabel(label_ok, true); } il.ReturnTemporaryLocal(strictness); }
/// <summary> /// Emits call to <see cref="ScriptContext.DeclareFunction"/>. /// </summary> internal static void EmitDeclareFunction(ILEmitter/*!*/il, IPlace/*!*/scriptContextPlace, PhpFunction/*!*/ function) { Label lbl_fieldinitialized = il.DefineLabel(); // private static PhpRoutine <routine>'function = null; var attrs = FieldAttributes.Static | FieldAttributes.Private; var field = il.TypeBuilder.DefineField(string.Format("<routine>'{0}", function.FullName), typeof(PhpRoutineDesc), attrs); // if (<field> == null) il.Emit(OpCodes.Ldsfld, field); il.Emit(OpCodes.Brtrue, lbl_fieldinitialized); { // <field> = new PhpRoutineDesc(<attributes>, new RoutineDelegate(null, <delegate>)) // LOAD <attributes>; il.LdcI4((int)function.MemberDesc.MemberAttributes); // new RoutineDelegate(null, <delegate>, true) il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ldftn, function.ArgLessInfo); il.Emit(OpCodes.Newobj, Constructors.RoutineDelegate); il.LoadBool(true); // new PhpRoutineDesc: il.Emit(OpCodes.Newobj, Constructors.PhpRoutineDesc_Attr_Delegate_Bool); // <field> = <STACK> il.Emit(OpCodes.Stsfld, field); // new PurePhpFunction(<field>, fullName, argfull); // writes desc.Member il.Emit(OpCodes.Ldsfld, field); il.Emit(OpCodes.Ldstr, function.FullName); CodeGenerator.EmitLoadMethodInfo(il, function.ArgFullInfo/*, AssemblyBuilder.DelegateBuilder*/); il.Emit(OpCodes.Newobj, Constructors.PurePhpFunction); il.Emit(OpCodes.Pop); } il.MarkLabel(lbl_fieldinitialized); // CALL ScriptContent.DeclareFunction(<field>, <name>); scriptContextPlace.EmitLoad(il); // LOAD <field> il.Emit(OpCodes.Ldsfld, field); // LOAD <fullName> il.Emit(OpCodes.Ldstr, function.FullName); // il.Emit(OpCodes.Call, Methods.ScriptContext.DeclareFunction); }
/// <summary> /// Define new instance of CallSite<<paramref name="delegateType"/>> and initialize it with specified binder. /// </summary> /// <param name="bodyEmitter"><see cref="ILEmitter"/> of the body that is using this call site. This method may emit initialization of the call site into this <paramref name="bodyEmitter"/>.</param> /// <param name="userFriendlyName">User friendly name used as name for the CallSite field.</param> /// <param name="delegateType">CallSite type argument.</param> /// <param name="binderInstanceEmitter">Function used to emit initialization of the binder from within the call sites container .cctor.</param> /// <returns>The <see cref="FieldInfo"/> containing the instance of the created CallSite.</returns> public FieldInfo/*!*/DefineCallSite(ILEmitter/*!*/bodyEmitter,string/*!*/userFriendlyName, Type/*!*/delegateType, Action<ILEmitter>/*!*/binderInstanceEmitter) { Debug.Assert(userFriendlyName != null && delegateType != null && binderInstanceEmitter != null); userFriendlyName += ("'" + (callSitesCount++)); // call sites container var type = EnsureContainer(); // call site type var callSiteType = Types.CallSiteGeneric[0].MakeGenericType(delegateType); // define the field: // public static readonly CallSite<delegateType> <userFriendlyName> var attrs = FieldAttributes.Static | FieldAttributes.InitOnly | ((staticCtorEmitter == null) ? FieldAttributes.Private : FieldAttributes.Public); var field = type.DefineField(PluginHandler.ConvertCallSiteName(userFriendlyName), callSiteType, attrs); if (staticCtorEmitter == null) // => this.classContext != null { // emit initialization of the call site just in the body of current method (as it is in C#, we need current generic arguments): Debug.Assert(this.classContext != null); // check if the call site if not null, otherwise initialize it first: // if (<field> == null) <InitializeCallSite>; Label ifend = bodyEmitter.DefineLabel(); bodyEmitter.Emit(OpCodes.Ldsfld, field); bodyEmitter.Emit(OpCodes.Brtrue, ifend); // init the field: InitializeCallSite(bodyEmitter, callSiteType, field, binderInstanceEmitter); bodyEmitter.MarkLabel(ifend); } else { // init the field in .cctor: InitializeCallSite(staticCtorEmitter, callSiteType, field, binderInstanceEmitter); } // return field; }
internal void EmitDefinition(CodeGenerator/*!*/ codeGenerator) { if (type.IsComplete) { Debug.Assert(type.IsComplete, "Incomplete types should be converted to evals."); Debug.Assert(type.RealTypeBuilder != null, "A class declared during compilation should have a type builder."); attributes.Emit(codeGenerator, this); typeSignature.Emit(codeGenerator); codeGenerator.EnterTypeDeclaration(type); foreach (TypeMemberDecl member_decl in members) { member_decl.EnterCodegenerator(codeGenerator); member_decl.Emit(codeGenerator); member_decl.LeaveCodegenerator(codeGenerator); } // emit stubs for implemented methods & properties that were not declared by this type: codeGenerator.EmitGhostStubs(type); codeGenerator.LeaveTypeDeclaration(); } else { Debug.Assert(this.typeDefinitionCode != null); // LOAD DynamicCode.Eval(<code>, context, definedVariables, self, includer, source, line, column, evalId) // wrap Eval into static method MethodBuilder method = codeGenerator.IL.TypeBuilder.DefineMethod( string.Format("{0}{1}", ScriptModule.DeclareHelperNane, type.FullName), MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.SpecialName, Types.Void, Types.ScriptContext); var il = new ILEmitter(method); codeGenerator.EnterLambdaDeclaration(il, false, LiteralPlace.Null, new IndexedPlace(PlaceHolder.Argument, 0), LiteralPlace.Null, LiteralPlace.Null); if (true) { codeGenerator.EmitEval( EvalKinds.SyntheticEval, new StringLiteral(position, this.typeDefinitionCode, AccessType.Read), position, (this.Namespace != null) ? this.Namespace.QualifiedName : (QualifiedName?)null, this.validAliases); il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ret); } codeGenerator.LeaveFunctionDeclaration(); // il = codeGenerator.IL; type.IncompleteClassDeclareMethodInfo = method; type.IncompleteClassDeclarationId = String.Format("{0}${1}:{2}:{3}", type.FullName, unchecked((uint)codeGenerator.SourceUnit.SourceFile.ToString().GetHashCode()), position.FirstLine, position.FirstColumn); // sequence point here codeGenerator.MarkSequencePoint(position.FirstLine, position.FirstColumn, position.LastLine, position.LastColumn + 2); if (type.Declaration.IsConditional) { // CALL <Declare>.<FullName>(<context>) codeGenerator.EmitLoadScriptContext(); il.Emit(OpCodes.Call, method); } else { // if (!<context>.IncompleteTypeDeclared(<id>)) // CALL <Declare>.<FullName>(<context>) var end_if = il.DefineLabel(); codeGenerator.EmitLoadScriptContext(); il.Emit(OpCodes.Ldstr, type.IncompleteClassDeclarationId); il.Emit(OpCodes.Call, Methods.ScriptContext.IncompleteTypeDeclared); il.Emit(OpCodes.Brtrue, end_if); if (true) { codeGenerator.EmitLoadScriptContext(); il.Emit(OpCodes.Call, type.IncompleteClassDeclareMethodInfo); } il.MarkLabel(end_if); il.ForgetLabel(end_if); } } }