public static void EmitAddFrame(ILEmitter/*!*/ il, IPlace/*!*/ scriptContextPlace, int typeArgCount, int argCount, Action<ILEmitter, int> typeArgEmitter, Action<ILEmitter, int>/*!*/ argEmitter) { Debug.Assert(typeArgCount == 0 || typeArgEmitter != null); // type args: if (typeArgCount > 0) { scriptContextPlace.EmitLoad(il); il.Emit(OpCodes.Ldfld, Fields.ScriptContext_Stack); il.EmitOverloadedArgs(Types.DTypeDesc[0], typeArgCount, Methods.PhpStack.AddTypeFrame.ExplicitOverloads, typeArgEmitter); } // args: scriptContextPlace.EmitLoad(il); il.Emit(OpCodes.Ldfld, Fields.ScriptContext_Stack); il.EmitOverloadedArgs(Types.Object[0], argCount, Methods.PhpStack.AddFrame.ExplicitOverloads, argEmitter); il.Emit(OpCodes.Call, Methods.PhpStack.AddFrame.Overload(argCount)); // AddFrame adds empty type frame by default, so if there are no type parameters, we can skip AddTypeFrame call: if (typeArgCount > 0) il.Emit(OpCodes.Call, Methods.PhpStack.AddTypeFrame.Overload(typeArgCount)); }
public static void EmitArgFullPostCall(ILEmitter/*!*/ il, IPlace/*!*/ stack, LocalBuilder locArgsCount) { // args-aware: if (locArgsCount != null) { // CALL stack.RemoveArgsAwareFrame(count); stack.EmitLoad(il); il.Ldloc(locArgsCount); il.Emit(OpCodes.Call, Methods.PhpStack.RemoveArgsAwareFrame); } }
public static void EmitArgFullPreCall(ILEmitter/*!*/ il, IPlace/*!*/ stack, bool argsAware, int formalParamCount, int formalTypeParamCount, out LocalBuilder locArgsCount) { if (argsAware) { locArgsCount = il.DeclareLocal(typeof(int)); // locArgsCount = stack.MakeArgsAware(<formal tpye param count | formal param count>); stack.EmitLoad(il); il.LdcI4((formalTypeParamCount << 16) | formalParamCount); il.Emit(OpCodes.Call, Methods.PhpStack.MakeArgsAware); il.Stloc(locArgsCount); } else { locArgsCount = null; // CALL stack.RemoveFrame(); stack.EmitLoad(il); il.Emit(OpCodes.Call, Methods.PhpStack.RemoveFrame); } }
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; }
internal void EmitHelpers() { CompilationUnit unit = this.CompilationUnit; ILEmitter il = new ILEmitter(DeclareHelperBuilder); IndexedPlace script_context_place = new IndexedPlace(PlaceHolder.Argument, 0); foreach (PhpFunction function in unit.GetDeclaredFunctions()) { if (function.IsDefinite) { CodeGenerator.EmitDeclareFunction(il, script_context_place, function); } } foreach (PhpType type in unit.GetDeclaredTypes()) { if (type.IsDefinite) { // CALL <context>.DeclareType(<type desc>, <name>); type.EmitAutoDeclareOnScriptContext(il, script_context_place); } else if (!type.IsComplete) { if (type.IncompleteClassDeclareMethodInfo != null) { // check whether base class is known at this point of execution, // if so, declare this incomplete class immediately. As PHP does. type.EmitDeclareIncompleteOnScriptContext(il, script_context_place); } } } foreach (GlobalConstant constant in unit.GetDeclaredConstants()) { if (constant.IsDefinite) { var field = constant.RealField; Debug.Assert(field != null); Debug.Assert(field.IsStatic); // CALL <context>.DeclareConstant(<name>, <value>); script_context_place.EmitLoad(il); il.Emit(OpCodes.Ldstr, constant.FullName); il.LoadLiteralBox(constant.Value); //il.Emit(OpCodes.Ldsfld, field); // const field cannot be referenced in IL il.Emit(OpCodes.Call, Methods.ScriptContext.DeclareConstant); } } il.Emit(OpCodes.Ret); }
/// <summary> /// Emit LOAD <paramref name="instance"/>. /// </summary>ILEmiter /// <param name="il"><see cref="ILEmitter"/> object instance.</param> /// <param name="instance">The place where to load the instance from.</param> /// <param name="declaringType">The type of resulting instance.</param> /// <remarks>Instance of value types are wrapped in <see cref="ClrValue<T>"/> object instance.</remarks> internal static void EmitLoadInstance(ILEmitter/*!*/il, IPlace/*!*/instance, Type/*!*/declaringType) { Debug.Assert(il != null && instance != null && declaringType != null, "ClrOverloadBuilder.EmitLoadInstance() null argument!"); // LOAD <instance> instance.EmitLoad(il); if (declaringType.IsValueType) { var clrValueType = ClrObject.valueTypesCache.Get(declaringType).Item1; Debug.Assert(clrValueType != null, "Specific ClrValue<T> not found!"); // CAST (ClrValue<T>) il.Emit(OpCodes.Castclass, clrValueType); // LOAD .realValue var realValueField = clrValueType.GetField("realValue"); Debug.Assert(realValueField != null, "ClrValue<T>.realValue field not found!"); il.Emit(OpCodes.Ldflda, clrValueType.GetField("realValue")); } else { // CAST (T) il.Emit(OpCodes.Castclass, declaringType); } }
/// <summary> /// Converts a value of the given CLR type to PHP value. /// </summary> internal static PhpTypeCode EmitConvertToPhp(ILEmitter/*!*/ il, Type/*!*/ type) { // box generic parameter if (type.IsGenericParameter) { il.Emit(OpCodes.Box, type); type = Types.Object[0]; } switch (Type.GetTypeCode(type)) { // primitives: case TypeCode.Boolean: return PhpTypeCode.Boolean; case TypeCode.Int32: return PhpTypeCode.Integer; case TypeCode.Int64: return PhpTypeCode.LongInteger; case TypeCode.Double: return PhpTypeCode.Double; case TypeCode.String: return PhpTypeCode.String; // coercion: case TypeCode.SByte: case TypeCode.Int16: case TypeCode.Byte: case TypeCode.UInt16: { il.Emit(OpCodes.Conv_I4); return PhpTypeCode.Integer; } case TypeCode.UInt32: EmitConstrainedCoercion(il, typeof(int), typeof(long), Int32.MaxValue); return PhpTypeCode.Object; case TypeCode.UInt64: EmitConstrainedCoercion(il, typeof(int), typeof(long), Int32.MaxValue); return PhpTypeCode.Object; case TypeCode.Single: il.Emit(OpCodes.Conv_R8); return PhpTypeCode.Double; case TypeCode.Char: il.Emit(OpCodes.Box, type); il.Emit(OpCodes.Callvirt, Methods.Object_ToString); return PhpTypeCode.String; case TypeCode.DBNull: { il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ldnull); return PhpTypeCode.Object; } case TypeCode.Decimal: // TODO: what to do with this guy? case TypeCode.DateTime: { il.Emit(OpCodes.Box, type); il.Emit(OpCodes.Call, Methods.ClrObject_Wrap); return PhpTypeCode.DObject; } case TypeCode.Object: { if (!typeof(IPhpVariable).IsAssignableFrom(type)) { if (type.IsValueType) il.Emit(OpCodes.Box, type); il.Emit(OpCodes.Call, Methods.ClrObject_WrapDynamic); return PhpTypeCode.Object; } else return PhpTypeCodeEnum.FromType(type); } default: { Debug.Fail(); return PhpTypeCode.Invalid; } } }
/// <summary> /// Converts a PHP value to the given CLR type (the caller passes a <paramref name="strictnessLocal"/> that will /// receive one of the <see cref="PHP.Core.ConvertToClr.ConversionStrictness"/> enumeration values that /// describe the conversion result (the Failed value indicates that conversion was not successful). /// </summary> /// <returns><B>True</B> if it the conversion will surely succeed.</returns> internal static bool EmitConvertToClr(ILEmitter/*!*/ il, PhpTypeCode typeCode, Type/*!*/ formalType, LocalBuilder/*!*/ strictnessLocal) { Debug.Assert(strictnessLocal.LocalType == typeof(ConversionStrictness)); // preprocess the value according to the PHP type code switch (typeCode) { case PhpTypeCode.PhpReference: { // dereference il.Emit(OpCodes.Ldfld, Fields.PhpReference_Value); typeCode = PhpTypeCode.Object; break; } case PhpTypeCode.ObjectAddress: { // dereference il.Emit(OpCodes.Ldind_Ref); typeCode = PhpTypeCode.Object; break; } case PhpTypeCode.LinqSource: case PhpTypeCode.PhpRuntimeChain: { Debug.Fail(); return true; } } // special treatment for generic parameters if (formalType.IsGenericParameter) { EmitConvertToClrGeneric(il, formalType, strictnessLocal); return false; } // convert CLR type return EmitConvertObjectToClr(il, typeCode, formalType, strictnessLocal); }
protected override void EmitEntryPoint(MethodBuilder/*!*/ methodBuilder) { ScriptBuilder script_builder = GetEntryScriptBuilder(); Debug.Assert(script_builder.CompilationUnit is ScriptCompilationUnit); if (script_builder == null) throw new InvalidOperationException(CoreResources.GetString("entrypoint_not_specified")); PhpSourceFile entry_file = ((ScriptCompilationUnit)script_builder.CompilationUnit).SourceUnit.SourceFile; ILEmitter il = new ILEmitter(methodBuilder); // LOAD new PhpScript.MainHelperDelegate(Default.Main); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ldftn, script_builder.MainHelper); il.Emit(OpCodes.Newobj, Constructors.MainHelperDelegate); // LOAD <source name> il.Emit(OpCodes.Ldstr, entry_file.RelativePath.ToString()); // LOAD Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) il.Emit(OpCodes.Call, Methods.Assembly.GetEntryAssembly); il.Emit(OpCodes.Callvirt, Properties.Assembly_Location.GetGetMethod()); il.Emit(OpCodes.Call, Methods.Path.GetDirectoryName); // ScriptContext.RunApplication(<main helper delegate>, <source name>, <entry assembly directory> ); il.Emit(OpCodes.Call, Methods.ScriptContext.RunApplication); // RETURN; il.Emit(OpCodes.Ret); }
/// <summary> /// Emit <see cref="PhpVariable.Copy"/> if needed. It means <see cref="Expression.Access"/> has to be <see cref="AccessType.Read"/> and <paramref name="returnType"/> has to be copiable. /// </summary> /// <param name="il">The <see cref="ILEmitter"/>.</param> /// <param name="returnType"><see cref="PhpTypeCode"/> of function call return value.</param> protected void EmitReturnValueCopy(ILEmitter/*!*/il, PhpTypeCode returnType) { Debug.Assert(il != null); // copy only if we are reading the return value && // only if return type is copiable: if (access != AccessType.None && // reading, not literals: PhpTypeCodeEnum.IsDeeplyCopied(returnType) && returnType != PhpTypeCode.PhpReference) // PhpSmartReference can be an issue if method returns an object field (but this is handled by binders) { il.LdcI4((int)CopyReason.ReturnedByCopy); il.Emit(OpCodes.Call, Methods.PhpVariable.Copy); } }
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 load of <see cref="MethodInfo"/> onto the top of evaluation stack. /// </summary> /// <param name="il"></param> /// <param name="mi"></param> internal static void EmitLoadMethodInfo(ILEmitter/*!*/il, MethodInfo/*!*/mi/*, DelegateBuilder dbuild*/) { if (mi == null) throw new ArgumentNullException("mi"); if (!mi.IsStatic) throw new NotSupportedException(); // following code uses hack, where we can create delegate in "compile time", and then takes its MethodInfo property. // new Func<...>( null, <mi> ).Method //// construct the type ////var miArgs = mi.GetParameters(); // THIS FAILS WHEN <mi> IS NOT BAKED YET ////Type[] delegateArgs = new Type[1 + miArgs.Length]; ////delegateArgs[0] = mi.ReturnType; ////for (int i = 0; i < miArgs.Length; i++) delegateArgs[i + 1] = miArgs[i].ParameterType; //var delegateCtor = DelegateBuilder.GetDelegateCtor(dbuild.GetDelegateType(delegateArgs, il.GetNextUniqueIndex())); var delegateCtor = DelegateBuilder.GetDelegateCtor(Types.Action[0]); // NOT NICE //.ldnull //.ldftn <mi> //.newobj instance void Action::.ctor(object, native int) //.call get_Method il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ldftn, mi); il.Emit(OpCodes.Newobj, delegateCtor); il.Emit(OpCodes.Call, Properties.Delegate_Method.GetGetMethod()); }
/// <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); }
internal void EmitPhpException(ILEmitter/*!*/ il, MethodInfo/*!*/ method) { // emits call to a method which reports an error: il.Emit(OpCodes.Call, method); // emits nop which makes sequence points working well in stack trace: if (context.Config.Compiler.Debug) il.Emit(OpCodes.Nop); }
/// <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)); } } } }
private void EmitLoadAccessorDelegate(ILEmitter/*!*/ il, ConstructorInfo/*!*/ delegateCtor, IPlace instance, bool dynamicStub, MethodInfo accessor) { if (accessor != null) { if (instance != null) instance.EmitLoad(il); else il.Emit(OpCodes.Ldnull); if (!dynamicStub && accessor.IsVirtual) { instance.EmitLoad(il); il.Emit(OpCodes.Ldvirtftn, accessor); } else il.Emit(OpCodes.Ldftn, accessor); il.Emit(OpCodes.Newobj, delegateCtor); } else il.Emit(OpCodes.Ldnull); }
protected virtual SetterDelegate/*!*/ GenerateSetterStub() { if (Member is ClrEvent) return new SetterDelegate(EventSetter); #if SILVERLIGHT DynamicMethod stub = new DynamicMethod("<^SetterStub>", Types.Void, Types.Object_Object); /*DynamicMethod stub = new DynamicMethod("<^SetterStub>", PhpFunctionUtils.DynamicStubAttributes, CallingConventions.Standard, Types.Void, Types.Object_Object, this.declaringType.RealType, true);*/ #else DynamicMethod stub = new DynamicMethod("<^SetterStub>", PhpFunctionUtils.DynamicStubAttributes, CallingConventions.Standard, Types.Void, Types.Object_Object, this.declaringType.RealType, true); #endif #if DEBUG_DYNAMIC_STUBS // Debugging - save the generated stub to TEMP AssemblyName name = new AssemblyName("SetterStub_" + Property.FullName.ToString().Replace(':', '_')); AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Save, "C:\\Temp"); ModuleBuilder mb = ab.DefineDynamicModule(name.Name, name.Name + ".dll"); TypeBuilder tb = mb.DefineType("Stub"); MethodBuilder meb = tb.DefineMethod(DeclaringType.ToString() + "::" + Property.FullName, MethodAttributes.PrivateScope | MethodAttributes.Static, Types.Void, Types.Object_Object); ILEmitter il_dbg = new ILEmitter(meb); IndexedPlace instance2 = new IndexedPlace(PlaceHolder.Argument, 0); IndexedPlace stack = new IndexedPlace(PlaceHolder.Argument, 1); ClrProperty clr_property_dbg = Member as ClrProperty; if (clr_property_dbg != null && clr_property_dbg.HasSetter) { if (!clr_property_dbg.Setter.IsStatic) { il_dbg.Emit(OpCodes.Ldarg_0); if (declaringType.RealType.IsValueType) il_dbg.Emit(OpCodes.Unbox, declaringType.RealType); #if EMIT_VERIFIABLE_STUBS else il_dbg.Emit(OpCodes.Castclass, declaringType.RealType); #endif } il_dbg.Emit(OpCodes.Ldarg_1); EmitSetConversion(il_dbg, PhpTypeCode.Object, clr_property_dbg.Setter.GetParameters()[0].ParameterType); il_dbg.Emit(OpCodes.Call, clr_property_dbg.Setter); } il_dbg.Emit(OpCodes.Ret); tb.CreateType(); ab.Save("SetterStub_" + Property.FullName.ToString().Replace(':', '_') + ".dll"); #endif ILEmitter il = new ILEmitter(stub); ClrProperty clr_property = Member as ClrProperty; Type arg_type; if (clr_property != null) { // return error-throwing setter if the property is read-only if (!clr_property.HasSetter) return new SetterDelegate(MissingSetter); if (!clr_property.Setter.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, declaringType.RealType); //#endif } il.Emit(OpCodes.Ldarg_1); arg_type = clr_property.Setter.GetParameters()[0].ParameterType; EmitSetConversion(il, PhpTypeCode.Object, arg_type); il.Emit(OpCodes.Call, clr_property.Setter); } else { ClrField clr_field = ClrField; // return error-throwing setter if the field is initonly if (clr_field.FieldInfo.IsInitOnly) return new SetterDelegate(MissingSetter); 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.Ldarg_1); arg_type = clr_field.FieldInfo.FieldType; EmitSetConversion(il, PhpTypeCode.Object, arg_type); il.Emit((clr_field.FieldInfo.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld), clr_field.FieldInfo); } il.Emit(OpCodes.Ret); return (SetterDelegate)stub.CreateDelegate(typeof(SetterDelegate)); }
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); }
protected override void EmitEntryPoint(MethodBuilder/*!*/ builder) { PureCompilationUnit unit = PureModuleBuilder.PureCompilationUnit; Debug.Assert(unit.EntryPoint != null); ILEmitter il = new ILEmitter(builder); // LOAD new RoutineDelegate(<main PHP method>); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ldftn, unit.EntryPoint.ArgLessInfo); il.Emit(OpCodes.Newobj, Constructors.RoutineDelegate); // ScriptContext.RunApplication(<main helper delegate>, null, null); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Call, Methods.ScriptContext.RunApplication); // RETURN; 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)); }
/// <summary> /// Converts a PHP value to the given CLR type that is a generic parameter. /// </summary> private static void EmitConvertToClrGeneric(ILEmitter/*!*/ il, Type/*!*/ formalType, LocalBuilder/*!*/ strictnessLocal) { Debug.Assert(formalType.IsGenericParameter); // f...ing GenericTypeParameterBuilder will not allow us to read its attributes and constraints :( if (!(formalType is GenericTypeParameterBuilder)) { GenericParameterAttributes attrs = formalType.GenericParameterAttributes; if (Reflection.Enums.GenericParameterAttrTest(attrs, GenericParameterAttributes.NotNullableValueTypeConstraint)) { // we know that we are converting to a value type il.Ldloca(strictnessLocal); il.Emit(OpCodes.Call, Methods.ConvertToClr.TryObjectToStruct.MakeGenericMethod(formalType)); return; } Type[] constraints = formalType.GetGenericParameterConstraints(); for (int i = 0; i < constraints.Length; i++) { if (constraints[i].IsClass) { if (!constraints[i].IsArray && !typeof(Delegate).IsAssignableFrom(constraints[i])) { // we know that we are converting to a class that is not an array nor a delegate il.Ldloca(strictnessLocal); il.Emit(OpCodes.Call, Methods.ConvertToClr.TryObjectToClass.MakeGenericMethod(formalType)); return; } else break; } } } // postpone the conversion to runtime il.Ldloca(strictnessLocal); il.Emit(OpCodes.Call, Methods.Convert.TryObjectToType.MakeGenericMethod(formalType)); }
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); }
/// <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 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 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); }
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); }
/// <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; }
/// <summary> /// Emit (stack_top dup).IsAliased = true; /// </summary> /// <param name="il"></param> private void EmitIsAliased(ILEmitter/*!*/il) { // set IsAliased to true il.Emit(OpCodes.Dup); il.Emit(OpCodes.Ldc_I4_1); il.EmitCall(OpCodes.Callvirt, Properties.PhpReference_IsAliased.GetSetMethod(), null); }
/// <summary> /// Emits helper declaring all single-declared functions and classes in the script being built. /// </summary> /// <remarks> /// For each function and class emits a call to <see cref="ApplicationContext.DeclareFunction"/> and /// <see cref="ApplicationContext.DeclareType"/>, respectively, which declares it. /// The helper is called as the first instruction of Main helper. /// </remarks> private void EmitDeclareHelper() { PureCompilationUnit unit = this.PureCompilationUnit; ILEmitter il = new ILEmitter(declareHelperBuilder); IndexedPlace app_context_place = new IndexedPlace(PlaceHolder.Argument, 0); TypeBuilder publicsContainer = null; // container type for public stubs of global declarations (which are inaccessible from other assemblies) foreach (PhpFunction function in unit.GetDeclaredFunctions()) { if (function.IsDefinite) { app_context_place.EmitLoad(il); // NEW RoutineDelegate(<static method>); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ldftn, function.ArgLessInfo); il.Emit(OpCodes.Newobj, Constructors.RoutineDelegate); // LOAD <full name>; il.Emit(OpCodes.Ldstr, function.FullName); // LOAD <attributes>; il.LdcI4((int)function.MemberDesc.MemberAttributes); // LOAD <argfull> if (function.ArgFullInfo != null) CodeGenerator.EmitLoadMethodInfo( il, (function.ArgFullInfo.DeclaringType != null) ? function.ArgFullInfo : EmitPhpFunctionPublicStub(ref publicsContainer, function) // function.ArgFullInfo is real global method not accessible from other assemblies, must be wrapped /*, AssemblyBuilder.DelegateBuilder*/); else il.Emit(OpCodes.Ldnull); // CALL <application context>.DeclareFunction(<stub>, <name>, <member attributes>, <argfull>) il.Emit(OpCodes.Call, Methods.ApplicationContext.DeclareFunction); } } foreach (PhpType type in unit.GetDeclaredTypes()) { if (type.IsDefinite) { // CALL <application context>.DeclareType(<type desc>, <name>); type.EmitAutoDeclareOnApplicationContext(il, app_context_place); } } foreach (GlobalConstant constant in unit.GetDeclaredConstants()) { if (constant.IsDefinite) { app_context_place.EmitLoad(il); // CALL <application context>.DeclareConstant(<name>, <value>); il.Emit(OpCodes.Ldstr, constant.FullName); //il.Emit(OpCodes.Ldsfld, constant.RealField); //if (constant.RealField.FieldType.IsValueType) il.Emit(OpCodes.Box, constant.RealField.FieldType); il.LoadLiteralBox(constant.Value); il.Emit(OpCodes.Call, Methods.ApplicationContext.DeclareConstant); } } il.Emit(OpCodes.Ret); // complete the publicsContainer type, if created: if (publicsContainer != null) publicsContainer.CreateType(); }
internal void EmitGetEventObject(ILEmitter/*!*/ il, IPlace/*!*/ contextPlace, IPlace instance, bool dynamicStub) { // [ ClrEventObject<handlerType>.Wrap(<SC>, <event name>, <addMethod>, <removeMethod>) ] contextPlace.EmitLoad(il); il.Emit(OpCodes.Ldstr, FullName); ConstructorInfo hook_ctor = typeof(Library.EventClass<>.HookDelegate).MakeGenericType(HandlerType). GetConstructor(Types.DelegateCtorArgs); // create delegates to the add and remove methods EmitLoadAccessorDelegate(il, hook_ctor, instance, dynamicStub, AddMethod); EmitLoadAccessorDelegate(il, hook_ctor, instance, dynamicStub, RemoveMethod); MethodInfo wrap_method = typeof(Library.EventClass<>).MakeGenericType(HandlerType).GetMethod("Wrap"); il.Emit(OpCodes.Call, wrap_method); }