public void DefineContextType() { linqContextBuilder = cg.IL.TypeBuilder.DefineNestedType(ContextTypeName + cg.IL.GetNextUniqueIndex(), TypeAttributes.Class | TypeAttributes.NestedPrivate | TypeAttributes.Sealed, typeof(PHP.Core.LinqContext), null); // .ctor: ConstructorBuilder ctor = linqContextBuilder.DefineConstructor(MethodAttributes.Assembly, CallingConventions.HasThis, Types.LinqContextArgs); ILEmitter il = new ILEmitter(ctor); il.Ldarg(0); il.Ldarg(1); il.Ldarg(2); il.Ldarg(3); il.Ldarg(4); il.Emit(OpCodes.Call, Constructors.LinqContext); il.Emit(OpCodes.Ret); linqContextCtor = ctor; }
/// <summary> /// Emit publically accessible stub that just calls argfull of <paramref name="function"/>. /// </summary> /// <returns><see cref="MethodInfo"/> of newly created function stub.</returns> private MethodInfo/*!*/EmitPhpFunctionPublicStub(ref TypeBuilder publicsContainer, PhpFunction/*!*/function) { Debug.Assert(function != null); Debug.Assert(function.ArgFullInfo != null, "!function.ArgFullInfo"); if (publicsContainer == null) { publicsContainer = PureAssemblyBuilder.RealModuleBuilder.DefineType( string.Format("{1}<{0}>", StringUtils.ToClsCompliantIdentifier(Path.ChangeExtension(PureAssemblyBuilder.FileName, "")), QualifiedName.Global.ToString()), TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class | TypeAttributes.SpecialName); } Type returnType; var parameterTypes = function.Signature.ToArgfullSignature(1, out returnType); parameterTypes[0] = Types.ScriptContext[0]; var mi = publicsContainer.DefineMethod(function.GetFullName(), MethodAttributes.Public | MethodAttributes.Static, returnType, parameterTypes); var il = new ILEmitter(mi); // load arguments for (int i = 0; i < parameterTypes.Length; i++) { if (function.Builder != null) mi.DefineParameter(i + 1, ParameterAttributes.None, function.Builder.ParameterBuilders[i].Name); il.Ldarg(i); } // call function.ArgFullInfo il.Emit(OpCodes.Call, function.ArgFullInfo); // .ret il.Emit(OpCodes.Ret); // return mi; }
internal static void EmitSetterStub(ILEmitter/*!*/ il, PropertyInfo/*!*/ propertyInfo, Type/*!*/ declaringType) { var setter = propertyInfo.GetSetMethod(/*false*/); if (setter == null) { il.Emit(OpCodes.Ldstr, declaringType.Name); il.Emit(OpCodes.Ldstr, "set_" + propertyInfo.Name); il.Emit(OpCodes.Call, Methods.PhpException.UndefinedMethodCalled); // CoreResources.readonly_property_written il.Emit(OpCodes.Ret); return; } var parameters = setter.GetParameters(); Debug.Assert(parameters.Length == 1 /*&& parameters[0].ParameterType == Types.PhpReference[0]*/); if (!setter.IsStatic) { // [ ((self)<instance>). ] il.Ldarg(0); il.Emit(OpCodes.Castclass, declaringType); } // [ setter((object)value) ] il.Ldarg(1); il.Emit(OpCodes.Castclass, parameters[0].ParameterType); il.Emit(OpCodes.Call, setter); // il.Emit(OpCodes.Ret); }
internal static void EmitGetterStub(ILEmitter/*!*/ il, PropertyInfo/*!*/ propertyInfo, Type/*!*/ declaringType) { var getter = propertyInfo.GetGetMethod(/*false*/); if (getter == null) { il.Emit(OpCodes.Ldstr, declaringType.Name); il.Emit(OpCodes.Ldstr, "get_" + propertyInfo.Name); il.Emit(OpCodes.Call, Methods.PhpException.UndefinedMethodCalled); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ret); return; } if (getter.IsStatic) { // [ return getter() ] il.Emit(OpCodes.Call, getter); } else { // [ return ((self)<instance>).getter() ] il.Ldarg(0); il.Emit(OpCodes.Castclass, declaringType); il.Emit(OpCodes.Call, getter); } // box il.EmitBoxing(PhpTypeCodeEnum.FromType(getter.ReturnType)); // il.Emit(OpCodes.Ret); }
internal static void EmitSetterStub(ILEmitter/*!*/ il, FieldInfo/*!*/ fieldInfo, Type/*!*/ declaringType) { Debug.Assert(fieldInfo.FieldType == Types.PhpReference[0]); if (fieldInfo.IsStatic) { // [ <real_field> = (PhpReference)value ] il.Ldarg(1); il.Emit(OpCodes.Castclass, Types.PhpReference[0]); il.Emit(OpCodes.Stsfld, fieldInfo); } else { // [ ((self)<instance>).<real_field> = (PhpReference)value ] il.Ldarg(0); il.Emit(OpCodes.Castclass, declaringType); il.Ldarg(1); il.Emit(OpCodes.Castclass, Types.PhpReference[0]); il.Emit(OpCodes.Stfld, fieldInfo); } il.Emit(OpCodes.Ret); }
protected virtual GetterDelegate/*!*/ GenerateGetterStub() { #if SILVERLIGHT DynamicMethod stub = new DynamicMethod("<^GetterStub>", Types.Object[0], Types.Object); #else DynamicMethod stub = new DynamicMethod("<^GetterStub>", PhpFunctionUtils.DynamicStubAttributes, CallingConventions.Standard, Types.Object[0], Types.Object, this.declaringType.RealType, true); #endif ILEmitter il = new ILEmitter(stub); ClrEvent clr_event; ClrProperty clr_property; Type result_type; if ((clr_event = Member as ClrEvent) != null) { Debug.Assert(!declaringType.RealType.IsValueType, "Value type with ClrEvent not handled! TODO: arg(0) is ClrValue<T>."); LocalBuilder temp = il.DeclareLocal(declaringType.RealType); il.Ldarg(0); il.Emit(OpCodes.Castclass, declaringType.RealType); il.Stloc(temp); clr_event.EmitGetEventObject(il, new Place(null, Properties.ScriptContext_CurrentContext), new Place(temp), true); } else { if ((clr_property = Member as ClrProperty) != null) { // return error-throwing getter if the property is write-only if (!clr_property.HasGetter) return new GetterDelegate(MissingGetter); if (!clr_property.Getter.IsStatic) { ClrOverloadBuilder.EmitLoadInstance(il, IndexedPlace.ThisArg, declaringType.RealType); // il.Emit(OpCodes.Ldarg_0); // if (declaringType.RealType.IsValueType) // il.Emit(OpCodes.Unbox, declaringType.RealType); //#if EMIT_VERIFIABLE_STUBS // else // il.Emit(OpCodes.Castclass, this.declaringType.RealType); //#endif } il.Emit(OpCodes.Call, clr_property.Getter); result_type = clr_property.Getter.ReturnType; } else { ClrField clr_field = ClrField; if (!clr_field.FieldInfo.IsStatic) { ClrOverloadBuilder.EmitLoadInstance(il, IndexedPlace.ThisArg, declaringType.RealType); //il.Emit(OpCodes.Ldarg_0); ////il.Emit(OpCodes.Castclass, this.declaringType.RealType); //if (declaringType.RealType.IsValueType) il.Emit(OpCodes.Unbox, declaringType.RealType); il.Emit(OpCodes.Ldfld, clr_field.FieldInfo); } else { il.Emit(OpCodes.Ldsfld, clr_field.FieldInfo); } result_type = clr_field.FieldInfo.FieldType; } il.EmitBoxing(ClrOverloadBuilder.EmitConvertToPhp(il, result_type/*, null*/)); } il.Emit(OpCodes.Ret); return (GetterDelegate)stub.CreateDelegate(typeof(GetterDelegate)); }
internal static void EmitGetterStub(ILEmitter/*!*/ il, FieldInfo/*!*/ fieldInfo, Type/*!*/ declaringType) { if (fieldInfo.IsStatic) { // [ return <real_field> ] il.Emit(OpCodes.Ldsfld, fieldInfo); } else { // [ return ((self)<instance>).<real_field> ] il.Ldarg(0); il.Emit(OpCodes.Castclass, declaringType); il.Emit(OpCodes.Ldfld, fieldInfo); } il.Emit(OpCodes.Ret); }
/// <summary> /// Emits stubs for one overridden or implemented PHP method. /// </summary> /// <param name="stubs">Already generated stubs.</param> /// <param name="target">The overriding/implementing method.</param> /// <param name="targetType">The type (perhaps constructed) that declared <paramref name="target"/>.</param> /// <param name="declaringType">The type where the stubs should be emitted.</param> /// <param name="template">The method being overridden/implemented (surely PHP).</param> /// <param name="newSlot"><B>True</B> if the stub should be assigned a new vtable slot, /// <B>false</B> otherwise.</param> /// <remarks> /// This method handles situations where method overriding/implementing does not work by itself because of /// the fact that method names in PHP are case insensitive. /// </remarks> private void EmitOverrideStubsForPhpTemplate(IDictionary<string, MethodBuilder>/*!*/ stubs, PhpMethod/*!*/ target, DType/*!*/ targetType, PhpType/*!*/ declaringType, DMemberRef/*!*/ template, bool newSlot) { PhpMethod php_template = (PhpMethod)template.Member; // Emit method stub if needed here ... (resolve .NET incompatibility of base method and overriding method) // // Until now, several possible cases or their combination are known: // - base and overriding methods match, but their name letter-casing don't (need to define override explicitly to properly Bake the type) // - base and overriding methods name match exactly, but overriding methods has additional arguments (with default values) (in PHP it is allowed) (stub needed) // - ghost stub, where B extends A implements I, where A contains definition of method in I and casing does not match // // if signatures don't match, virtual sealed stub must be created, it only calls the target method // if signatures match, only explicit override must be stated if (target.Name.ToString() != php_template.Name.ToString() || // the names differ (perhaps only in casing) target.Signature.ParamCount != php_template.Signature.ParamCount || // signature was extended (additional arguments added, with implicit value only) target.Signature.AliasReturn != php_template.Signature.AliasReturn // returns PhpReference instead of Object ) { MethodInfo target_argfull = DType.MakeConstructed(target.ArgFullInfo, targetType as ConstructedType); TypeBuilder type_builder = declaringType.RealTypeBuilder; // we have to generate a pass-thru override stub that overrides the template based on // name since it is impossible to install an explicit override of a method declared by // a generic type in v2.0 SRE (feedback ID=97425) bool sre_bug_workaround = (template.Type is ConstructedType); if (target.DeclaringType == declaringType && !sre_bug_workaround && target.Signature.ParamCount == php_template.Signature.ParamCount && target.Signature.AliasReturn == php_template.Signature.AliasReturn) { // signatures match, just install an explicit override if possible type_builder.DefineMethodOverride(target_argfull, DType.MakeConstructed(php_template.ArgFullInfo, template.Type as ConstructedType)); } else { string stubs_key = null; MethodAttributes attrs; if (sre_bug_workaround) { // check whether we have generated a stub having the template name before if (stubs.ContainsKey(stubs_key = "," + php_template.ArgFullInfo.Name)) return; attrs = php_template.ArgFullInfo.Attributes & ~MethodAttributes.Abstract; } else { attrs = MethodAttributes.PrivateScope | MethodAttributes.Virtual; } if (newSlot) attrs |= MethodAttributes.NewSlot; else attrs &= ~MethodAttributes.NewSlot; // determine stub return and parameters type Type return_type; Type[] param_types = php_template.Signature.ToArgfullSignature(1, out return_type); param_types[0] = Types.ScriptContext[0]; MethodBuilder override_stub = type_builder.DefineMethod( (sre_bug_workaround ? php_template.ArgFullInfo.Name : "<Override>"), attrs, return_type, param_types); ILEmitter il = new ILEmitter(override_stub); // // return target( [arg1, ...[, default, ...]] ); // // pass-thru all arguments, including this (arg0) int pass_args = Math.Min(param_types.Length, target.Signature.ParamCount + 1); for (int i = 0; i <= pass_args; ++i) il.Ldarg(i); // this, param1, .... for (int i = pass_args; i <= target.Signature.ParamCount; ++i) { // ... // PhpException.MissingArgument(i, target.FullName); // but in some override it can be optional argument il.Emit(OpCodes.Ldsfld, PHP.Core.Emit.Fields.Arg_Default); // paramN } il.Emit(OpCodes.Callvirt, target_argfull); // return if (target.Signature.AliasReturn != php_template.Signature.AliasReturn) il.Emit(OpCodes.Call, target.Signature.AliasReturn ? Methods.PhpVariable.Dereference // PhpVariable.Deference(obj) : Methods.PhpVariable.MakeReference); // PhpVariable.MakeReference(obj) il.Emit(OpCodes.Ret); if (sre_bug_workaround) { stubs.Add(stubs_key, override_stub); } else { if (!php_template.ArgFullInfo.IsVirtual) throw new InvalidOperationException(string.Format("Cannot override non-virtual method '{0}'!", php_template.ArgFullInfo.Name)); type_builder.DefineMethodOverride(override_stub, DType.MakeConstructed(php_template.ArgFullInfo, template.Type as ConstructedType)); } } } }
public void EmitFieldExportSetter(PhpField/*!*/ field, PropertyBuilder/*!*/ property, MethodBuilder/*!*/ setter) { IPlace instance_place = (field.IsStatic ? null : IndexedPlace.ThisArg); EmissionContext emission_context = SetupStubPlaces(field.DeclaringPhpType, setter.IsStatic); il = new ILEmitter(setter); try { // prepare the field for writing AssignmentCallback callback = field.EmitSet(this, instance_place, false, null, false); // load and convert the argument il.Ldarg(setter.IsStatic ? 0 : 1); PhpTypeCode type_code = ClrOverloadBuilder.EmitConvertToPhp( il, property.PropertyType/*, ScriptContextPlace*/); EmitBoxing(type_code); // write the field callback(this, PhpTypeCode.Object); il.Emit(OpCodes.Ret); } finally { RestorePlaces(emission_context); } }
/// <summary> /// Emits stub for one overridden/implemented/exported CLR overload. /// </summary> /// <param name="il"></param> /// <param name="scriptContextPlace"></param> /// <param name="stubParameters">The overload parameters.</param> /// <param name="stubTypeParameters">The overload type parameters.</param> /// <param name="stubReturnType">The overload return type.</param> /// <param name="target">The overriding/implementing/exporting method.</param> /// <param name="targetType">The type (perhaps constructed) that declared <paramref name="target"/>.</param> public static void EmitMethodStubBody(ILEmitter/*!*/ il, IPlace/*!*/ scriptContextPlace, ParameterInfo[]/*!*/ stubParameters, Type[]/*!*/ stubTypeParameters, Type/*!*/ stubReturnType, PhpMethod/*!*/ target, DType/*!*/ targetType) { bool stub_is_static = il.MethodBase.IsStatic; ClrStubBuilder stub_builder = new ClrStubBuilder(il, scriptContextPlace, stubParameters.Length, (stub_is_static ? 0 : 1)); if (stubParameters.Length >= target.Signature.MandatoryParamCount && stubTypeParameters.Length >= target.Signature.MandatoryGenericParamCount && (target.Properties & RoutineProperties.IsArgsAware) == 0) { // we can directly call the target argful if (!stub_is_static) il.Ldarg(FunctionBuilder.ArgThis); scriptContextPlace.EmitLoad(il); stub_builder.EmitLoadArgfullParameters(stubParameters, stubTypeParameters, target); // invoke the target (virtually if it's not static) il.Emit(stub_is_static ? OpCodes.Call : OpCodes.Callvirt, DType.MakeConstructed(target.ArgFullInfo, targetType as ConstructedType)); } else { // we have to take the argless way stub_builder.EmitLoadArglessParameters(stubParameters, stubTypeParameters, target); // invoke the target's argless // TODO: this is not behaving 100% correct, because we're losing virtual dispatch here if (stub_is_static) il.Emit(OpCodes.Ldnull); else il.Ldarg(FunctionBuilder.ArgThis); scriptContextPlace.EmitLoad(il); il.Emit(OpCodes.Ldfld, Fields.ScriptContext_Stack); il.Emit(OpCodes.Call, DType.MakeConstructed(target.ArgLessInfo, targetType as ConstructedType)); } // do not keep it on stack needlessly if (stubReturnType == Types.Void) il.Emit(OpCodes.Pop); // convert ref/out parameters back to CLR type for (int i = 0; i < stubParameters.Length; i++) { stub_builder.EmitStoreClrParameter(stubParameters[i]); } if (stubReturnType != Types.Void) { // convert the return parameter back to CLR type stub_builder.EmitConvertReturnValue( stubReturnType, target.Signature.AliasReturn ? PhpTypeCode.PhpReference : PhpTypeCode.Object); } il.Emit(OpCodes.Ret); }
/// <summary> /// Define a non-virtual intermediate method that performs the (non-virtual) argfull call. /// </summary> private MethodInfo/*!*/ BuildNonVirtualMediator() { Type ret_type; Type[] arg_types = signature.ToArgfullSignature(1, out ret_type); arg_types[0] = Types.ScriptContext[0]; MethodInfo mediator = DefineRealMethod( "<Mediator>", MethodAttributes.PrivateScope | MethodAttributes.SpecialName, ret_type, arg_types); // just delegate the call to the real argfull ILEmitter il = new ILEmitter(mediator); for (int i = 0; i <= arg_types.Length; i++) il.Ldarg(i); il.Emit(OpCodes.Call, argfull); il.Emit(OpCodes.Ret); return mediator; }
/// <summary> /// Create static <see cref="DynamicMethod"/> that wraps call of given <paramref name="mi"/>. The call is performed statically, method's overrides are not called. /// </summary> /// <param name="mi"><see cref="MethodInfo"/> to be called statically.</param> /// <returns>New <see cref="MethodInfo"/> representing static method stub.</returns> public static MethodInfo/*!*/WrapInstanceMethodCall(MethodInfo/*!*/mi) { Debug.Assert(mi != null); Debug.Assert(!mi.IsStatic, "'mi' must not be static!"); var parameters = mi.GetParameters(); // array of parameters type // Type[]{ <DeclaringType>, <arg1.Type>, ..., <argn.Type> } var paramTypes = new Type[parameters.Length + 1]; // = new Type[]{ mi.DeclaringType }.Concat(parameters.Select<ParameterInfo, Type>(p => p.ParameterType)).ToArray(); paramTypes[0] = mi.DeclaringType; for (int i = 0; i < parameters.Length; i++) paramTypes[i + 1] = parameters[i].ParameterType; // create static dynamic method that calls given MethodInfo statically DynamicMethod stub = new DynamicMethod(mi.Name + "_", mi.ReturnType, paramTypes, mi.DeclaringType); ILEmitter il = new ILEmitter(stub); // return <mi>( instance, arg_1, arg_2, ..., arg_n ): for (int i = 0; i <= parameters.Length; i++) il.Ldarg(i); il.Emit(OpCodes.Call, mi); il.Emit(OpCodes.Ret); // return stub; }
/// <summary> /// Emits code that loads the value from this storage place. /// </summary> /// <param name="il">The <see cref="ILEmitter"/> to emit the code to.</param> public void EmitLoad(ILEmitter il) { switch (holder) { case PlaceHolder.Local: il.Ldloc(index); break; case PlaceHolder.Argument: il.Ldarg(index); break; case PlaceHolder.None: il.LdcI4(index); break; } }