/// <summary> /// Emits deserializing (SerializiationInfo, StreamingContext) constructor. /// </summary> private static void EmitDeserializingConstructor(PhpType /*!*/ phpType) { // (SerializationInfo, StreamingContext) constructor ConstructorBuilder ctor_builder = phpType.DeserializingConstructorBuilder; if (ctor_builder != null) { ILEmitter cil = new ILEmitter(ctor_builder); if (phpType.Base == null) { EmitInvokePhpObjectDeserializingConstructor(cil); } else { phpType.Base.EmitInvokeDeserializationConstructor(cil, phpType, null); } // [ __InitializeStaticFields(context) ] cil.Emit(OpCodes.Call, Methods.ScriptContext.GetCurrentContext); cil.EmitCall(OpCodes.Call, phpType.StaticFieldInitMethodInfo, null); cil.Emit(OpCodes.Ret); } }
/// <summary> /// Emit the PhpType finalizer. The finalizer is emitted only if there is __destruct() function /// and there is no finalizer in any base class already. The finalizer calls this.Dispose() which /// calls __destruct() function directly. /// </summary> /// <param name="phpType"></param> private static void EmitFinalizer(PhpType /*!*/ phpType) { // only if __destruct was now defined in some base class, no need to override existing definition on Finalize DRoutine basedestruct; DRoutineDesc destruct; if ((destruct = phpType.TypeDesc.GetMethod(DObject.SpecialMethodNames.Destruct)) != null && (phpType.Base == null || phpType.Base.GetMethod(DObject.SpecialMethodNames.Destruct, phpType, out basedestruct) == GetMemberResult.NotFound)) { MethodBuilder finalizer_builder = phpType.RealTypeBuilder.DefineMethod("Finalize", MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Family, typeof(void), Type.EmptyTypes); ILEmitter dil = new ILEmitter(finalizer_builder); // exact Finalize() method pattern follows: // try dil.BeginExceptionBlock(); // this.Dispose(false) dil.Emit(OpCodes.Ldarg_0); dil.Emit(OpCodes.Ldc_I4_0); dil.Emit(OpCodes.Callvirt, PHP.Core.Emit.Methods.DObject_Dispose); // finally dil.BeginFinallyBlock(); // Object.Finalize() dil.Emit(OpCodes.Ldarg_0); dil.Emit(OpCodes.Call, PHP.Core.Emit.Methods.Object_Finalize); dil.EndExceptionBlock(); dil.Emit(OpCodes.Ret); } }
/// <summary> /// Defines CLR-friendly constructors based on a PHP "constructor" method. /// </summary> public static void DefineExportedConstructors(PhpType /*!*/ phpType) { phpType.Builder.ClrConstructorStubs = new List <StubInfo>(); PhpMethod ctor = phpType.GetConstructor() as PhpMethod; if (ctor == null) { // the class defines (nor inherits) no constructor -> create a parameter-less CLR constructor ConstructorBuilder ctor_builder = phpType.RealTypeBuilder.DefineConstructor( DefaultConstructorAttributes, CallingConventions.Standard, Type.EmptyTypes); phpType.ClrConstructorInfos = new ConstructorInfo[] { ctor_builder }; phpType.Builder.ClrConstructorStubs.Add( new StubInfo(ctor_builder, new ParameterInfo[0], StubInfo.EmptyGenericParameters, null)); } else if (!ctor.IsAbstract) { Debug.Assert(!ctor.IsStatic && ctor.Signature.GenericParamCount == 0); if (ctor.Builder == null) { // contructor not defined in this class phpType.ClrConstructorInfos = new ConstructorInfo[0]; return; } // infer constructor visibility List <ConstructorInfo> ctor_infos = new List <ConstructorInfo>(); MethodAttributes attr = Reflection.Enums.ToMethodAttributes(ctor.RoutineDesc.MemberAttributes); foreach (StubInfo info in ClrStubBuilder.DefineMethodExportStubs( ctor, attr, true, delegate(string[] genericParamNames, object[] parameterTypes, object returnType) { // accept all overloads return(true); })) { phpType.Builder.ClrConstructorStubs.Add(info); // infos are returned in ascending order w.r.t. parameter count ctor_infos.Add(info.ConstructorBuilder); } phpType.ClrConstructorInfos = ctor_infos.ToArray(); } }
/// <summary> /// Emits init field helpers (<c>__lastContext</c> field, <c><InitializeInstanceFields></c> /// method and <c>__InitializeStaticFields</c> into a class. /// </summary> internal static void EmitInitFieldHelpers(PhpType phpType) { // // <InitializeInstanceFields> // // <InitializeInstanceFields> method - will contain instance field initialization phpType.Builder.InstanceFieldInit = phpType.RealTypeBuilder.DefineMethod( InstanceFieldInitMethodName, #if SILVERLIGHT MethodAttributes.Public | MethodAttributes.HideBySig, #else MethodAttributes.Private | MethodAttributes.HideBySig, #endif CallingConventions.Standard, Types.Void, Types.ScriptContext); phpType.Builder.InstanceFieldInitEmitter = new ILEmitter(phpType.Builder.InstanceFieldInit); // // <InitializeStaticFields> // // <InitializeStaticFields> method has already been defined during the analysis phase - will contain (thread) // static field initialization ILEmitter cil = new ILEmitter(phpType.StaticFieldInitMethodBuilder); if (phpType.Builder.HasThreadStaticFields) { // __lastContext thread-static field - will contain the last SC that inited static fields for this thread FieldBuilder last_context = phpType.RealTypeBuilder.DefineField( "<lastScriptContext>", Types.ScriptContext[0], FieldAttributes.Private | FieldAttributes.Static); // SILVERLIGHT: Not sure what this does & what would be the right behavior... #if !SILVERLIGHT last_context.SetCustomAttribute(AttributeBuilders.ThreadStatic); #endif // Label init_needed_label = cil.DefineLabel(); // [ if (arg0 == __lastContext) ret ] cil.Emit(OpCodes.Ldarg_0); cil.Emit(OpCodes.Ldsfld, last_context); cil.Emit(OpCodes.Bne_Un_S, init_needed_label); cil.Emit(OpCodes.Ret); // [ __lastContext = arg0 ] cil.MarkLabel(init_needed_label); cil.Emit(OpCodes.Ldarg_0); cil.Emit(OpCodes.Stsfld, last_context); // the rest of the method is created when fields are emitted } }
private static void EmitExportedConstructors(PhpType /*!*/ phpType) { PhpMethod ctor = phpType.GetConstructor() as PhpMethod; foreach (StubInfo info in phpType.Builder.ClrConstructorStubs) { EmitExportedConstructor( phpType, info.ConstructorBuilder, ctor, info.Parameters); } }
internal override PhpTypeCode EmitReadFieldOfThisInInstanceMethod(CodeGenerator codeGenerator, bool wantRef) { PhpType type = codeGenerator.LocationStack.PeekMethodDecl().Type; DProperty property; if (type.GetProperty(VarName, type, out property) == GetMemberResult.OK && !property.IsStatic) { // ask the DProperty to emit code that reads the field return(property.EmitGet(codeGenerator, IndexedPlace.ThisArg, wantRef, null, false)); } else { return(base.EmitReadFieldOfThisInInstanceMethod(codeGenerator, wantRef)); } }
internal override AssignmentCallback EmitWriteFieldOfThisInInstanceMethod(CodeGenerator /*!*/ codeGenerator, bool writeRef) { PhpType type = codeGenerator.LocationStack.PeekMethodDecl().Type; DProperty property; if (type.GetProperty(VarName, type, out property) == GetMemberResult.OK && !property.IsStatic) { // ask the DProperty to emit code that writes the field return(property.EmitSet(codeGenerator, IndexedPlace.ThisArg, writeRef, null, false)); } else { return(base.EmitWriteFieldOfThisInInstanceMethod(codeGenerator, writeRef)); } }
/// <summary> /// Emits constructors into a class. /// </summary> internal static void EmitClassConstructors(PhpType /*!*/ phpType) { #if !SILVERLIGHT EmitDeserializingConstructor(phpType); #endif EmitShortConstructor(phpType); EmitLongConstructor(phpType); EmitFinalizer(phpType); // emit CLR-friendly constructors based on the PHP constructor method effective for the type if (phpType.IsExported) { EmitExportedConstructors(phpType); } }
/// <summary> /// Used by CLR modules and PHP pure modules. /// </summary> internal static void ReflectTypes(Assembly /*!*/ realAssembly, Dictionary <string, DTypeDesc> /*!*/ types) { // types: foreach (Type type in realAssembly.GetTypes()) { if (type.IsVisible) { // skip PHP types that were declared conditionally: if (PhpType.IsPhpRealType(type) && PhpType.IsRealConditionalDefinition(type)) { continue; } // converts CLR namespaces and nested types to PHP namespaces: string full_name = ClrNotationUtils.FromClrNotation(type.FullName, true).ToString(); DTypeDesc existing; if (types.TryGetValue(full_name, out existing)) { ClrTypeDesc existing_clr = existing as ClrTypeDesc; if (existing_clr != null && (existing_clr.GenericOverloads.Count > 0 || type.IsGenericTypeDefinition)) { ClrTypeDesc new_clr = DTypeDesc.Create(type) as ClrTypeDesc; if (new_clr != null) { // type is overloaded by the number of generic parameters: existing_clr.AddGenericOverload(new_clr); } else { // do not add, just mark existing with the flag: existing.MemberAttributes |= PhpMemberAttributes.Ambiguous; } } else { // do not add, just mark existing with the flag: existing.MemberAttributes |= PhpMemberAttributes.Ambiguous; } } else { types[full_name] = DTypeDesc.Create(type); } } } }
private static void EmitExportedConstructor(PhpType /*!*/ phpType, ConstructorBuilder /*!*/ ctorStubBuilder, PhpMethod phpCtor, ParameterInfo[] /*!*/ parameters) { // set parameter names and attributes if (phpCtor != null) { ClrStubBuilder.DefineStubParameters( ctorStubBuilder, phpCtor.Builder.Signature.FormalParams, parameters); } // emit ctor body ILEmitter cil = new ILEmitter(ctorStubBuilder); // [ this(ScriptContext.CurrentContext ] cil.Ldarg(FunctionBuilder.ArgThis); cil.EmitCall(OpCodes.Call, Methods.ScriptContext.GetCurrentContext, null); LocalBuilder sc_local = cil.DeclareLocal(Types.ScriptContext[0]); cil.Stloc(sc_local); cil.Ldloc(sc_local); cil.LdcI4(1); cil.Emit(OpCodes.Call, phpType.ShortConstructorInfo); if (phpCtor != null) { // invoke the PHP ctor method ClrStubBuilder.EmitMethodStubBody( cil, new Place(sc_local), parameters, new GenericTypeParameterBuilder[0], Types.Void, phpCtor, phpCtor.DeclaringType); } else { cil.Emit(OpCodes.Ret); } }
/// <summary> /// Traces the calling stack to discover current PHP class context. /// </summary> /// <returns><see cref="Type"/> of the PHP class that represents current class context for this thread or /// <B>null</B> if this thread is executing in a function or startup Main context.</returns> public static DTypeDesc GetClassContext() { // SILVERLIGHT: Todo Todo .. ? what to do here ? #if !SILVERLIGHT StackTrace stack_trace = new StackTrace(1); int frame_count = stack_trace.FrameCount; for (int i = 0; i < frame_count; i++) { StackFrame stack_frame = stack_trace.GetFrame(i); MethodBase method = stack_frame.GetMethod(); Type type = method.DeclaringType; if (type != null) { if (PhpType.IsPhpRealType(type)) { return(DTypeDesc.Create(type)); } MethodInfo minfo = method as MethodInfo; if (minfo != null) { ParameterInfo[] parameters = minfo.GetParameters(); if (!PhpFunctionUtils.IsArglessStub(minfo, parameters) && PhpScript.IsScriptType(minfo.DeclaringType) && !PhpScript.IsMainHelper(minfo, parameters)) { return(null); } // if the method is a helper method (Main, an arg-less overload, a constructor, etc.), // continue with the trace } } } #endif return(null); }
/// <summary> /// Reflect argfull function, PHP types and constants from given <c>type</c>. /// </summary> /// <param name="types">Dictionary of types where newly discovered PHP types will be placed. (Types having [ImplementsType] attribute.)</param> /// <param name="functions">Dictionary of reflected functions.</param> /// <param name="constants">Dictionary of reflected constants.</param> /// <param name="type">The type to reflect functions from.</param> /// <param name="full">Whether to perform full function reflect.</param> private void ReflectArgfulls( Dictionary <string, DTypeDesc> /*!*/ types, Dictionary <string, DRoutineDesc> /*!*/ functions, DualDictionary <string, DConstantDesc> /*!*/ constants, Type /*!*/ type, bool full) { // skip generic types: if (type.IsGenericTypeDefinition) { return; } if (PhpType.IsPhpRealType(type)) { var dtype = PhpTypeDesc.Create(type); types[dtype.MakeSimpleName()] = dtype; } // reflect even if it is PhpType to find global functions [ImplementsFunction] and constants [ImplementsConstant] if (IsLibraryType(type)) { ReflectLibraryType(functions, constants, type, full); } }
/// <summary> /// Generates a <c><PopulateTypeDesc></c> method that populates a <see cref="DTypeDesc"/> /// at runtime (instead of reflecting the class). /// </summary> /// <param name="phpType">The class representation used in the compiler.</param> internal static void GenerateTypeDescPopulation(PhpType phpType) { MethodBuilder populator = DefinePopulateTypeDescMethod(phpType.RealTypeBuilder); ILEmitter il = new ILEmitter(populator); // methods foreach (KeyValuePair <Name, DRoutineDesc> pair in phpType.TypeDesc.Methods) { if (!pair.Value.IsAbstract) { EmitAddMethod(il, pair.Key.ToString(), pair.Value.MemberAttributes, pair.Value.PhpMethod.ArgLessInfo); } } // fields foreach (KeyValuePair <VariableName, DPropertyDesc> pair in phpType.TypeDesc.Properties) { PhpField field = pair.Value.PhpField; // determine whether we need to add this field if (field.Implementor == field.DeclaringPhpType || field.UpgradesVisibility) { EmitAddProperty(il, phpType.Builder.RealOpenType, pair.Key.ToString(), pair.Value.MemberAttributes, field.RealField); } } // constants foreach (KeyValuePair <VariableName, DConstantDesc> pair in phpType.TypeDesc.Constants) { EmitAddConstant(il, pair.Key.ToString(), pair.Value.ClassConstant); } il.Emit(OpCodes.Ret); }
/// <summary> /// Reflect PHP classes declared statically in the given script <c>type</c>. /// </summary> /// <param name="scriptType">Script type to reflect.</param> /// <param name="types">List of types to reflect to.</param> private static void ReflectScriptTypeClasses(Type scriptType, Dictionary <string, DTypeDesc> /*!*/ types) { ScriptDeclaresAttribute script_declares = ScriptDeclaresAttribute.Reflect(scriptType); if (script_declares == null) { return; } var module = scriptType.Module; foreach (var typeToken in script_declares.DeclaredTypes) { Type type = module.ResolveType(typeToken); // reflect PHP class, skip PHP types that were declared conditionally if (PhpType.IsPhpRealType(type) && !PhpType.IsRealConditionalDefinition(type)) { // converts CLR namespaces and nested types to PHP namespaces: string full_name = QualifiedName.FromClrNotation(type.FullName, true).ToString(); // Creating PhpTypeDesc with cache lookup since this type can be in the cache already: // Also force PHP type, because we already checked PhpType.IsPhpRealType(type) PhpTypeDesc phpType = (PhpTypeDesc)DTypeDesc.Create(type.TypeHandle); DTypeDesc existing; if (types.TryGetValue(full_name, out existing)) { // TODO (TP): can be generic overload!! existing.MemberAttributes |= PhpMemberAttributes.Ambiguous; } types.Add(full_name, phpType); } } }
/// <summary> /// Emits stubs for all overloads of one overridden or implemented 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.</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<string, MethodBuilder>/*!*/ stubs, PhpMethod/*!*/ target, DType/*!*/ targetType, PhpType/*!*/ declaringType, DMemberRef/*!*/ template, bool newSlot) { ClrMethod clr_template = template.Member as ClrMethod; if (clr_template == null) { if (!target.IsStatic) EmitOverrideStubsForPhpTemplate(stubs, target, targetType, declaringType, template, newSlot); return; } // // following code emits stubs in case of CLR base method // ConstructedType constructed_type = template.Type as ConstructedType; TypeBuilder type_builder = declaringType.RealTypeBuilder; // override all virtual non-final overloads foreach (ClrMethod.Overload overload in clr_template.Overloads) { if (overload.Method.IsVirtual && !overload.Method.IsFinal) { // map generic type parameters according to the constructed type Type constructed_return_type; ParameterInfo[] constructed_params = overload.MakeConstructed(constructed_type, out constructed_return_type); // check whether we have not generated this signature before string clr_sig = ClrMethod.Overload.ClrSignatureToString( overload.GenericParamCount, constructed_params, constructed_return_type); if (stubs.ContainsKey(clr_sig)) continue; Type[] param_types = new Type[constructed_params.Length]; for (int j = 0; j < param_types.Length; j++) { param_types[j] = constructed_params[j].ParameterType; } // determine the stub attributes MethodAttributes attr; string name; name = overload.Method.Name; attr = Reflection.Enums.ToMethodAttributes(target.MemberDesc.MemberAttributes); attr |= (MethodAttributes.Virtual | MethodAttributes.HideBySig); if (newSlot) attr |= MethodAttributes.NewSlot; MethodBuilder overload_builder = type_builder.DefineMethod(name, attr); if (overload.MandatoryGenericParamCount > 0) { // define the same generic parameters that are defined for the overridden method // (the same constraints but possibly having different names) ClrStubBuilder.DefineStubGenericParameters( overload_builder, overload.GenericParameters, target.Signature, param_types); } overload_builder.SetReturnType(constructed_return_type); overload_builder.SetParameters(param_types); // set parameter names and attributes ClrStubBuilder.DefineStubParameters(overload_builder, target.Builder.Signature.FormalParams, constructed_params); if (!overload_builder.IsAbstract) { EmissionContext emission_context = SetupStubPlaces(target.DeclaringPhpType, false); try { // convert parameters and invoke the target ClrStubBuilder.EmitMethodStubBody( new ILEmitter(overload_builder), ScriptContextPlace, constructed_params, overload.GenericParameters, constructed_return_type, target, targetType); } finally { RestorePlaces(emission_context); } } stubs.Add(clr_sig, overload_builder); } } }
/// <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); } } }
private EmissionContext SetupStubPlaces(PhpType/*!*/ type, bool stubIsStatic) { EmissionContext context = new EmissionContext(ScriptContextPlace, SelfPlace, il); ScriptContextPlace = new LazyLoadSCPlace(); if (stubIsStatic) { SelfPlace = LiteralPlace.Null; } else { if (type.ProxyFieldInfo != null) { // the real this is not a DObject SelfPlace = new Place(IndexedPlace.ThisArg, type.ProxyFieldInfo); } else { // the real this is a DObject SelfPlace = IndexedPlace.ThisArg; } } return context; }
/// <summary> /// Enumerates all export overloads for the given target PHP method. /// </summary> public static IEnumerable<StubInfo> DefineMethodExportStubs( PhpRoutine/*!*/ target, PhpType/*!*/ declaringType, MethodAttributes attributes, bool defineConstructors, StubSignatureFilter/*!*/ signatureFilter) { Debug.Assert(target.Builder != null); Type return_type = Types.Object[0]; PhpRoutineSignature signature = target.Signature; AST.FormalParam[] formal_params = target.Builder.Signature.FormalParams; AST.FormalTypeParam[] formal_type_params = target.Builder.TypeSignature.TypeParams; int gen_sig_count = signature.GenericParamCount - signature.MandatoryGenericParamCount + 1; int arg_sig_count = signature.ParamCount - signature.MandatoryParamCount + 1; // TODO: return type hints // HACK: change return type to void for methods that are apparently event handlers if (signature.GenericParamCount == 0 && arg_sig_count == 1 && signature.ParamCount == 2 && (signature.TypeHints[0] == null || signature.TypeHints[0].RealType == Types.Object[0]) && (signature.TypeHints[1] != null && typeof(EventArgs).IsAssignableFrom(signature.TypeHints[1].RealType))) { return_type = Types.Void; } for (int gen_sig = 0; gen_sig < gen_sig_count; gen_sig++) { for (int arg_sig = 0; arg_sig < arg_sig_count; arg_sig++) { // determine parameter types (except for method mandatory generic parameters) object[] parameter_types = GetStubParameterTypes( arg_sig + signature.MandatoryParamCount, gen_sig + signature.MandatoryGenericParamCount, signature, formal_type_params); // determine generic parameter names string[] generic_param_names = new string[target.Signature.MandatoryGenericParamCount + gen_sig]; for (int i = 0; i < generic_param_names.Length; i++) { generic_param_names[i] = formal_type_params[i].Name.ToString(); } // are we allowed to generate this signature? if (!signatureFilter(generic_param_names, parameter_types, return_type)) continue; GenericTypeParameterBuilder[] generic_params = StubInfo.EmptyGenericParameters; MethodBase method_base = null; MethodBuilder method = null; if (!defineConstructors) { method = declaringType.RealTypeBuilder.DefineMethod(target.FullName, attributes); // determine generic parameters if (generic_param_names.Length > 0) generic_params = method.DefineGenericParameters(generic_param_names); method_base = method; } ParameterInfo[] parameters = new ParameterInfo[parameter_types.Length]; // fill in parameter infos Type[] real_parameter_types = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { Type type = parameter_types[i] as Type; // generic method parameter fixup if (type == null) { int index = (int)parameter_types[i]; if (index < 0) type = generic_params[-(index + 1)].MakeByRefType(); else type = generic_params[index]; } string param_name; ParameterAttributes param_attrs; if (i < formal_params.Length) { param_name = formal_params[i].Name.ToString(); param_attrs = (formal_params[i].IsOut ? ParameterAttributes.Out : ParameterAttributes.None); } else { param_name = "args" + (i + 1); param_attrs = ParameterAttributes.None; } parameters[i] = new StubParameterInfo(i, type, param_attrs, param_name); real_parameter_types[i] = type; } if (method != null) { method.SetParameters(real_parameter_types); method.SetReturnType(return_type); method.SetCustomAttribute(AttributeBuilders.DebuggerHidden); } else { // constructor is never a generic method attributes |= MethodAttributes.SpecialName | MethodAttributes.RTSpecialName; attributes &= ~MethodAttributes.Virtual; ConstructorBuilder constructor = declaringType.RealTypeBuilder.DefineConstructor( attributes, CallingConventions.Standard, real_parameter_types); constructor.SetCustomAttribute(AttributeBuilders.DebuggerHidden); method_base = constructor; } yield return new StubInfo(method_base, parameters, generic_params, return_type); } } }
/// <summary> /// Change current class context. Remember the previous ones. /// </summary> /// <param name="classContextPlace">New class context place.</param> /// <param name="classContext">New class context type.</param> internal void PushClassContext(IPlace classContextPlace, PhpType classContext) { this.classContextPlaces.Push(classContextPlace); this.classContexts.Push(classContext); }
/// <summary> /// Resolves type based on provided <paramref name="typeName"/>. /// </summary> /// <param name="typeName">Either <see cref="GenericQualifiedName"/> or <see cref="PrimitiveTypeName"/> or <c>null</c> reference.</param> /// <param name="referringType"></param> /// <param name="referringRoutine"></param> /// <param name="position"></param> /// <param name="mustResolve"></param> /// <returns></returns> internal DType ResolveType(object typeName, PhpType referringType, PhpRoutine referringRoutine, Position position, bool mustResolve) { DType result = null; if (typeName != null) { if (typeName.GetType() == typeof(GenericQualifiedName)) { result = ResolveTypeName((GenericQualifiedName)typeName, referringType, referringRoutine, position, mustResolve); } else if (typeName.GetType() == typeof(PrimitiveTypeName)) { result = PrimitiveType.GetByName((PrimitiveTypeName)typeName); } else { throw new ArgumentException("typeName"); } } return result; }
/// <summary> /// Initializes a new instance of the <see cref="ClassCouplingVisitor" /> class. /// </summary> /// <param name="type"></param> public ClassCouplingVisitor(PhpType type) { currentType = type; }
private GenericParameter ResolveTypeParameterName(Name name, PhpType referringType, PhpRoutine referringRoutine) { GenericParameter result = null; if (referringRoutine != null) { result = referringRoutine.Signature.GetGenericParameter(name); if (result != null) return result; } if (referringType != null) { result = referringType.GetGenericParameter(name); if (result != null) return result; } return result; }
public DType/*!*/ ResolveTypeName(QualifiedName qualifiedName, PhpType referringType, PhpRoutine referringRoutine, Position position, bool mustResolve) { DType result; if (qualifiedName.IsSelfClassName) { if (referringType != null) { result = referringType; } else { // we are sure the self is used incorrectly in function: if (referringRoutine != null) ErrorSink.Add(Errors.SelfUsedOutOfClass, SourceUnit, position); // global code can be included to the method: result = UnknownType.UnknownSelf; } } else if (qualifiedName.IsStaticClassName) { if (referringType != null) { if (referringType.IsFinal) { // we are sure the 'static' == 'self' result = referringType; } else { if (referringRoutine != null) referringRoutine.Properties |= RoutineProperties.LateStaticBinding; result = StaticType.Singleton; } } else { // we are sure the static is used incorrectly in function: //if (referringRoutine != null) // do not allow 'static' in global code: ErrorSink.Add(Errors.StaticUsedOutOfClass, SourceUnit, position); // global code can be included to the method: result = UnknownType.UnknownStatic; } } else if (qualifiedName.IsParentClassName) { if (referringType != null) { if (referringType.IsInterface) { ErrorSink.Add(Errors.ParentUsedOutOfClass, SourceUnit, position); result = UnknownType.UnknownParent; } else { DType base_type = referringType.Base; if (base_type == null) { ErrorSink.Add(Errors.ClassHasNoParent, SourceUnit, position, referringType.FullName); result = UnknownType.UnknownParent; } else { result = base_type; } } } else { // we are sure the self is used incorrectly when we are in a function: if (referringRoutine != null) ErrorSink.Add(Errors.ParentUsedOutOfClass, SourceUnit, position); // global code can be included to the method: result = UnknownType.UnknownParent; } } else { // try resolve the name as a type parameter name: if (qualifiedName.IsSimpleName) { result = ResolveTypeParameterName(qualifiedName.Name, referringType, referringRoutine); if (result != null) return result; } Scope referring_scope = GetReferringScope(referringType, referringRoutine); QualifiedName? alias; result = sourceUnit.ResolveTypeName(qualifiedName, referring_scope, out alias, ErrorSink, position, mustResolve); ReportUnknownType(result, alias, position); } return result; }
public DType ResolveType(object typeNameOrPrimitiveType, PhpType referringType, PhpRoutine referringRoutine, Position position, bool mustResolve) { Debug.Assert(typeNameOrPrimitiveType == null || typeNameOrPrimitiveType is PrimitiveType || typeNameOrPrimitiveType is GenericQualifiedName); DType result = typeNameOrPrimitiveType as PrimitiveType; if (result != null) return result; if (typeNameOrPrimitiveType != null) { return ResolveTypeName((GenericQualifiedName)typeNameOrPrimitiveType, referringType, referringRoutine, position, mustResolve); } return null; }
private Scope GetReferringScope(PhpType referringType, PhpRoutine referringRoutine) { if (referringType != null) return referringType.Declaration.Scope; if (referringRoutine is PhpFunction) return ((PhpFunction)referringRoutine).Declaration.Scope; //if (referringRoutine is PhpLambdaFunction) ... // used for global statements during full analysis: Debug.Assert(currentScope.IsValid, "Scope is available only during full analysis."); return currentScope; }
/// <summary> /// Emits (ScriptContext, DTypeDesc) constructor. /// </summary> private static void EmitLongConstructor(PhpType /*!*/ phpType) { // (ScriptContext, DTypeDesc) constructor ConstructorBuilder ctor_builder = phpType.LongConstructorBuilder; ctor_builder.DefineParameter(1, ParameterAttributes.None, "context"); ctor_builder.DefineParameter(2, ParameterAttributes.None, "caller"); // [ this(arg1,true) ] ILEmitter cil = new ILEmitter(ctor_builder); cil.Ldarg(FunctionBuilder.ArgThis); cil.Ldarg(FunctionBuilder.ArgContextInstance); cil.LdcI4(1); cil.Emit(OpCodes.Call, phpType.ShortConstructorInfo); if (phpType.ProxyFieldInfo != null) { // [ <proxy>.InvokeConstructor(args) ] cil.Ldarg(FunctionBuilder.ArgThis); cil.Emit(OpCodes.Ldfld, phpType.ProxyFieldInfo); cil.Ldarg(1); cil.Ldarg(2); cil.Emit(OpCodes.Call, Methods.DObject_InvokeConstructor); } else { // try to find constructor method and call it directly // if it is publically visible without any reason to throw a warning in runtime DRoutineDesc construct = null; // = found constructor; if not null, can be called statically without runtime checks bool constructorFound = false; // try to find constructor for (DTypeDesc type_desc = phpType.TypeDesc; type_desc != null; type_desc = type_desc.Base) { construct = type_desc.GetMethod(DObject.SpecialMethodNames.Construct); if (construct == null) { construct = type_desc.GetMethod(new Name(type_desc.MakeSimpleName())); } if (construct != null) { constructorFound = true; if (!construct.IsPublic || construct.IsStatic || construct.PhpRoutine == null || construct.PhpRoutine.ArgLessInfo == null) { construct = null; // invalid constructor found, fall back to dynamic behavior } break; } } // emit constructor call if (construct != null) { // publically visible not static constructor, can be called statically anywhere // [ __construct( this, context.Stack ) ] cil.Ldarg(FunctionBuilder.ArgThis); // this cil.Ldarg(1); cil.Emit(OpCodes.Ldfld, Emit.Fields.ScriptContext_Stack); // context.Stack cil.Emit(OpCodes.Call, construct.PhpRoutine.ArgLessInfo); // __construct cil.Emit(OpCodes.Pop); } else if (!constructorFound) // there is no ctor at all { // [ context.Stack.RemoveFrame() ] cil.Ldarg(1); cil.Emit(OpCodes.Ldfld, Emit.Fields.ScriptContext_Stack); // context.Stack cil.Emit(OpCodes.Callvirt, Emit.Methods.PhpStack.RemoveFrame); // .RemoveFrame } else { // constructor should be checked in runtime (various visibility cases) // warnings can be displayed // [ InvokeConstructor(arg2) ] cil.Ldarg(FunctionBuilder.ArgThis); cil.Ldarg(1); cil.Ldarg(2); cil.Emit(OpCodes.Call, Methods.DObject_InvokeConstructor); } } cil.Emit(OpCodes.Ret); }
private void PreAnalyzePartialDeclaration(Analyzer/*!*/ analyzer, TypeDecl/*!*/ aggregate, ref DTypeDesc aggregateBase, List<DTypeDesc>/*!*/ aggregateInterfaces) { // // little hack, change the sourceUnit in order to match the current partial class declaration with the right file and imported namespaces // var current_sourceUnit = analyzer.SourceUnit; analyzer.SourceUnit = this.type.Declaration.SourceUnit; // // // try { bool valid = true; if (type.IsInterface != aggregate.type.IsInterface) { analyzer.ErrorSink.Add(Errors.IncompatiblePartialDeclarations, type.Declaration.SourceUnit, type.Declaration.Position, type.FullName); analyzer.ErrorSink.Add(Errors.RelatedLocation, aggregate.type.Declaration.SourceUnit, aggregate.type.Declaration.Position); valid = false; } if (type.Visibility != aggregate.type.Visibility) { analyzer.ErrorSink.Add(Errors.ConflictingPartialVisibility, type.Declaration.SourceUnit, type.Declaration.Position, type.FullName); analyzer.ErrorSink.Add(Errors.RelatedLocation, aggregate.type.Declaration.SourceUnit, aggregate.type.Declaration.Position); valid = false; } // merge base types: DTypeDesc base_type = ResolveBaseType(analyzer); if (base_type != null) { if (aggregateBase != null) { if (!base_type.Type.Equals(aggregateBase.Type)) //Edited by Ðonny Jan 07 2009 - missing "!"? { analyzer.ErrorSink.Add(Errors.PartialDeclarationsDifferInBase, type.Declaration.SourceUnit, type.Declaration.Position, type.FullName); analyzer.ErrorSink.Add(Errors.RelatedLocation, aggregate.type.Declaration.SourceUnit, aggregate.type.Declaration.Position); valid = false; } } else { aggregateBase = base_type; } } typeSignature.PreAnalyze(analyzer, type); // merge generic parameters: valid &= aggregate.typeSignature.Merge(analyzer.ErrorSink, this.type, typeSignature); // move members to the aggregate: members.ForEach(member => member.sourceUnit = analyzer.SourceUnit); // override SourceUnit of the members to match the debug information and imported namespaces properly furing the analysis aggregate.members.AddRange(members); members = null; // move attributes to the aggregate: aggregate.attributes.Merge(attributes); // merge interfaces: ResolveBaseInterfaces(analyzer, aggregateInterfaces); // next partial declaration; // (if the declaration is erroneous, stop analysis before reporting more messy errors): if (valid && type.Version.Next != null) { ((TypeDecl)type.Version.Next.Declaration.Node).PreAnalyzePartialDeclaration(analyzer, aggregate, ref aggregateBase, aggregateInterfaces); } } finally { // cut the AST off the tables: type.Declaration.Node = null; type = null; // // change the sourceUnit back // analyzer.SourceUnit = current_sourceUnit; } }
/// <summary> /// Called when a <see cref="PHP.Core.AST.TypeDecl"/> AST node is entered during the emit phase. /// </summary> public void EnterTypeDeclaration(PhpType/*!*/ type) { CompilerLocationStack.TypeDeclContext cd_context = new CompilerLocationStack.TypeDeclContext(); cd_context.Type = type; cd_context.TypeContextPlace = TypeContextPlace; TypeContextPlace = new Place(null, type.TypeDescFieldInfo); // CallSites cd_context.CallSites = callSites; this.callSites = new CallSitesBuilder( sourceUnit.CompilationUnit.Module.GlobalType.RealModuleBuilder, type.QualifiedName.ToString(), TypeContextPlace, /*class_context = TypeContextPlace, can be used in .cctor of call sites container*/ type); // locationStack.PushTypeDecl(cd_context); }
/// <summary> /// Used by full reflection. /// </summary> public PhpField(VariableName name, DPropertyDesc/*!*/ fieldDesc, FieldInfo/*!*/ fieldInfo, PropertyInfo exportedProperty) : base(fieldDesc, name) { Debug.Assert(fieldDesc is DPhpFieldDesc); this.realField = fieldInfo; this.exportedProperty = exportedProperty; this.hasInitialValue = realField.IsDefined(typeof(PhpHasInitValueAttribute), false); this.builder = null; this.implementor = DeclaringPhpType; }
public DType/*!*/ ResolveTypeName(GenericQualifiedName genericName, PhpType referringType, PhpRoutine referringRoutine, Position position, bool mustResolve) { DType type = ResolveTypeName(genericName.QualifiedName, referringType, referringRoutine, position, mustResolve); DTypeDesc[] arguments = (genericName.GenericParams.Length > 0) ? new DTypeDesc[genericName.GenericParams.Length] : DTypeDesc.EmptyArray; for (int i = 0; i < arguments.Length; i++) { arguments[i] = ResolveType(genericName.GenericParams[i], referringType, referringRoutine, position, mustResolve).TypeDesc; } return type.MakeConstructedType(this, arguments, position); }
internal void ReportAbstractNotImplemented(ErrorSink/*!*/ errors, PhpType/*!*/ referringType) { member.ReportAbstractNotImplemented(errors, type, referringType); }
/// <summary> /// Resolves a method of given <see cref="DType"/> by its name. /// </summary> /// <param name="type">The type of routine being resolved.</param> /// <param name="methodName">The name of routine to be resolved.</param> /// <param name="position">Position of method call used for error reporting.</param> /// <param name="referringType">The type where the seached routine is being called. Can be <c>null</c>.</param> /// <param name="referringRoutine">The routine where the searched routine is being called. Can be <c>null</c>.</param> /// <param name="calledStatically">True if the searched routine is called statically - if it uses static method call syntax. /// This affects the __call or __callStatic method lookup. /// It affects also the error reporting, where for instance method calls, the bad visibility error is /// ignored and falls back to return <see cref="UnknownMethod"/>.</param> /// <param name="checkVisibilityAtRuntime">Will determine if the routine call must be checked for visibility at runtime.</param> /// <param name="isCallMethod">Will determine if __call or __callStatic magic methods were found instead.</param> /// <returns>The resolved routine. Cannot return <c>null</c>.</returns> public DRoutine/*!*/ ResolveMethod(DType/*!*/ type, Name methodName, Position position, PhpType referringType, PhpRoutine referringRoutine, bool calledStatically, out bool checkVisibilityAtRuntime, out bool isCallMethod) { checkVisibilityAtRuntime = false; isCallMethod = false; // we cannot resolve a method unless we know the inherited members: if (type.IsDefinite) { KnownType known; // the method is a constructor: if (methodName.IsConstructName || (known = type as KnownType) != null && methodName.Equals(known.QualifiedName.Name)) return ResolveConstructor(type, position, referringType, referringRoutine, out checkVisibilityAtRuntime); DRoutine routine; GetMemberResult member_result; member_result = type.GetMethod(methodName, referringType, out routine); // Look for __call or __callStatic magic methods if no method was found: // Note: __call when looking for instance method is disabled, since there can be the searched method in some future override. if (member_result == GetMemberResult.NotFound && calledStatically) { // in PHP, it is possible to call instance methods statically if we are in instance method context. // In such case we have to look for __call instead of __callStatic: // determine the proper call method: // use __call for instance method invocation, including static method invocation within the current type (e.g. A::foo(), parent::foo(), ...) // use __callStatic for static method invocation Name callMethodName = (!calledStatically || // just to have complete condition here, always false (referringRoutine != null && referringType != null && !referringRoutine.IsStatic && // in non-static method type.TypeDesc.IsAssignableFrom(referringType.TypeDesc)) // {CurrentType} is inherited from or equal {type} ) ? DObject.SpecialMethodNames.Call : DObject.SpecialMethodNames.CallStatic; member_result = type.GetMethod(callMethodName, referringType, out routine); if (member_result != GetMemberResult.NotFound) isCallMethod = true; } switch (member_result) { case GetMemberResult.OK: return routine; case GetMemberResult.NotFound: if (calledStatically) // throw an error only in we are looking for static method, instance method can be defined in some future inherited class ErrorSink.Add(Errors.UnknownMethodCalled, SourceUnit, position, type.FullName, methodName); return new UnknownMethod(type, methodName.Value); case GetMemberResult.BadVisibility: { if (!calledStatically) // instance method will check the routine dynamically, there can be some override later return new UnknownMethod(type, methodName.Value); if (referringType == null && referringRoutine == null) { // visibility must be checked at run-time: checkVisibilityAtRuntime = true; return routine; } else { // definitive error: if (routine.IsPrivate) { ErrorSink.Add(Errors.PrivateMethodCalled, SourceUnit, position, type.FullName, methodName.Value, referringType.FullName); } else { ErrorSink.Add(Errors.ProtectedMethodCalled, SourceUnit, position, type.FullName, methodName.Value, referringType.FullName); } return new UnknownMethod(type, methodName.Value); } } default: Debug.Fail(); return null; } } else { // warning (if any) reported by the type resolver: return new UnknownMethod(type, methodName.Value); } }
/// <summary> /// Resolves constructor of the specified type within the current context (location). /// </summary> public DRoutine/*!*/ ResolveConstructor(DType/*!*/ type, Position position, PhpType referringType, PhpRoutine referringRoutine, out bool checkVisibilityAtRuntime) { checkVisibilityAtRuntime = false; KnownRoutine ctor; // Do resolve ctor despite of the indefiniteness of the type to make error reporting consistent // when accessing the constructors thru the new operator. switch (type.GetConstructor(referringType, out ctor)) { case GetMemberResult.OK: return ctor; case GetMemberResult.NotFound: // default ctor to be used: return new UnknownMethod(type); case GetMemberResult.BadVisibility: if (referringType == null && referringRoutine == null) { // visibility must be checked at run-time: checkVisibilityAtRuntime = true; return ctor; } else { // definitive error: if (ctor.IsPrivate) { ErrorSink.Add(Errors.PrivateCtorCalled, SourceUnit, position, type.FullName, ctor.FullName, referringType.FullName); } else { ErrorSink.Add(Errors.ProtectedCtorCalled, SourceUnit, position, type.FullName, ctor.FullName, referringType.FullName); } return new UnknownMethod(type); } default: Debug.Fail(); return null; } }
/// <summary> /// Resolves static properties. /// </summary> public DProperty/*!*/ ResolveProperty(DType/*!*/ type, VariableName propertyName, Position position, bool staticOnly, PhpType referringType, PhpRoutine referringRoutine, out bool checkVisibilityAtRuntime) { Debug.Assert(type != null); checkVisibilityAtRuntime = false; // we cannot resolve a property unless we know the inherited members: if (type.IsDefinite) { DProperty property; GetMemberResult member_result = type.GetProperty(propertyName, referringType, out property); switch (member_result) { case GetMemberResult.OK: if (staticOnly && !property.IsStatic) goto case GetMemberResult.NotFound; return property; case GetMemberResult.NotFound: ErrorSink.Add(Errors.UnknownPropertyAccessed, SourceUnit, position, type.FullName, propertyName); return new UnknownProperty(type, propertyName.Value); case GetMemberResult.BadVisibility: if (referringType == null && referringRoutine == null) { // visibility must be checked at run-time: checkVisibilityAtRuntime = true; return property; } else { // definitive error: if (property.IsPrivate) { ErrorSink.Add(Errors.PrivatePropertyAccessed, SourceUnit, position, type.FullName, propertyName.Value, referringType.FullName); } else { ErrorSink.Add(Errors.ProtectedPropertyAccessed, SourceUnit, position, type.FullName, propertyName.Value, referringType.FullName); } return new UnknownProperty(type, propertyName.Value); } default: Debug.Fail(); throw null; } } else { // warning (if any) reported by the type resolver: return new UnknownProperty(type, propertyName.Value); } }
/// <summary> /// Create new call sites builder. /// </summary> /// <param name="moduleBuilder">Module to contain call sites container.</param> /// <param name="userFriendlyName">User friendly name used to identify the call sites container by user.</param> /// <param name="classContextPlace">If known and if it can be emitted in static .cctor, defines the place where the class context can be loaded. Otherwise <c>null</c> if the class context will be determined in run time.</param> /// <param name="classContext">Current PHP type context.</param> public CallSitesBuilder(ModuleBuilder /*!*/ moduleBuilder, string /*!*/ userFriendlyName, IPlace classContextPlace, PhpType classContext) { Debug.Assert(moduleBuilder != null && userFriendlyName != null); this.userFriendlyName = userFriendlyName; this.moduleBuilder = moduleBuilder; this.PushClassContext(classContextPlace, classContext); this.delegateBuilder = new DelegateBuilder(moduleBuilder); }
internal DConstant ResolveClassConstantName(DType/*!*/ type, VariableName constantName, Position position, PhpType referringType, PhpRoutine referringRoutine, out bool checkVisibilityAtRuntime) { checkVisibilityAtRuntime = false; // we cannot resolve a class constant unless we know the inherited members: if (type.IsDefinite) { ClassConstant constant; GetMemberResult member_result = type.GetConstant(constantName, referringType, out constant); switch (member_result) { case GetMemberResult.OK: return constant; case GetMemberResult.NotFound: ErrorSink.Add(Errors.UnknownClassConstantAccessed, SourceUnit, position, type.FullName, constantName); return new UnknownClassConstant(type, constantName.Value); case GetMemberResult.BadVisibility: if (referringType == null && referringRoutine == null) { // visibility must be checked at run-time: checkVisibilityAtRuntime = true; return constant; } else { // definitive error: if (constant.IsPrivate) { ErrorSink.Add(Errors.PrivateConstantAccessed, SourceUnit, position, type.FullName, constantName.Value, referringType.FullName); } else { ErrorSink.Add(Errors.ProtectedConstantAccessed, SourceUnit, position, type.FullName, constantName.Value, referringType.FullName); } return new UnknownClassConstant(type, constantName.Value); } default: Debug.Fail(); throw null; } } else { // warning (if any) reported by the type resolver: return new UnknownClassConstant(type, constantName.Value); } }
private void Bake() { if (types != null) { bakedTypes = new KeyValuePair <string, PhpTypeDesc> [types.Count]; int i = 0; foreach (Declaration declaration in types.Values) { PhpType type = (PhpType)declaration.Declaree; // store full name before calling Bake() as it nulls the PhpType: string full_name = type.FullName; PhpTypeDesc baked = type.Bake(); // baked is null if the type is indefinite // (its base class may be evaluated when the module's main method is executed): if (baked != null && !declaration.IsConditional) { bakedTypes[i++] = new KeyValuePair <string, PhpTypeDesc>(full_name, baked); } } // trim: Array.Resize(ref bakedTypes, i); types = null; } if (functions != null) { bakedFunctions = new KeyValuePair <string, PhpRoutineDesc> [functions.Count]; int i = 0; foreach (Declaration declaration in functions.Values) { PhpFunction function = (PhpFunction)declaration.Declaree; string full_name = function.FullName; PhpRoutineDesc baked = function.Bake(); if (!declaration.IsConditional) { bakedFunctions[i++] = new KeyValuePair <string, PhpRoutineDesc>(full_name, baked); } } // trim: Array.Resize(ref bakedFunctions, i); functions = null; } if (constants != null) { bakedConstants = new KeyValuePair <string, DConstantDesc> [constants.Count]; int i = 0; foreach (Declaration declaration in constants.Values) { GlobalConstant constant = (GlobalConstant)declaration.Declaree; string full_name = constant.FullName; DConstantDesc baked = constant.Bake(); if (!declaration.IsConditional) { bakedConstants[i++] = new KeyValuePair <string, DConstantDesc>(full_name, baked); } } // trim: Array.Resize(ref bakedConstants, i); constants = null; } }
internal virtual void ReportMethodNotCompatible(ErrorSink /*!*/ errors, DType /*!*/ declaringType, PhpType /*!*/ referringType) { // to be implemented by methods and properties Debug.Fail(); throw null; }
/// <summary> /// Emits ghost stubs for methods and properties that are declared by a <paramref name="type"/>'s /// base type but need to be adapted to a particular CLR signature because of implementing an /// interface by the <paramref name="type"/>. /// </summary> /// <param name="type">The <see cref="PhpType"/> that possibly released ghosts by implementing an interface. /// </param> public void EmitGhostStubs(PhpType/*!*/ type) { List<KeyValuePair<DMemberRef, DMemberRef>> ghosts = type.Builder.GhostImplementations; if (ghosts != null) { Dictionary<string, MethodBuilder> m_stubs = new Dictionary<string, MethodBuilder>(); Dictionary<Type, PropertyBuilder> f_stubs = new Dictionary<Type, PropertyBuilder>(); for (int i = 0; i < ghosts.Count; i++) { PhpMethod impl_method; PhpField impl_field; if ((impl_method = ghosts[i].Value.Member as PhpMethod) != null) { // emit ghost method stub EmitOverrideStubs(m_stubs, impl_method, ghosts[i].Value.Type, type, ghosts[i].Key, true); } else if ((impl_field = ghosts[i].Value.Member as PhpField) != null) { // emit ghost property stub EmitOverrideStubs(f_stubs, impl_field, type, ghosts[i].Key, true); } } } }
/// <summary> /// Emits (ScriptContext, bool) constructor. /// </summary> private static void EmitShortConstructor(PhpType /*!*/ phpType) { // (ScriptContext,bool) constructor ConstructorBuilder ctor_builder = phpType.ShortConstructorBuilder; ctor_builder.DefineParameter(1, ParameterAttributes.None, "context"); ctor_builder.DefineParameter(2, ParameterAttributes.None, "newInstance"); ILEmitter cil = new ILEmitter(ctor_builder); // invoke base constructor if (phpType.Base == null) { EmitInvokePhpObjectConstructor(cil); } else { phpType.Base.EmitInvokeConstructor(cil, phpType, null); } // perform fast DObject.<typeDesc> init if we are in its subclass if (phpType.Root is PhpType) { // [ if (GetType() == typeof(self)) this.typeDesc = self.<typeDesc> ] cil.Ldarg(FunctionBuilder.ArgThis); cil.Emit(OpCodes.Call, Methods.Object_GetType); cil.Emit(OpCodes.Ldtoken, phpType.RealTypeBuilder); cil.Emit(OpCodes.Call, Methods.GetTypeFromHandle); Label label = cil.DefineLabel(); cil.Emit(OpCodes.Bne_Un_S, label); if (true) { cil.Ldarg(FunctionBuilder.ArgThis); cil.Emit(OpCodes.Ldsfld, phpType.TypeDescFieldInfo); cil.Emit(OpCodes.Stfld, Fields.DObject_TypeDesc); cil.MarkLabel(label); } } // register this instance for finalization if it introduced the __destruct method DRoutine destruct; if (phpType.TypeDesc.GetMethod(DObject.SpecialMethodNames.Destruct) != null && (phpType.Base == null || phpType.Base.GetMethod(DObject.SpecialMethodNames.Destruct, phpType, out destruct) == GetMemberResult.NotFound)) { cil.Ldarg(FunctionBuilder.ArgContextInstance); cil.Ldarg(FunctionBuilder.ArgThis); if (phpType.ProxyFieldInfo != null) { cil.Emit(OpCodes.Ldfld, phpType.ProxyFieldInfo); } cil.Emit(OpCodes.Call, Methods.ScriptContext.RegisterDObjectForFinalization); } // [ <InitializeInstanceFields>(arg1) ] cil.Ldarg(FunctionBuilder.ArgThis); cil.Ldarg(FunctionBuilder.ArgContextInstance); cil.EmitCall(OpCodes.Call, phpType.Builder.InstanceFieldInit, null); // [ __InitializeStaticFields(arg1) ] cil.Ldarg(FunctionBuilder.ArgContextInstance); cil.EmitCall(OpCodes.Call, phpType.StaticFieldInitMethodInfo, null); cil.Emit(OpCodes.Ret); }
/// <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; }
/// <summary> /// Used by the compiler. /// </summary> internal PhpMethod(PhpType/*!*/ declaringType, Name name, PhpMemberAttributes memberAttributes, bool hasBody, Signature astSignature, TypeSignature astTypeSignature, SourceUnit/*!*/ sourceUnit, Position position) : base(new PhpRoutineDesc(declaringType.TypeDesc, memberAttributes), astSignature, astTypeSignature) { Debug.Assert(declaringType != null && sourceUnit != null && position.IsValid); this.name = name; this.position = position; this.hasBody = hasBody; this.sourceUnit = sourceUnit; }
/// <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)); } } } }
internal override void ReportMethodNotCompatible(ErrorSink errors, DType declaringType, PhpType referringType) { errors.Add(Errors.MethodNotCompatible, referringType.Declaration.SourceUnit, referringType.Declaration.Position, referringType.FullName, this.FullName, declaringType.MakeFullGenericName(), this.FullName); //ReportError(errors, Errors.RelatedLocation); }
internal override void ReportAbstractNotImplemented(ErrorSink/*!*/ errors, DType/*!*/ declaringType, PhpType/*!*/ referringType) { errors.Add(Errors.AbstractPropertyNotImplemented, referringType.Declaration.SourceUnit, referringType.Declaration.Position, referringType.FullName, declaringType.MakeFullGenericName(), this.FullName); ReportError(errors, Errors.RelatedLocation); }
/// <summary> /// Enumerates all export overloads for the given target PHP method. /// </summary> public static IEnumerable <StubInfo> DefineMethodExportStubs( PhpRoutine /*!*/ target, PhpType /*!*/ declaringType, MethodAttributes attributes, bool defineConstructors, StubSignatureFilter /*!*/ signatureFilter) { Debug.Assert(target.Builder != null); Type return_type = Types.Object[0]; PhpRoutineSignature signature = target.Signature; AST.FormalParam[] formal_params = target.Builder.Signature.FormalParams; AST.FormalTypeParam[] formal_type_params = target.Builder.TypeSignature.TypeParams; int gen_sig_count = signature.GenericParamCount - signature.MandatoryGenericParamCount + 1; int arg_sig_count = signature.ParamCount - signature.MandatoryParamCount + 1; // TODO: return type hints // HACK: change return type to void for methods that are apparently event handlers if (signature.GenericParamCount == 0 && arg_sig_count == 1 && signature.ParamCount == 2 && (signature.TypeHints[0] == null || signature.TypeHints[0].RealType == Types.Object[0]) && (signature.TypeHints[1] != null && typeof(EventArgs).IsAssignableFrom(signature.TypeHints[1].RealType))) { return_type = Types.Void; } for (int gen_sig = 0; gen_sig < gen_sig_count; gen_sig++) { for (int arg_sig = 0; arg_sig < arg_sig_count; arg_sig++) { // determine parameter types (except for method mandatory generic parameters) object[] parameter_types = GetStubParameterTypes( arg_sig + signature.MandatoryParamCount, gen_sig + signature.MandatoryGenericParamCount, signature, formal_type_params); // determine generic parameter names string[] generic_param_names = new string[target.Signature.MandatoryGenericParamCount + gen_sig]; for (int i = 0; i < generic_param_names.Length; i++) { generic_param_names[i] = formal_type_params[i].Name.ToString(); } // are we allowed to generate this signature? if (!signatureFilter(generic_param_names, parameter_types, return_type)) { continue; } GenericTypeParameterBuilder[] generic_params = StubInfo.EmptyGenericParameters; MethodBase method_base = null; MethodBuilder method = null; if (!defineConstructors) { method = declaringType.RealTypeBuilder.DefineMethod(target.FullName, attributes); // determine generic parameters if (generic_param_names.Length > 0) { generic_params = method.DefineGenericParameters(generic_param_names); } method_base = method; } ParameterInfo[] parameters = new ParameterInfo[parameter_types.Length]; // fill in parameter infos Type[] real_parameter_types = new Type[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { Type type = parameter_types[i] as Type; // generic method parameter fixup if (type == null) { int index = (int)parameter_types[i]; if (index < 0) { type = generic_params[-(index + 1)].MakeByRefType(); } else { type = generic_params[index]; } } string param_name; ParameterAttributes param_attrs; if (i < formal_params.Length) { param_name = formal_params[i].Name.ToString(); param_attrs = (formal_params[i].IsOut ? ParameterAttributes.Out : ParameterAttributes.None); } else { param_name = "args" + (i + 1); param_attrs = ParameterAttributes.None; } parameters[i] = new StubParameterInfo(i, type, param_attrs, param_name); real_parameter_types[i] = type; } if (method != null) { method.SetParameters(real_parameter_types); method.SetReturnType(return_type); method.SetCustomAttribute(AttributeBuilders.DebuggerHidden); } else { // constructor is never a generic method attributes |= MethodAttributes.SpecialName | MethodAttributes.RTSpecialName; attributes &= ~MethodAttributes.Virtual; ConstructorBuilder constructor = declaringType.RealTypeBuilder.DefineConstructor( attributes, CallingConventions.Standard, real_parameter_types); constructor.SetCustomAttribute(AttributeBuilders.DebuggerHidden); method_base = constructor; } yield return(new StubInfo(method_base, parameters, generic_params, return_type)); } } }
/// <summary> /// Used by full reflection for fields that are not implemented by their declaring type. /// <seealso cref="PhpPublicFieldAttribute"/> /// </summary> public PhpField(VariableName name, DPropertyDesc/*!*/ fieldDesc, DPropertyDesc/*!*/ implementingFieldDesc, bool hasInitialValue, PropertyInfo exportedProperty) : base(fieldDesc, name) { Debug.Assert(fieldDesc is DPhpFieldDesc); this.realField = implementingFieldDesc.PhpField.RealField; this.exportedProperty = exportedProperty; this.hasInitialValue = hasInitialValue; this.builder = null; this.implementor = implementingFieldDesc.DeclaringType.PhpType; this.upgradesVisibility = (IsPublic && implementingFieldDesc.IsProtected); }
internal virtual void ReportMethodNotCompatible(ErrorSink/*!*/ errors, DType/*!*/ declaringType, PhpType/*!*/ referringType) { // to be implemented by methods and properties Debug.Fail(); throw null; }
/// <summary> /// Create new call sites builder. /// </summary> /// <param name="moduleBuilder">Module to contain call sites container.</param> /// <param name="userFriendlyName">User friendly name used to identify the call sites container by user.</param> /// <param name="classContextPlace">If known and if it can be emitted in static .cctor, defines the place where the class context can be loaded. Otherwise <c>null</c> if the class context will be determined in run time.</param> /// <param name="classContext">Current PHP type context.</param> public CallSitesBuilder(ModuleBuilder/*!*/moduleBuilder, string/*!*/userFriendlyName, IPlace classContextPlace, PhpType classContext) { Debug.Assert(moduleBuilder != null && userFriendlyName != null); this.userFriendlyName = userFriendlyName; this.moduleBuilder = moduleBuilder; this.PushClassContext(classContextPlace, classContext); this.delegateBuilder = new DelegateBuilder(moduleBuilder); }
internal TypeDeclLocation(PhpType/*!*/ type, int nestingLevel) : base(nestingLevel) { this.type = type; }
internal virtual void ReportAbstractNotImplemented(ErrorSink /*!*/ errors, DType /*!*/ declaringType, PhpType /*!*/ referringType) { // to be implemented by methods and properties Debug.Fail(null); throw null; }
internal void ReportAbstractNotImplemented(ErrorSink /*!*/ errors, PhpType /*!*/ referringType) { member.ReportAbstractNotImplemented(errors, type, referringType); }
/// <summary> /// Notices the analyzer that class declaration is entered. /// </summary> internal void EnterTypeDecl(PhpType type) { TypeDeclLocation c = new TypeDeclLocation(type, locationStack.Count); typeDeclStack.Push(c); locationStack.Push(c); }