void TypeRedeclared(Reflection.PhpTypeInfo type) { Debug.Assert(type != null); // TODO: ErrCode & throw throw new InvalidOperationException($"Type {type.Name} redeclared!"); }
/// <summary> /// Resolves whether given instance <paramref name="obj"/> is of given type <paramref name="tinfo"/>. /// </summary> /// <param name="obj">Value to be checked.</param> /// <param name="tinfo">Type descriptor.</param> /// <returns>Whether <paramref name="obj"/> is of type <paramref name="tinfo"/>.</returns> public static bool IsInstanceOf(object obj, Reflection.PhpTypeInfo tinfo) { // note: if tinfo is null => // type was not declared => // value cannot be its instance because there is no way how to instantiate it // ignoring the case when object is passed from CLR return(obj != null && tinfo != null && tinfo.Type.IsInstanceOfType(obj)); }
/// <summary> /// Resolves whether given instance <paramref name="value"/> is of given type <paramref name="tinfo"/>. /// </summary> /// <param name="value">Value to be checked.</param> /// <param name="tinfo">Type descriptor.</param> /// <returns>Whether <paramref name="value"/> is of type <paramref name="tinfo"/>.</returns> public static bool IsInstanceOf(object value, Reflection.PhpTypeInfo tinfo) { if (tinfo == null) { throw new ArgumentNullException(nameof(tinfo)); } return(tinfo.Type.GetTypeInfo().IsInstanceOfType(value)); }
/// <summary> /// Gets runtime instance fields of given object. /// </summary> /// <param name="tinfo">Type of <paramref name="instance"/>.</param> /// <param name="instance">Instance of type <paramref name="tinfo"/>.</param> /// <returns>Array representing internal runtime fields or <c>null</c> if no values are available.</returns> public static PhpArray GetRuntimeFields(this PhpTypeInfo tinfo, object instance) { Debug.Assert(instance != null); Debug.Assert(instance.GetType() == tinfo.Type.AsType()); // PhpArray __runtime_fields if (tinfo.RuntimeFieldsHolder != null) { // all runtime fields are considered public, // no visibility check needed return((PhpArray)tinfo.RuntimeFieldsHolder.GetValue(instance)); } else { return(null); } }
/// <summary> /// Gets a collection of the trait types implemented by the current type. /// </summary> public static IEnumerable <PhpTypeInfo> GetImplementedTraits(this PhpTypeInfo phptype) { if (ReferenceEquals(phptype, null)) { throw new ArgumentNullException(nameof(phptype)); } foreach (var f in phptype.Type.DeclaredFields) { // traits instance is stored in a fields: // private readonly TraitType<phptype> <>trait_TraitType; if (f.IsPrivate && f.IsInitOnly && f.Name.StartsWith("<>trait_") && f.FieldType.IsConstructedGenericType) { yield return(GetPhpTypeInfo(f.FieldType.GetGenericTypeDefinition())); } } }
public ClrProperty(PhpTypeInfo tinfo, PropertyInfo property) : base(tinfo) { Property = property ?? throw new ArgumentNullException(nameof(property)); _lazyGetter = new Lazy <Func <object, PhpValue> >(() => { var pinstance = Expression.Parameter(typeof(object)); var expr = Bind(null !, Expression.Convert(pinstance, Property.DeclaringType)); expr = ConvertExpression.BindToValue(expr); // return((Func <object, PhpValue>)Expression.Lambda(expr, true, pinstance).Compile()); }); // SetValue(instance, PhpValue): void _lazySetValue = new Lazy <Action <Context, object, PhpValue> >(() => { if (IsReadOnly) { // error return((_, _instance, _value) => { PhpException.ErrorException(Resources.ErrResources.readonly_property_written, ContainingType.Name, PropertyName); }); } var pctx = Expression.Parameter(typeof(Context)); var pinstance = Expression.Parameter(typeof(object)); var pvalue = Expression.Parameter(typeof(PhpValue)); // expr: <instance>.<field> var expr = Bind(pctx, Expression.Convert(pinstance, Property.DeclaringType)); // expr: <property> := <value> expr = Expression.Assign(expr, ConvertExpression.Bind(pvalue, expr.Type, pctx)); // TODO: PHP semantic (Operators.SetValue) // {expr}: void var lambda = Expression.Lambda(Expression.Block(expr, Expression.Empty()), pctx, pinstance, pvalue); return((Action <Context, object, PhpValue>)lambda.Compile()); }); }
internal TypeMethods(PhpTypeInfo type) { IEnumerable <MethodInfo> methods = type.Type .GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); // skip members of {System.Object} if we are in a PHP type if (type.Type.AsType() != typeof(object)) { methods = methods.Where(s_notObjectMember); } // skip [PhpHidden] methods methods = methods.Where(s_notPhpHidden); // collect available methods (including methods on base classes) foreach (var m in methods.ToLookup(_MethodName, StringComparer.OrdinalIgnoreCase)) { if (!ReflectionUtils.IsAllowedPhpName(m.Key)) // .ctor, .phpnew, implicit interface implementation { continue; } if (_methods == null) { _methods = new Dictionary <string, PhpMethodInfo>(StringComparer.OrdinalIgnoreCase); } var info = PhpMethodInfo.Create(_methods.Count + 1, m.Key, m.ToArray(), type); _methods[info.Name] = info; // resolve special methods var magic = MagicMethodByName(info.Name); if (magic != MagicMethods.undefined) { if (_magicMethods == null) { _magicMethods = new Dictionary <MagicMethods, PhpMethodInfo>(); } _magicMethods[magic] = info; } } }
/// <summary> /// Gets <see cref="PhpTypeInfo"/> of given <paramref name="type"/>. /// </summary> public static PhpTypeInfo GetPhpTypeInfo(this Type type) { if (type == null) { throw new ArgumentNullException("type"); } PhpTypeInfo result = null; var handle = type.TypeHandle; // lookup cache first lock (s_cache) // TODO: RW lock { s_cache.TryGetValue(handle, out result); } // invoke GetPhpTypeInfo<TType>() dynamically and cache the result if (result == null) { if (type.GetTypeInfo().IsGenericTypeDefinition) { // generic type definition cannot be used as a type parameter for GetPhpTypeInfo<T> // just instantiate the type info and cache the result result = new PhpTypeInfo(type); } else { // TypeInfoHolder<TType>.TypeInfo; result = (PhpTypeInfo)s_getPhpTypeInfo_T .MakeGenericMethod(type) .Invoke(null, Utilities.ArrayUtils.EmptyObjects); } lock (s_cache) { s_cache[handle] = result; } } // return(result); }
/// <summary> /// Builds delegate that creates uninitialized class instance for purposes of deserialization. /// </summary> internal static Func <Context, object> BuildCreateEmptyObjectFunc(PhpTypeInfo tinfo) { Debug.Assert(tinfo != null); Func <Context, object> candidate = null; if (!tinfo.IsInterface && !tinfo.IsTrait) { var ctors = tinfo.Type.DeclaredConstructors; foreach (var c in ctors) { if (c.IsStatic || c.IsPhpHidden()) { continue; } if (c.IsPhpFieldsOnlyCtor()) { // we want this one: return((_ctx) => c.Invoke(new object[] { _ctx })); } var ps = c.GetParameters(); if (ps.Length == 0) { candidate = (_ctx) => c.Invoke(Array.Empty <object>()); } if (ps.Length == 1 && ps[0].ParameterType == typeof(Context)) { candidate = (_ctx) => c.Invoke(new object[] { _ctx }); } } } return(candidate ?? ((_ctx) => { Debug.Fail(string.Format(Resources.ErrResources.construct_not_supported, tinfo.Name)); return null; })); }
static void DeclareType(PhpTypeInfo info) { if (info.Index == 0) { var name = info.Name; int index; if (NameToIndex.TryGetValue(name, out index)) { throw new ArgumentException(); // redeclaration } else { index = -AppTypes.Count - 1; AppTypes.Add(info); NameToIndex[name] = index; } info.Index = index; } }
public static void DeclareAppType(PhpTypeInfo info) { if (info.Index == 0) { var name = info.Name; int index; if (_nameToIndex.TryGetValue(name, out index)) { throw new ArgumentException($"Type '{name}' already declared!"); // redeclaration } else { index = -_appTypes.Count - 1; _appTypes.Add(info); _nameToIndex[name] = index; } info.Index = index; } }
int EnsureTypeIndex(PhpTypeInfo info) { if (info.Index == 0) { TypesAppContext.RwLock.EnterWriteLock(); try { // double checked lock if (info.Index == 0) { int index; var name = info.Name; if (_nameToIndex.TryGetValue(name, out index)) { if (index < 0) // redeclaring over an app context type { _redeclarationCallback(info); } info.Index = index; } else { index = _contextTypesCounter.GetNewIndex() + 1; _nameToIndex[name] = index; } info.Index = index; } } finally { TypesAppContext.RwLock.ExitWriteLock(); } } Debug.Assert(info.Index != 0); return(info.Index); }
/// <summary> /// Gets descriptor representing a runtime field. /// Can be <c>null</c> if type does not support runtime fields. /// </summary> public static PhpPropertyInfo GetRuntimeProperty(this PhpTypeInfo tinfo, string propertyName, object instance) { if (tinfo.RuntimeFieldsHolder != null) { var key = new IntStringKey(propertyName); if (instance != null) { var runtimefields = tinfo.GetRuntimeFields(instance); if (runtimefields == null || runtimefields.Count == 0 || !runtimefields.ContainsKey(key)) { return(null); } } return(new PhpPropertyInfo.RuntimeProperty(tinfo, key)); } else { return(null); } }
/// <summary> /// Gets field name to be used as array key when casting object to array. /// </summary> static string FieldAsArrayKey(FieldInfo f, PhpTypeInfo declaringType) { Debug.Assert(f != null); Debug.Assert(declaringType != null); if (f.IsPublic) { return(f.Name); } if (f.IsFamily) { return(" * " + f.Name); } if (f.IsPrivate) { return(" " + declaringType.Name + " " + f.Name); } Debug.Fail($"Unexpected field attributes {f.Attributes}"); return(f.Name); }
/// <summary> /// Copies fields from <paramref name="source"/> object to <paramref name="target"/> object. /// Types of both objects must be the same. /// </summary> /// <param name="tinfo">Type of both objects.</param> /// <param name="source">Source instance.</param> /// <param name="target">Target instance.</param> public static void MemberwiseClone(PhpTypeInfo tinfo, object source, object target) { Debug.Assert(tinfo != null); Debug.Assert(source != null); Debug.Assert(target != null); Debug.Assert(source.GetType() == target.GetType()); Debug.Assert(source.GetType() == tinfo.Type.AsType()); // copy CLR fields, skipping runtime fields foreach (var fldvalue in TypeMembersUtils.EnumerateInstanceFields(source, (f, d) => f, null, null, true)) { fldvalue.Key.SetValue(target, fldvalue.Value.DeepCopy()); } // fast copy of runtime fields var runtime_fields = tinfo.GetRuntimeFields(source); if (runtime_fields != null && runtime_fields.Count != 0) { tinfo.RuntimeFieldsHolder.SetValue(target, runtime_fields.DeepCopy()); } }
public ClrFieldProperty(PhpTypeInfo tinfo, FieldInfo field) : base(tinfo) { _field = field ?? throw new ArgumentNullException(nameof(field)); // _lazyGetter = new Lazy <Func <object, PhpValue> >(() => (Func <object, PhpValue>)CompileAccess(AccessMask.Read)); _lazyEnsureAlias = new Lazy <Func <object, PhpAlias> >(() => (Func <object, PhpAlias>)CompileAccess(AccessMask.ReadRef)); _lazyEnsureObject = new Lazy <Func <object, object> >(() => (Func <object, object>)CompileAccess(AccessMask.EnsureObject)); _lazyEnsureArray = new Lazy <Func <object, IPhpArray> >(() => (Func <object, IPhpArray>)CompileAccess(AccessMask.EnsureArray)); // SetValue(instance, PhpValue): void _lazySetValue = new Lazy <Action <Context, object, PhpValue> >(() => { if (IsReadOnly) { // error return(new Action <Context, object, PhpValue>((_, _instance, _value) => { PhpException.ErrorException(Resources.ErrResources.readonly_property_written, ContainingType.Name, PropertyName); })); } var pctx = Expression.Parameter(typeof(Context)); var pinstance = Expression.Parameter(typeof(object)); var pvalue = Expression.Parameter(typeof(PhpValue)); // expr: <instance>.<field> var expr = Bind(pctx, Expression.Convert(pinstance, _field.DeclaringType)); // expr: <field> := <value> expr = BinderHelpers.BindAccess(expr, pctx, AccessMask.Write, pvalue); // var lambda = Expression.Lambda(Expression.Block(expr, Expression.Empty()), pctx, pinstance, pvalue); return((Action <Context, object, PhpValue>)lambda.Compile()); }); }
/// <summary> /// Gets <see cref="PhpTypeInfo"/> of given <paramref name="type"/>. /// </summary> public static PhpTypeInfo GetPhpTypeInfo(this Type type) { if (type == null) { throw new ArgumentNullException("type"); } PhpTypeInfo result = null; var handle = type.TypeHandle; // lookup cache first lock (_cache) // TODO: RW lock { _cache.TryGetValue(handle, out result); } // invoke GetPhpTypeInfo<TType>() dynamically and cache the result if (result == null) { if (_lazyGetPhpTypeInfo_T == null) { _lazyGetPhpTypeInfo_T = typeof(PhpTypeInfoExtension).GetRuntimeMethod("GetPhpTypeInfo", Dynamic.Cache.Types.Empty); } // TypeInfoHolder<TType>.TypeInfo; result = (PhpTypeInfo)_lazyGetPhpTypeInfo_T .MakeGenericMethod(type) .Invoke(null, Utilities.ArrayUtils.EmptyObjects); lock (_cache) { _cache[handle] = result; } } // return(result); }
int EnsureTypeIndex(PhpTypeInfo info) { if (info.Index == 0) { s_rwLock.EnterWriteLock(); try { // double checked lock if (info.Index == 0) { if (s_nameToIndex.TryGetValue(info.Name, out var index)) { if (index < 0) // redeclaring over an app context type { RedeclarationError(info); } info.Index = index; } else { index = s_contextTypesCounter.GetNewIndex(); s_nameToIndex[info.Name] = index; } info.Index = index; } } finally { s_rwLock.ExitWriteLock(); } } Debug.Assert(info.Index != 0); return(info.Index); }
/// <summary> /// Copies fields from <paramref name="source"/> object to <paramref name="target"/> object. /// Types of both objects must be the same. /// </summary> /// <param name="tinfo">Type of both objects.</param> /// <param name="source">Source instance.</param> /// <param name="target">Target instance.</param> public static void MemberwiseClone(PhpTypeInfo tinfo, object source, object target) { Debug.Assert(tinfo != null); Debug.Assert(source != null); Debug.Assert(target != null); Debug.Assert(source.GetType() == target.GetType()); Debug.Assert(source.GetType() == tinfo.Type.AsType()); // copy CLR fields, skipping runtime fields foreach (var prop in TypeMembersUtils.EnumerateInstanceFields(source, Utilities.FuncExtensions.Identity <PhpPropertyInfo>(), null, null, ignoreRuntimeFields: true)) { var value = prop.Value.DeepCopy(); prop.Key.SetValue(null, target, value); } // fast copy of runtime fields var runtime_fields = tinfo.GetRuntimeFields(source); if (runtime_fields != null && runtime_fields.Count != 0) { tinfo.RuntimeFieldsHolder.SetValue(target, runtime_fields.DeepCopy()); } }
public void DeclareTypeAlias(PhpTypeInfo info, string name) { if (s_nameToIndex.TryGetValue(name, out var index)) { if (index < 0) // redeclaring over an app context type { RedeclarationError(info); } } else { index = s_contextTypesCounter.GetNewIndex(); s_nameToIndex[name] = index; } // if (_contextTypes.Length < index) { Array.Resize(ref _contextTypes, index * 2); } DeclareType(ref _contextTypes[index - 1], info); }
protected PhpPropertyInfo(PhpTypeInfo tinfo) { ContainingType = tinfo ?? throw new ArgumentNullException(nameof(tinfo)); }
public RuntimeProperty(PhpTypeInfo tinfo, IntStringKey name) : base(tinfo) { _name = name; }
internal TypeMethods(PhpTypeInfo type) { // note: GetMethods() ignores "private" members on subclasses IEnumerable <MethodInfo> methods = type.Type .GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy); int index = 0; // skip members of {System.Object} if we are in a PHP type if (type.Type != typeof(object)) { methods = methods.Where(s_notObjectMember); } // skip [PhpHidden] methods and hidden methods (internal, private protected) methods = methods.Where(s_phpvisible); // collect available methods (including methods on base classes) foreach (var m in methods.ToLookup(_MethodName, StringComparer.OrdinalIgnoreCase)) { if (!ReflectionUtils.IsAllowedPhpName(m.Key)) // .ctor, implicit interface implementation { continue; } var overrides = m.ToArray(); // ignore methods in base classes that has been "overriden" in current class // in PHP we do override even if signature does not match (e.g. __construct) SelectVisibleOverrides(ref overrides); var magic = MagicMethods.undefined; if (IsSpecialName(overrides)) { // 'specialname' methods, // get_Item, set_Item if (!Enum.TryParse <MagicMethods>(m.Key.ToLowerInvariant(), out magic)) { continue; } } // TODO: negative {index} in case of non-user method var info = PhpMethodInfo.Create(++index, m.Key, overrides, type); if (magic == MagicMethods.undefined) { _methods ??= new Dictionary <string, PhpMethodInfo>(StringComparer.OrdinalIgnoreCase); _methods[info.Name] = info; // resolve magic methods magic = MagicMethodByName(info.Name); } if (magic != MagicMethods.undefined) { _magicMethods ??= new Dictionary <MagicMethods, PhpMethodInfo>(); _magicMethods[magic] = info; } } }
/// <summary> /// Enumerates self, all base types and all inherited interfaces. /// </summary> public static IEnumerable <PhpTypeInfo> EnumerateTypeHierarchy(this PhpTypeInfo phptype) { return(EnumerateClassHierarchy(phptype).Concat( // phptype + base types phptype.Type.GetInterfaces().Select(GetPhpTypeInfo))); // inherited interfaces }
/// <summary> /// Returns names and properties of all instance properties or only PHP fields (including runtime fields). /// </summary> /// <param name="instance">The instance being serialized.</param> /// <param name="tinfo"><paramref name="instance"/> type info.</param> /// <returns>Name-value pairs. Names are properly formatted for serialization.</returns> public static IEnumerable <KeyValuePair <string, PhpValue> > EnumerateSerializableProperties(object /*!*/ instance, PhpTypeInfo tinfo) { return(TypeMembersUtils.EnumerateInstanceFields(instance, FormatSerializedPropertyName, TypeMembersUtils.s_keyToString, (m) => !m.IsReadOnly)); }
/// <summary> /// Invoked when a type is redeclared. /// </summary> /// <param name="type">The type being declared, but another with the same name is already declared in context.</param> static void RedeclarationError(PhpTypeInfo type) { // TODO: ErrCode & throw, Log throw new InvalidOperationException($"Type {type.Name} redeclared!"); }
/// <summary> /// Resolves a method by its name, visible in given <paramref name="context"/>. /// </summary> /// <param name="tinfo">Type info.</param> /// <param name="name">Method name.</param> /// <param name="context">Current class context.</param> /// <returns>Resolved method or <c>null</c>.</returns> public static RoutineInfo GetVisibleMethod(this PhpTypeInfo /*!*/ tinfo, string /*!*/ name, RuntimeTypeHandle context = default) { var tcontext = context.Equals(default) ? null : Type.GetTypeFromHandle(context);
public PhpMethodInfoWithBoundType(int index, string name, MethodInfo[] methods, PhpTypeInfo lateStaticType) : base(index, name, methods) { Debug.Assert(lateStaticType != null); _lateStaticType = lateStaticType; }
internal bool IsDeclared(PhpTypeInfo type) { return(type.Index > 0 && type.Index <= _contextTypes.Length && _contextTypes[type.Index - 1] == type); }
/// <summary> /// Creates instance of <see cref="PhpMethodInfo"/>. /// </summary> public static PhpMethodInfo Create(int index, string name, MethodInfo[] methods, PhpTypeInfo callertype = null) { if (callertype != null) { if (callertype.Type.IsClass && methods.Any(Dynamic.BinderHelpers.HasLateStaticParameter)) { // if method requires late static bound type, remember it: return(new PhpMethodInfoWithBoundType(index, name, methods, lateStaticType: callertype)); } // reuse PhpMethodInfo from base type if possible if (AllDeclaredInBase(methods, callertype.Type) && callertype.BaseType != null && callertype.BaseType.RuntimeMethods[name] is PhpMethodInfo frombase) { return(frombase); } } return(new PhpMethodInfo(index, name, methods)); }