/// <summary> /// Called when a <see cref="PHP.Core.AST.FieldDecl"/> AST node is visited during the emit phase. /// </summary> public void InitializeField(PhpField/*!*/ field, AST.Expression initVal) { ILEmitter cil; IPlace sc_place; if (field.IsStatic) { // (J) even overiding static field is created again in derivating type // there is no initialization taking place if the implementing CLI field does not live in current class //if (field.Overrides != null) return; if (field.IsAppStatic) { // app-static field initialization is emitted into the static ctor cil = field.DeclaringPhpType.Builder.StaticCtorEmitter; sc_place = new LazyLoadSCPlace(); } else { // thread-static field initialization is emitted into the __InitializeStaticFields method cil = new ILEmitter(field.DeclaringPhpType.StaticFieldInitMethodBuilder); sc_place = new IndexedPlace(PlaceHolder.Argument, ScriptBuilder.ArgContext); } } else { if (initVal == null && field.Implementor != field.DeclaringType) return; // instance field initialization is emitted into the <InitializeInstanceFields> method cil = field.DeclaringPhpType.Builder.InstanceFieldInitEmitter; sc_place = new IndexedPlace(PlaceHolder.Argument, FunctionBuilder.ArgContextInstance); cil.Ldarg(FunctionBuilder.ArgThis); } if (initVal != null) { // emit the expression evaluating code ILEmitter old_il = il; IPlace old_sc_place = ScriptContextPlace; try { // set il and SC-emitter appropriately il = cil; ScriptContextPlace = sc_place; EmitBoxing(initVal.Emit(this)); } finally { // restore the saved il and SC-emitter il = old_il; ScriptContextPlace = old_sc_place; } cil.Emit(OpCodes.Newobj, Constructors.PhpSmartReference.Object); } else cil.Emit(OpCodes.Newobj, Constructors.PhpSmartReference.Void); // store it in the field Debug.Assert(field.IsStatic == field.RealField.IsStatic); cil.Emit(field.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, field.RealField); }
public PhpFieldBuilder(PhpField/*!*/ field) { this.field = field; }
/// <summary> /// Emits property stubs for a overriden or implemented CLR property. /// </summary> /// <param name="stubs">Already generated stubs.</param> /// <param name="target">The overriding/implementing field.</param> /// <param name="declaringType">The type where the stubs should be emitted.</param> /// <param name="template">The property being overriden/implemented.</param> /// <param name="newSlot"><B>True</B> if the stub should be assigned a new vtable slot, /// <B>false</B> otherwise.</param> private void EmitOverrideStubs(IDictionary<Type, PropertyBuilder>/*!*/ stubs, PhpField/*!*/ target, PhpType/*!*/ declaringType, DMemberRef/*!*/ template, bool newSlot) { ClrProperty clr_template = template.Member as ClrProperty; if (clr_template == null) return; MethodInfo getter = clr_template.Getter; MethodInfo setter = clr_template.Setter; // we're only interested in non-final virtual getters/setters if (getter != null && (!getter.IsVirtual || getter.IsFinal)) getter = null; if (setter != null && (!setter.IsVirtual || setter.IsFinal)) setter = null; ConstructedType constructed_type = template.Type as ConstructedType; // map property type according to constructed type Type property_type = clr_template.RealProperty.PropertyType; if (constructed_type != null) property_type = constructed_type.MapRealType(property_type); // do we already have getter/setter of this type? PropertyBuilder prop_builder; if (stubs.TryGetValue(property_type, out prop_builder)) { if (prop_builder.GetGetMethod(true) != null) getter = null; if (prop_builder.GetSetMethod(true) != null) setter = null; } if (getter != null || setter != null) { if (prop_builder == null) { // the property might already exist - we could be just adding an accessor TypeBuilder type_builder = declaringType.RealTypeBuilder; prop_builder = type_builder.DefineProperty( clr_template.Name.ToString(), Reflection.Enums.ToPropertyAttributes(target.MemberDesc.MemberAttributes), property_type, Type.EmptyTypes); stubs.Add(property_type, prop_builder); } if (getter != null) { // add getter MethodBuilder getter_builder = DefineOverrideAccessor( declaringType, target, getter, newSlot, property_type, Type.EmptyTypes); prop_builder.SetGetMethod(getter_builder); EmitFieldExportGetter(target, prop_builder, getter_builder); } if (setter != null) { // add setter MethodBuilder setter_builder = DefineOverrideAccessor( declaringType, target, setter, newSlot, Types.Void, new Type[] { property_type }); prop_builder.SetSetMethod(setter_builder); EmitFieldExportSetter(target, prop_builder, setter_builder); } } }
/// <summary> /// Defines a property accessor method and installs an explicit override if necessary. /// </summary> private MethodBuilder/*!*/ DefineOverrideAccessor(PhpType/*!*/ declaringType, PhpField/*!*/ target, MethodInfo/*!*/ template, bool newSlot, Type/*!*/ returnType, Type[]/*!!*/ paramTypes) { bool changed; string name = ClrStubBuilder.GetNonConflictingMethodName(declaringType.TypeDesc, template.Name, out changed); MethodAttributes attr; if (changed) attr = MethodAttributes.PrivateScope; else attr = Reflection.Enums.ToMethodAttributes(target.MemberDesc.MemberAttributes); attr |= (MethodAttributes.Virtual | MethodAttributes.HideBySig); if (newSlot) attr |= MethodAttributes.NewSlot; MethodBuilder method_builder = declaringType.RealTypeBuilder.DefineMethod( name, attr, returnType, paramTypes); if (changed) { declaringType.RealTypeBuilder.DefineMethodOverride( method_builder, template); } return method_builder; }
public void EmitFieldExportStubs(PhpField/*!*/ field, PropertyBuilder/*!*/ property) { Debug.Assert(field != null && property != null); MethodBuilder getter = (MethodBuilder)property.GetGetMethod(true); MethodBuilder setter = (MethodBuilder)property.GetSetMethod(true); // emit getter: if (getter != null) EmitFieldExportGetter(field, property, getter); // emit setter: if (setter != null) EmitFieldExportSetter(field, property, setter); }
/// <summary> /// Emits stubs for overriden/implemented properties and explicit export stubs. /// </summary> /// <param name="field">The overriding/implementing/exported field.</param> public void EmitOverrideAndExportStubs(PhpField/*!*/ field) { // keep track of property types that have already been generated Dictionary<Type, PropertyBuilder> stubs = null; // emit explicit export stub // (note: the property builder is already defined - needed for custom attributes usage) if (field.IsExported) { Debug.Assert(field.ExportedPropertyBuilder != null); PropertyBuilder prop_builder = field.ExportedPropertyBuilder; EmitFieldExportStubs(field, prop_builder); stubs = new Dictionary<Type, PropertyBuilder>(); stubs.Add(prop_builder.PropertyType, prop_builder); } // emit stubs for overriden property if (field.Overrides != null) { if (stubs == null) stubs = new Dictionary<Type, PropertyBuilder>(); EmitOverrideStubs(stubs, field, field.DeclaringPhpType, field.Overrides, false); } if (field.Implements != null) { // emit stubs(s) for implemented property/ies for (int i = 0; i < field.Implements.Count; i++) { if (stubs == null) stubs = new Dictionary<Type, PropertyBuilder>(); EmitOverrideStubs(stubs, field, field.DeclaringPhpType, field.Implements[i], true); } } }
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); } }
public void EmitFieldExportGetter(PhpField/*!*/ field, PropertyBuilder/*!*/ property, MethodBuilder/*!*/ getter) { IPlace instance_place = (field.IsStatic ? null : IndexedPlace.ThisArg); EmissionContext emission_context = SetupStubPlaces(field.DeclaringPhpType, getter.IsStatic); il = new ILEmitter(getter); try { // read the field PhpTypeCode type_code = field.EmitGet(this, instance_place, false, null, false); // convert it to the return type ClrOverloadBuilder.EmitConvertToClr( il, type_code, property.PropertyType); il.Emit(OpCodes.Ret); } finally { RestorePlaces(emission_context); } }
/// <summary> /// I do not like PHP-specific access code emission here. TODO: Move to PhpField /// </summary> /// <param name="phpField"></param> /// <param name="ensureArray"></param> void EmitEnsureStaticPhpFieldDirect(PhpField phpField, bool ensureArray) { ILEmitter il = codeGenerator.IL; MethodInfo static_init_info = ((PhpType)phpField.Implementor).StaticFieldInitMethodInfo; // ensure that the field has been initialized for this request by invoking __InitializeStaticFields if (static_init_info != null && !il.IsFeatureControlFlowPrecedent(phpField.Implementor)) { codeGenerator.EmitLoadScriptContext(); il.Emit(OpCodes.Call, static_init_info); // remember that we have just initialized class_entry's static fields il.MarkFeature(phpField.Implementor); } // LOAD EnsureVariableIs[Array|Object](ref <field>.value,[<context>]); il.Emit(OpCodes.Ldsfld, phpField.RealField); il.Emit(OpCodes.Ldflda, Fields.PhpReference_Value); if (ensureArray) { il.Emit(OpCodes.Call, Methods.Operators.EnsureVariableIsArray); } else { codeGenerator.EmitLoadScriptContext(); il.Emit(OpCodes.Call, Methods.Operators.EnsureVariableIsObject); } }
/// <summary> /// I do not like PHP-specific access code emission here. TODO: Move to PhpField /// </summary> /// <param name="field"></param> /// <param name="fieldName"></param> /// <param name="ensureArray"></param> private void EmitEnsurePhpFieldDirect(PhpField/*!*/ field, SimpleVarUse/*!*/ fieldName, bool ensureArray) { ILEmitter il = codeGenerator.IL; // check whether the field is set il.Ldarg(FunctionBuilder.ArgThis); il.Emit(OpCodes.Ldfld, field.RealField); Label direct_ensure = il.DefineLabel(); Label ensuring_over = il.DefineLabel(); // test whether it is set il.Emit(OpCodes.Callvirt, Properties.PhpReference_IsSet.GetGetMethod()); il.Emit(OpCodes.Brtrue, direct_ensure); // the field has been unset -> must call operator that handles __get/__set if (ensureArray) this.Lengthen(); // TODO: ??? codeGenerator.EmitLoadSelf(); fieldName.EmitName(codeGenerator); codeGenerator.EmitLoadClassContext(); if (ensureArray) { il.Emit(OpCodes.Call, Methods.Operators.EnsurePropertyIsArray); } else { codeGenerator.EmitLoadScriptContext(); il.Emit(OpCodes.Call, Methods.Operators.EnsurePropertyIsObject); } il.Emit(OpCodes.Br, ensuring_over); // read the field again and call EnsureVariableIsArray il.MarkLabel(direct_ensure, true); il.Ldarg(FunctionBuilder.ArgThis); il.Emit(OpCodes.Ldfld, field.RealField); il.Emit(OpCodes.Ldflda, Fields.PhpReference_Value); if (ensureArray) { il.Emit(OpCodes.Call, Methods.Operators.EnsureVariableIsArray); } else { codeGenerator.EmitLoadScriptContext(); il.Emit(OpCodes.Call, Methods.Operators.EnsureVariableIsObject); } il.MarkLabel(ensuring_over, true); EmitErrorCheck(ensureArray); }
internal void AnalyzeMember(Analyzer/*!*/ analyzer, PhpField/*!*/ field) { this.field = field; }