TypeRefContext
This commit is contained in:
Родитель
6a9aa80f71
Коммит
6cb9b1b306
|
@ -1,6 +1,7 @@
|
|||
using Microsoft.CodeAnalysis;
|
||||
using Pchp.CodeAnalysis.CodeGen;
|
||||
using Pchp.CodeAnalysis.Emit;
|
||||
using Pchp.CodeAnalysis.FlowAnalysis;
|
||||
using Pchp.CodeAnalysis.Semantics;
|
||||
using Pchp.CodeAnalysis.Symbols;
|
||||
using System;
|
||||
|
@ -21,6 +22,8 @@ namespace Pchp.CodeAnalysis
|
|||
readonly PEModuleBuilder _moduleBuilder;
|
||||
readonly bool _emittingPdb;
|
||||
readonly DiagnosticBag _diagnostics;
|
||||
readonly Worklist _worklist;
|
||||
// readonly CallGraph _callgraph; //keeps graph of what methods call specific method // used to reanalyze caller methods when return type ot arg. type changes
|
||||
|
||||
private SourceCompiler(PhpCompilation compilation, PEModuleBuilder moduleBuilder, bool emittingPdb, DiagnosticBag diagnostics)
|
||||
{
|
||||
|
@ -33,8 +36,8 @@ namespace Pchp.CodeAnalysis
|
|||
_emittingPdb = emittingPdb;
|
||||
_diagnostics = diagnostics;
|
||||
|
||||
// var callgraph = new ...
|
||||
// var worklist = new ... // parallel worklist algorithm
|
||||
_worklist = new Worklist(); // parallel worklist algorithm
|
||||
|
||||
// semantic model
|
||||
}
|
||||
|
||||
|
@ -45,7 +48,7 @@ namespace Pchp.CodeAnalysis
|
|||
var methods = sourcesymbols.GetFunctions()
|
||||
.Concat(sourcesymbols.GetTypes().SelectMany(t => t.GetMembers()))
|
||||
.OfType<SourceBaseMethodSymbol>();
|
||||
methods.Foreach(action);
|
||||
methods.ForEach(action);
|
||||
|
||||
// TODO: methodsWalker.VisitNamespace(_compilation.SourceModule.GlobalNamespace)
|
||||
}
|
||||
|
@ -57,11 +60,24 @@ namespace Pchp.CodeAnalysis
|
|||
return method.BoundBlock;
|
||||
}
|
||||
|
||||
internal void AnalyzeMethod(SourceBaseMethodSymbol method)
|
||||
internal void AnalyzeMethods()
|
||||
{
|
||||
//this.WalkMethods(m => worklist.Enlist(m, this.AnalyzeMethod))
|
||||
//worklist.Do
|
||||
|
||||
// DEBUG
|
||||
this.WalkMethods(this.AnalyzeMethod);
|
||||
}
|
||||
|
||||
private void AnalyzeMethod(SourceBaseMethodSymbol method)
|
||||
{
|
||||
Contract.ThrowIfNull(method);
|
||||
|
||||
var bound = method.BoundBlock;
|
||||
|
||||
// Initial State // declared locals, initial types
|
||||
// TypeAnalysis + ResolveSymbols
|
||||
// if (LowerBody(bound)) Enlist(method)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -84,7 +100,7 @@ namespace Pchp.CodeAnalysis
|
|||
CancellationToken cancellationToken)
|
||||
{
|
||||
var compiler = new SourceCompiler(compilation, moduleBuilder, emittingPdb, diagnostics);
|
||||
|
||||
|
||||
// 1. Synthetize magic
|
||||
// a.inline syntax like traits
|
||||
// b.synthetize entry point, getters, setters, ctors, dispose, magic methods, …
|
||||
|
@ -100,7 +116,7 @@ namespace Pchp.CodeAnalysis
|
|||
// b.build global variables/constants table
|
||||
// c.type analysis(converge type - mask), resolve symbols
|
||||
// d.lower semantics, update bound tree, repeat
|
||||
compiler.WalkMethods(compiler.AnalyzeMethod);
|
||||
compiler.AnalyzeMethods();
|
||||
|
||||
// 4. Emit method bodies
|
||||
compiler.WalkMethods(compiler.EmitMethodBody);
|
||||
|
|
|
@ -119,7 +119,7 @@ namespace Pchp.CodeAnalysis.Symbols
|
|||
|
||||
if ((object)this.ContainingType == null &&
|
||||
this.IsDefinition &&
|
||||
this.ContainingModule == moduleBeingBuilt.SourceModule)
|
||||
object.ReferenceEquals(this.ContainingModule, moduleBeingBuilt.SourceModule))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ namespace Pchp.CodeAnalysis.Symbols
|
|||
|
||||
if ((object)this.ContainingType != null &&
|
||||
this.IsDefinition &&
|
||||
this.ContainingModule == moduleBeingBuilt.SourceModule)
|
||||
object.ReferenceEquals(this.ContainingModule, moduleBeingBuilt.SourceModule))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ namespace Pchp.CodeAnalysis.Symbols
|
|||
Debug.Assert(this.IsDefinitionOrDistinct());
|
||||
|
||||
if (this.IsDefinition && // can't be generic instantiation
|
||||
this.ContainingModule == moduleBeingBuilt.SourceModule) // must be declared in the module we are building
|
||||
object.ReferenceEquals(this.ContainingModule, moduleBeingBuilt.SourceModule)) // must be declared in the module we are building
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -193,7 +193,7 @@ namespace Pchp.CodeAnalysis.Symbols
|
|||
PEModuleBuilder moduleBeingBuilt = (PEModuleBuilder)context.Module;
|
||||
|
||||
if (this.IsDefinition &&
|
||||
this.ContainingModule == moduleBeingBuilt.SourceModule)
|
||||
object.ReferenceEquals(this.ContainingModule, moduleBeingBuilt.SourceModule))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
using Pchp.Syntax;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pchp.CodeAnalysis.FlowAnalysis
|
||||
{
|
||||
/// <summary>
|
||||
/// Used by analysis of routine in case we know additional information about called context.
|
||||
/// </summary>
|
||||
public struct CallInfo
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Context of type references used in call info.
|
||||
/// </summary>
|
||||
private readonly TypeRefContext _typeCtx;
|
||||
|
||||
/// <summary>
|
||||
/// Known types of parameters.
|
||||
/// </summary>
|
||||
private readonly TypeRefMask[] _paramsType;
|
||||
|
||||
///// <summary>
|
||||
///// Known parameters value.
|
||||
///// </summary>
|
||||
//public readonly object[] _paramsValue;
|
||||
|
||||
/// <summary>
|
||||
/// Optional. Gets type of <c>static</c> in called context.
|
||||
/// </summary>
|
||||
private readonly TypeRefMask _lateStaticBindType;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Construction
|
||||
|
||||
/// <summary>
|
||||
/// Initializes <see cref="CallInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="ctx">Type context of the caller.</param>
|
||||
/// <param name="paramsCount">Amount of parameters used for the call.</param>
|
||||
public CallInfo(TypeRefContext ctx, int paramsCount)
|
||||
: this(ctx, paramsCount, 0)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes <see cref="CallInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="ctx">Type context of the caller.</param>
|
||||
/// <param name="paramsCount">Amount of parameters used for the call.</param>
|
||||
/// <param name="lateStaticBindType">Type of the <c>self</c> in the caller context.</param>
|
||||
public CallInfo(TypeRefContext ctx, int paramsCount, TypeRefMask lateStaticBindType)
|
||||
: this(ctx, GetParamsTypeArr(paramsCount, TypeRefMask.AnyType), lateStaticBindType)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes <see cref="CallInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="ctx">Type context of the caller.</param>
|
||||
/// <param name="paramsType">Type of parameters used for the call. Length of the array corresponds to the parameters count.</param>
|
||||
public CallInfo(TypeRefContext ctx, TypeRefMask[] paramsType)
|
||||
: this(ctx, paramsType, 0)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes <see cref="CallInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="ctx">Type context of the caller.</param>
|
||||
/// <param name="paramsType">Type of parameters used for the call. Length of the array corresponds to the parameters count.</param>
|
||||
/// <param name="lateStaticBindType">Type of the <c>self</c> in the caller context.</param>
|
||||
public CallInfo(TypeRefContext ctx, TypeRefMask[] paramsType, TypeRefMask lateStaticBindType)
|
||||
{
|
||||
_typeCtx = ctx;
|
||||
_paramsType = paramsType;
|
||||
_lateStaticBindType = (lateStaticBindType.IsSingleType ? lateStaticBindType : 0);
|
||||
}
|
||||
|
||||
private static TypeRefMask[] GetParamsTypeArr(int count, TypeRefMask value)
|
||||
{
|
||||
if (count < 0)
|
||||
return null;
|
||||
|
||||
if (count == 0)
|
||||
return EmptyArray<TypeRefMask>.Instance;
|
||||
|
||||
//
|
||||
var arr = new TypeRefMask[count];
|
||||
for (int i = 0; i < arr.Length; i++)
|
||||
arr[i] = value;
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets known parameters count. If call info is empty, the method gets <c>-1</c>.
|
||||
/// </summary>
|
||||
public int ParametersCount { get { return (_paramsType != null) ? _paramsType.Length : -1; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets actual parameter type if provided. Otherwise <c>void</c>.
|
||||
/// </summary>
|
||||
/// <param name="ctx">Target type context.</param>
|
||||
/// <param name="index">Index of parameter.</param>
|
||||
/// <returns>Type mask of the parameter or <c>void</c>.</returns>
|
||||
public TypeRefMask GetParamType(TypeRefContext/*!*/ctx, int index)
|
||||
{
|
||||
if (ctx == null) throw new ArgumentNullException("ctx");
|
||||
|
||||
if (_typeCtx != null && index >= 0 && index < _paramsType.Length)
|
||||
return ctx.AddToContext(_typeCtx, _paramsType[index]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets actual lates static bind type (type of <c>static</c>) if provided.
|
||||
/// Otherwise <c>void</c>.
|
||||
/// </summary>
|
||||
/// <param name="ctx">Target type context.</param>
|
||||
/// <returns>TYpe mask of <c>static</c> in given context or <c>void</c>.</returns>
|
||||
public TypeRefMask GetLateStaticBindType(TypeRefContext/*!*/ctx)
|
||||
{
|
||||
if (ctx == null) throw new ArgumentNullException("ctx");
|
||||
|
||||
if (_typeCtx != null && !_lateStaticBindType.IsUninitialized)
|
||||
return ctx.AddToContext(_typeCtx, _lateStaticBindType);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// Gets actual parameter value if known. Otherwise <see cref="Helpers.ExpressionValue.UnknownValue"/>.
|
||||
///// </summary>
|
||||
//public object GetParamValue(int index)
|
||||
//{
|
||||
// // if (index >= 0 && index < _paramValues.Length) return _paramValues[index];
|
||||
|
||||
// return Helpers.ExpressionValue.UnknownValue;
|
||||
//}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Pchp.Syntax;
|
||||
using Pchp.Syntax.AST;
|
||||
|
||||
namespace Pchp.CodeAnalysis.FlowAnalysis
|
||||
{
|
||||
/// <summary>
|
||||
/// Reference to a type.
|
||||
/// </summary>
|
||||
public interface ITypeRef : IEquatable<ITypeRef>
|
||||
{
|
||||
/// <summary>
|
||||
/// Full type name.
|
||||
/// </summary>
|
||||
QualifiedName QualifiedName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether the type represents an object (class or interface) and not a primitive type.
|
||||
/// </summary>
|
||||
bool IsObject { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether the type represents an array.
|
||||
/// </summary>
|
||||
bool IsArray { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether the type represents a primitive type.
|
||||
/// </summary>
|
||||
bool IsPrimitiveType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether the type represents a lambda function or <c>callable</c> primitive type;
|
||||
/// </summary>
|
||||
bool IsLambda { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets known keys of the array. Each value is either <c>int</c> or <c>string</c>. Cannot be <c>null</c>.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">In case the type does not represent an array.</exception>
|
||||
IEnumerable<object> Keys { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets merged type information of array items values.
|
||||
/// </summary>
|
||||
TypeRefMask ElementType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets type information of callable return value. This value is valid for lambda functions and instances of closures.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">In case the type does not represent a callable.</exception>
|
||||
TypeRefMask LambdaReturnType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets lambda function signature.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">In case the type does not represent a callable.</exception>
|
||||
Signature LambdaSignature { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type code.
|
||||
/// </summary>
|
||||
PhpDataType TypeCode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Transfers this type reference to the target type context.
|
||||
/// The method may return <c>this</c>, it cannot return <c>null</c>.
|
||||
/// </summary>
|
||||
ITypeRef/*!*/Transfer(TypeRefContext/*!*/source, TypeRefContext/*!*/target);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Pchp.CodeAnalysis.FlowAnalysis
|
||||
{
|
||||
/// <summary>
|
||||
/// PHP value type codes.
|
||||
/// </summary>
|
||||
public enum PhpDataType : int
|
||||
{
|
||||
Undefined = 0,
|
||||
|
||||
Null,
|
||||
Boolean,
|
||||
Long,
|
||||
Double,
|
||||
String,
|
||||
Array,
|
||||
Object,
|
||||
Resource,
|
||||
Callable,
|
||||
|
||||
/// <summary>
|
||||
/// Amount of types within the enum.
|
||||
/// </summary>
|
||||
Count
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper methods for <see cref="PhpDataType"/>.
|
||||
/// </summary>
|
||||
public static class PhpDataTypeEnum
|
||||
{
|
||||
/// <summary>
|
||||
/// Mask which bits corresponds to <see cref="PhpDataType"/> values. Determines which type can be a <c>null</c> value.
|
||||
/// </summary>
|
||||
private const int NullableTypesMask =
|
||||
(1 << (int)PhpDataType.Object) |
|
||||
(1 << (int)PhpDataType.Array) |
|
||||
(1 << (int)PhpDataType.Resource) |
|
||||
(1 << (int)PhpDataType.Callable) |
|
||||
(1 << (int)PhpDataType.Null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether given type is a nullable type.
|
||||
/// </summary>
|
||||
public static bool IsNullable(this PhpDataType code)
|
||||
{
|
||||
return (NullableTypesMask & (1 << (int)code)) != 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
using Pchp.CodeAnalysis.FlowAnalysis;
|
||||
using Pchp.CodeAnalysis.Semantics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pchp.CodeAnalysis.FlowAnalysis
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides helper methods for working with types.
|
||||
/// </summary>
|
||||
internal static class TypeHelpers
|
||||
{
|
||||
///// <summary>
|
||||
///// Gets value determining whether <paramref name="totype"/> type can be assigned from <paramref name="fromtype"/>.
|
||||
///// </summary>
|
||||
///// <param name="totype">Type mask which we check whether is assignable from <paramref name="fromtype"/>.</param>
|
||||
///// <param name="fromtype">Type mask we check whether is equal or is a subclass of <paramref name="totype"/>.</param>
|
||||
///// <param name="ctx">Type context for resolving class names from type mask.</param>
|
||||
///// <param name="model">Helper object which caches class inheritance.</param>
|
||||
///// <remarks>
|
||||
///// Gets <c>true</c>, if <paramref name="totype"/> is equal to or is a base type of <paramref name="fromtype"/>.
|
||||
///// Gets <c>False</c> for <c>void</c> type masks.
|
||||
///// </remarks>
|
||||
//public static bool IsAssignableFrom(this TypeRefMask totype, TypeRefMask fromtype, TypeRefContext/*!*/ctx, ISemanticModel/*!*/model)
|
||||
//{
|
||||
// Debug.Assert(ctx != null);
|
||||
// Debug.Assert(model != null);
|
||||
|
||||
// if ((totype & fromtype & ~(ulong)TypeRefMask.FlagsMask) != 0)
|
||||
// return true; // types are equal (or at least one of them is Any Type)
|
||||
|
||||
// // object <-> unspecified object instance
|
||||
// if ((ctx.IsObject(totype) && ctx.IsObject(fromtype)) &&
|
||||
// (ctx.IsAnObject(totype) || ctx.IsAnObject(fromtype)))
|
||||
// return true;
|
||||
|
||||
// if (IsImplicitConversion(fromtype, totype, ctx, model))
|
||||
// return true;
|
||||
|
||||
// // cut off object types (primitive types do not have subclasses)
|
||||
// var selfObjs = ctx.GetObjectTypes(totype);
|
||||
// if (selfObjs.Count == 0)
|
||||
// return false; // self mask does not represent a class
|
||||
|
||||
// var typeObjs = ctx.GetObjectTypes(fromtype);
|
||||
// if (typeObjs.Count == 0)
|
||||
// return false; // type mask does not represent a class
|
||||
|
||||
// // build inheritance graph and check whether any type from self is a base of anything in type
|
||||
// if (selfObjs.Count == 1 && typeObjs.Count == 1)
|
||||
// return model.IsAssignableFrom(selfObjs[0].QualifiedName, typeObjs[0].QualifiedName);
|
||||
|
||||
// //
|
||||
// return model.IsAssignableFrom(selfObjs.Select(t => t.QualifiedName), typeObjs.Select(t => t.QualifiedName));
|
||||
//}
|
||||
|
||||
///// <summary>
|
||||
///// Determines whether there is an implicit conversion from one type to another.
|
||||
///// </summary>
|
||||
//private static bool IsImplicitConversion(TypeRefMask fromtype, TypeRefMask totype, TypeRefContext/*!*/ctx, ISemanticModel/*!*/model)
|
||||
//{
|
||||
// // TODO: optimize bit operations
|
||||
|
||||
// //
|
||||
// if (ctx.IsArray(totype) && ctx.IsArray(fromtype))
|
||||
// {
|
||||
// // both types are arrays, type may be more specific (int[]) and self just ([])
|
||||
|
||||
// // TODO: check whether their element types are assignable,
|
||||
// // avoid infinite recursion!
|
||||
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// // any callable -> "callable"
|
||||
// if (ctx.IsLambda(totype) && IsCallable(fromtype, ctx, model))
|
||||
// return true;
|
||||
|
||||
// //// allowed conversions
|
||||
|
||||
// // int <-> bool
|
||||
// //if (ctx.IsInteger(totype) && ctx.IsBoolean(fromtype)) // int -> bool
|
||||
// // return true;
|
||||
|
||||
// // int <-> double
|
||||
// if (ctx.IsNumber(fromtype) && ctx.IsNumber(totype)) // TODO: maybe settings for strict number type check
|
||||
// return true;
|
||||
|
||||
// if (ctx.IsString(totype) && IsConversionToString(fromtype, ctx, model))
|
||||
// return true;
|
||||
|
||||
// //
|
||||
// if (ctx.IsNull(fromtype) && ctx.IsNullable(totype))
|
||||
// return true; // NULL can be assigned to any nullable
|
||||
|
||||
// //
|
||||
// return false;
|
||||
//}
|
||||
|
||||
///// <summary>
|
||||
///// Determines whether given type can be converted to string without warning.
|
||||
///// </summary>
|
||||
//internal static bool IsConversionToString(TypeRefMask fromtype, TypeRefContext/*!*/ctx, ISemanticModel/*!*/model)
|
||||
//{
|
||||
// // primitive -> string
|
||||
// if (ctx.IsPrimitiveType(fromtype))
|
||||
// return true;
|
||||
|
||||
// // object with __toString() -> string
|
||||
// if (ctx.IsObject(fromtype) && ctx.GetObjectTypes(fromtype).Any(tref => model.GetClass(tref.QualifiedName).HasMethod(NameUtils.SpecialNames.__toString, model)))
|
||||
// return true;
|
||||
|
||||
// //
|
||||
// return false;
|
||||
//}
|
||||
|
||||
///// <summary>
|
||||
///// Checks whether given type is callable.
|
||||
///// </summary>
|
||||
//internal static bool IsCallable(TypeRefMask type, TypeRefContext/*!*/ctx, ISemanticModel/*!*/model)
|
||||
//{
|
||||
// if (ctx.IsLambda(type) || ctx.IsString(type) || ctx.IsArray(type))
|
||||
// return true;
|
||||
|
||||
// // type has "__invoke" method
|
||||
// if (type.IsSingleType) // just optimization
|
||||
// {
|
||||
// var tref = ctx.GetObjectTypes(type).FirstOrDefault();
|
||||
// if (tref != null)
|
||||
// {
|
||||
// var node = model.GetClass(tref.QualifiedName);
|
||||
// // type has __invoke method or is assignable from Closure
|
||||
// if (node.HasMethod(NameUtils.SpecialNames.__invoke, model) || model.IsAssignableFrom(NameUtils.SpecialNames.Closure, node))
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
|
||||
// return false;
|
||||
//}
|
||||
|
||||
///// <summary>
|
||||
///// Gets value indicating whether specified object of type <paramref name="type"/> can handle <c>[]</c> operator.
|
||||
///// </summary>
|
||||
///// <param name="type">Type of the object.</param>
|
||||
///// <param name="ctx">Type context.</param>
|
||||
///// <param name="model">Type graph.</param>
|
||||
///// <returns>True iff <c>[]</c> operator is allowed.</returns>
|
||||
//internal static bool HasArrayAccess(TypeRefMask type, TypeRefContext/*!*/ctx, ISemanticModel/*!*/model)
|
||||
//{
|
||||
// //
|
||||
// if (type.IsAnyType || type.IsVoid || ctx.IsArray(type) || ctx.IsString(type))
|
||||
// return true;
|
||||
|
||||
// // object implementing ArrayAccess
|
||||
// if (ctx.IsObject(type))
|
||||
// {
|
||||
// var types = ctx.GetObjectTypes(type);
|
||||
// foreach (var t in types)
|
||||
// if (model.IsAssignableFrom(NameUtils.SpecialNames.ArrayAccess, t.QualifiedName))
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// //
|
||||
// return false;
|
||||
//}
|
||||
|
||||
///// <summary>
|
||||
///// Gets value indicating whether specified object of type <paramref name="type"/> can be used as <c>foreach</c> enumerable variable.
|
||||
///// </summary>
|
||||
///// <param name="type">Type of the object.</param>
|
||||
///// <param name="ctx">Type context.</param>
|
||||
///// <param name="model">Type graph.</param>
|
||||
///// <returns>True iff <paramref name="type"/> is allowed as <c>foreach</c> enumerator.</returns>
|
||||
//internal static bool IsTraversable(TypeRefMask type, TypeRefContext/*!*/ctx, ISemanticModel/*!*/model)
|
||||
//{
|
||||
// //
|
||||
// if (type.IsAnyType || type.IsVoid || ctx.IsArray(type))
|
||||
// return true;
|
||||
|
||||
// // object implementing Traversable
|
||||
// if (ctx.IsObject(type))
|
||||
// {
|
||||
// var types = ctx.GetObjectTypes(type);
|
||||
// foreach (var t in types)
|
||||
// if (model.IsAssignableFrom(NameUtils.SpecialNames.Traversable, t.QualifiedName))
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// //
|
||||
// return false;
|
||||
//}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,379 @@
|
|||
using Pchp.Syntax;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using AST = Pchp.Syntax.AST;
|
||||
|
||||
namespace Pchp.CodeAnalysis.FlowAnalysis
|
||||
{
|
||||
#region ITypeRef Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Represents a direct type reference.
|
||||
/// </summary>
|
||||
internal class ClassTypeRef : ITypeRef, IEquatable<ClassTypeRef>
|
||||
{
|
||||
private readonly QualifiedName _qname;
|
||||
|
||||
public ClassTypeRef(QualifiedName qname)
|
||||
{
|
||||
Debug.Assert(!string.IsNullOrEmpty(qname.Name.Value));
|
||||
Debug.Assert(!qname.IsReservedClassName); // not self, parent, static
|
||||
Debug.Assert(!qname.IsPrimitiveTypeName); // use PrimitiveTypeRef instead
|
||||
_qname = qname;
|
||||
}
|
||||
|
||||
#region ITypeRef Members
|
||||
|
||||
public QualifiedName QualifiedName { get { return _qname; } }
|
||||
|
||||
public bool IsObject { get { return true; } }
|
||||
|
||||
public bool IsArray { get { return false; } }
|
||||
|
||||
public bool IsPrimitiveType { get { return false; } }
|
||||
|
||||
public bool IsLambda { get { return false; } }
|
||||
|
||||
public IEnumerable<object> Keys { get { throw new InvalidOperationException(); } }
|
||||
|
||||
public TypeRefMask ElementType { get { return default(TypeRefMask); } }
|
||||
|
||||
public TypeRefMask LambdaReturnType { get { throw new InvalidOperationException(); } }
|
||||
|
||||
public AST.Signature LambdaSignature { get { throw new InvalidOperationException(); } }
|
||||
|
||||
public PhpDataType TypeCode { get { return PhpDataType.Object; } }
|
||||
|
||||
public ITypeRef/*!*/Transfer(TypeRefContext/*!*/source, TypeRefContext/*!*/target) { return this; } // there is nothing depending on the context
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEquatable<ITypeRef> Members
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _qname.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as ClassTypeRef);
|
||||
}
|
||||
|
||||
public bool Equals(ITypeRef other)
|
||||
{
|
||||
return Equals(other as ClassTypeRef);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEquatable<ClassTypeRef> Members
|
||||
|
||||
public bool Equals(ClassTypeRef other)
|
||||
{
|
||||
return other != null && other._qname == _qname;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an array type.
|
||||
/// </summary>
|
||||
internal sealed class ArrayTypeRef : ITypeRef, IEquatable<ArrayTypeRef>
|
||||
{
|
||||
// TODO: manage keys, handle duplicities (HashSet?), fast merging
|
||||
// flag whether there might be more keys in addition to listed\
|
||||
// limit number of keys, ignore numeric keys? remember just strings and constant names
|
||||
|
||||
private readonly HashSet<object> _keys;
|
||||
private readonly TypeRefMask _elementType;
|
||||
|
||||
public ArrayTypeRef(IEnumerable<object> keys, TypeRefMask elementType)
|
||||
{
|
||||
_keys = null;// new HashSet<object>(keys);
|
||||
_elementType = elementType;
|
||||
}
|
||||
|
||||
#region ITypeRef Members
|
||||
|
||||
public QualifiedName QualifiedName
|
||||
{
|
||||
get { return QualifiedName.Array; }
|
||||
}
|
||||
|
||||
public bool IsObject { get { return false; } }
|
||||
|
||||
public bool IsArray { get { return true; } }
|
||||
|
||||
public bool IsPrimitiveType { get { return true; } }
|
||||
|
||||
public bool IsLambda { get { return false; } }
|
||||
|
||||
public IEnumerable<object> Keys { get { return (IEnumerable<object>)_keys ?? ArrayUtils.EmptyObjects; } }
|
||||
|
||||
public TypeRefMask ElementType { get { return _elementType; } }
|
||||
|
||||
public TypeRefMask LambdaReturnType { get { throw new InvalidOperationException(); } }
|
||||
|
||||
public AST.Signature LambdaSignature { get { throw new InvalidOperationException(); } }
|
||||
|
||||
public PhpDataType TypeCode { get { return PhpDataType.Array; } }
|
||||
|
||||
public ITypeRef/*!*/Transfer(TypeRefContext/*!*/source, TypeRefContext/*!*/target)
|
||||
{
|
||||
Contract.ThrowIfNull(source);
|
||||
Contract.ThrowIfNull(target);
|
||||
|
||||
// TODO: keys
|
||||
|
||||
if (source == target || _elementType.IsVoid || _elementType.IsAnyType)
|
||||
return this;
|
||||
|
||||
// note: there should be no circular dependency
|
||||
return new ArrayTypeRef(_keys, target.AddToContext(source, _elementType));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEquatable<ITypeRef> Members
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as ArrayTypeRef);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ~_elementType.GetHashCode();
|
||||
}
|
||||
|
||||
public bool Equals(ITypeRef other)
|
||||
{
|
||||
return Equals(other as ArrayTypeRef);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEquatable<ArrayTypeRef> Members
|
||||
|
||||
public bool Equals(ArrayTypeRef other)
|
||||
{
|
||||
return other != null && other._elementType == _elementType; // TODO: keys
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a PHP primitive type.
|
||||
/// </summary>
|
||||
internal sealed class PrimitiveTypeRef : ITypeRef, IEquatable<PrimitiveTypeRef>
|
||||
{
|
||||
private readonly PhpDataType _code;
|
||||
|
||||
public PrimitiveTypeRef(PhpDataType code)
|
||||
{
|
||||
_code = code;
|
||||
}
|
||||
|
||||
#region ITypeRef Members
|
||||
|
||||
public QualifiedName QualifiedName
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (_code)
|
||||
{
|
||||
case PhpDataType.Null: return QualifiedName.Null;
|
||||
case PhpDataType.Boolean: return QualifiedName.Boolean;
|
||||
case PhpDataType.Long: return QualifiedName.Integer;
|
||||
case PhpDataType.Double: return QualifiedName.Double;
|
||||
case PhpDataType.String: return QualifiedName.String;
|
||||
case PhpDataType.Array: return QualifiedName.Array;
|
||||
case PhpDataType.Object: return QualifiedName.Object;
|
||||
case PhpDataType.Resource: return QualifiedName.Resource;
|
||||
case PhpDataType.Callable: return QualifiedName.Callable;
|
||||
default:
|
||||
throw new ArgumentException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsObject { get { return _code == PhpDataType.Object; } }
|
||||
|
||||
public bool IsArray { get { return _code == PhpDataType.Array; } }
|
||||
|
||||
public bool IsPrimitiveType { get { return true; } }
|
||||
|
||||
public bool IsLambda { get { return _code == PhpDataType.Callable; } }
|
||||
|
||||
public IEnumerable<object> Keys { get { return null; } }
|
||||
|
||||
public TypeRefMask ElementType { get { return TypeRefMask.AnyType; } }
|
||||
|
||||
public TypeRefMask LambdaReturnType { get { return TypeRefMask.AnyType; } }
|
||||
|
||||
public AST.Signature LambdaSignature { get { return default(AST.Signature); } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets underlaying type code of the primitive type.
|
||||
/// </summary>
|
||||
public PhpDataType TypeCode { get { return _code; } }
|
||||
|
||||
public ITypeRef/*!*/Transfer(TypeRefContext/*!*/source, TypeRefContext/*!*/target) { return this; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEquatable<ITypeRef> Members
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (int)_code;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as PrimitiveTypeRef);
|
||||
}
|
||||
|
||||
public bool Equals(ITypeRef other)
|
||||
{
|
||||
return Equals(other as PrimitiveTypeRef);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEquatable<PrimitiveTypeRef> Members
|
||||
|
||||
public bool Equals(PrimitiveTypeRef other)
|
||||
{
|
||||
return other != null && other._code == _code;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a lambda function with known return type and parameters optionally.
|
||||
/// </summary>
|
||||
internal sealed class LambdaTypeRef : ITypeRef, IEquatable<LambdaTypeRef>
|
||||
{
|
||||
private readonly TypeRefMask _returnType;
|
||||
private readonly AST.Signature _signature;
|
||||
|
||||
public LambdaTypeRef(TypeRefMask returnType, AST.Signature signature)
|
||||
{
|
||||
_returnType = returnType;
|
||||
_signature = signature;
|
||||
}
|
||||
|
||||
#region ITypeRef Members
|
||||
|
||||
/// <summary>
|
||||
/// Lambda function is of sub class of <c>Closure</c>.
|
||||
/// </summary>
|
||||
public QualifiedName QualifiedName
|
||||
{
|
||||
get { return NameUtils.SpecialNames.Closure; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lambda function is an object of type <c>Closure</c>.
|
||||
/// This handles case when lambda is used as object with methods <c>bindTo</c> or <c>__invoke</c>.
|
||||
/// </summary>
|
||||
public bool IsObject
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool IsArray
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool IsPrimitiveType
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool IsLambda
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public IEnumerable<object> Keys
|
||||
{
|
||||
get { throw new InvalidOperationException(); }
|
||||
}
|
||||
|
||||
public TypeRefMask ElementType
|
||||
{
|
||||
get { throw new InvalidOperationException(); }
|
||||
}
|
||||
|
||||
public TypeRefMask LambdaReturnType { get { return _returnType; } }
|
||||
|
||||
public AST.Signature LambdaSignature { get { return _signature; } }
|
||||
|
||||
public PhpDataType TypeCode { get { return PhpDataType.Callable; } }
|
||||
|
||||
public ITypeRef Transfer(TypeRefContext source, TypeRefContext target)
|
||||
{
|
||||
if (source == target || _returnType.IsVoid || _returnType.IsAnyType)
|
||||
return this;
|
||||
|
||||
// note: there should be no circular dependency
|
||||
return new LambdaTypeRef(target.AddToContext(source, _returnType), _signature);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEquatable<ITypeRef> Members
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _returnType.GetHashCode() ^ 0x777;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as LambdaTypeRef);
|
||||
}
|
||||
|
||||
public bool Equals(ITypeRef other)
|
||||
{
|
||||
return Equals(other as LambdaTypeRef);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEquatable<LambdaTypeRef> Members
|
||||
|
||||
public bool Equals(LambdaTypeRef other)
|
||||
{
|
||||
return other != null && other._returnType.Equals(_returnType) && Equals(other._signature, _signature);
|
||||
}
|
||||
|
||||
private static bool Equals(AST.FormalParam[] params1, AST.FormalParam[] params2)
|
||||
{
|
||||
if (params1.Length == params2.Length)
|
||||
{
|
||||
for (int i = 0; i < params1.Length; i++)
|
||||
if (params1[i].Name != params2[i].Name)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
|
@ -0,0 +1,805 @@
|
|||
using AST = Pchp.Syntax.AST;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Pchp.Syntax;
|
||||
using Pchp.CodeAnalysis.Utilities;
|
||||
|
||||
namespace Pchp.CodeAnalysis.FlowAnalysis
|
||||
{
|
||||
/// <summary>
|
||||
/// Context of <see cref="TypeRefMask"/> and <see cref="ITypeRef"/> instances.
|
||||
/// Contains additional information for routine context like current namespace, current type context etc.
|
||||
/// </summary>
|
||||
public sealed class TypeRefContext
|
||||
{
|
||||
#region Fields & Properties
|
||||
|
||||
/// <summary>
|
||||
/// Bit masks initialized when such type is added to the context.
|
||||
/// Its bits corresponds to <see cref="_typeRefs"/> indices.
|
||||
/// </summary>
|
||||
private ulong _isObjectMask, _isArrayMask, _isIntMask, _isDoubleMask, _isBoolMask, _isStringMask, _isPrimitiveMask, _isLambdaMask, _isResourceMask, _nullTypeMask, _AnObjectMask;
|
||||
private ulong IsNumberMask { get { return _isIntMask | _isDoubleMask; } }
|
||||
private ulong IsNullableMask { get { return _isObjectMask | _isArrayMask | _isStringMask | _isLambdaMask | _nullTypeMask | _isResourceMask; } }
|
||||
|
||||
/// <summary>
|
||||
/// Allowed types for array key.
|
||||
/// </summary>
|
||||
private ulong IsArrayKeyMask { get { return _isStringMask | _isBoolMask | _isIntMask | _isDoubleMask | _isResourceMask; } }
|
||||
|
||||
/// <summary>
|
||||
/// List of types occuring in the context.
|
||||
/// </summary>
|
||||
private readonly List<ITypeRef>/*!*/_typeRefs;
|
||||
|
||||
/// <summary>
|
||||
/// Name of <c>parent</c> class type if any.
|
||||
/// </summary>
|
||||
private readonly QualifiedName? _parentTypeCtx;
|
||||
|
||||
/// <summary>
|
||||
/// contains type mask of <c>self</c> type with <c>includesSubclasses</c> flag set whether type is not final.
|
||||
/// </summary>
|
||||
private readonly TypeRefMask _typeCtxMask;
|
||||
|
||||
/// <summary>
|
||||
/// When resolved, contains type mask of <c>parent</c> type.
|
||||
/// </summary>
|
||||
private TypeRefMask _parentTypeMask;
|
||||
|
||||
/// <summary>
|
||||
/// When resolved, contains type mask of <c>static</c> type.
|
||||
/// </summary>
|
||||
private TypeRefMask _staticTypeMask;
|
||||
|
||||
/// <summary>
|
||||
/// Current naming context. Used for resolving PHPDoc type names, current namespace name etc. Can be <c>null</c>.
|
||||
/// </summary>
|
||||
public NamingContext Naming { get { return _namingCtx; } }
|
||||
private readonly NamingContext _namingCtx;
|
||||
|
||||
/// <summary>
|
||||
/// Current source unit. Used for resolving current file name, elements position etc. Can be <c>null</c>.
|
||||
/// </summary>
|
||||
public SourceUnit SourceUnit { get { return _sourceUnit; } }
|
||||
private readonly SourceUnit _sourceUnit;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Primitive Types
|
||||
|
||||
internal static readonly PrimitiveTypeRef/*!*/NullTypeRef = new PrimitiveTypeRef(PhpDataType.Null);
|
||||
internal static readonly PrimitiveTypeRef/*!*/BoolTypeRef = new PrimitiveTypeRef(PhpDataType.Boolean);
|
||||
internal static readonly PrimitiveTypeRef/*!*/IntTypeRef = new PrimitiveTypeRef(PhpDataType.Long);
|
||||
internal static readonly PrimitiveTypeRef/*!*/DoubleTypeRef = new PrimitiveTypeRef(PhpDataType.Double);
|
||||
internal static readonly PrimitiveTypeRef/*!*/StringTypeRef = new PrimitiveTypeRef(PhpDataType.String);
|
||||
internal static readonly PrimitiveTypeRef/*!*/ArrayTypeRef = new PrimitiveTypeRef(PhpDataType.Array);
|
||||
internal static readonly PrimitiveTypeRef/*!*/ObjectTypeRef = new PrimitiveTypeRef(PhpDataType.Object);
|
||||
internal static readonly PrimitiveTypeRef/*!*/ResourceTypeRef = new PrimitiveTypeRef(PhpDataType.Resource);
|
||||
internal static readonly PrimitiveTypeRef/*!*/CallableTypeRef = new PrimitiveTypeRef(PhpDataType.Callable);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Initialization
|
||||
|
||||
public TypeRefContext(NamingContext naming, SourceUnit sourceUnit, AST.TypeDecl typeCtx, QualifiedName? parentTypeCtx)
|
||||
{
|
||||
Debug.Assert(!parentTypeCtx.HasValue || typeCtx != null);
|
||||
|
||||
_namingCtx = naming;
|
||||
_sourceUnit = sourceUnit;
|
||||
_parentTypeCtx = parentTypeCtx;
|
||||
_typeRefs = new List<ITypeRef>();
|
||||
_typeCtxMask = GetTypeCtxMask(typeCtx);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets type mask corresponding to <c>self</c> with <c>includesSubclasses</c> flag set whether type is not final.
|
||||
/// </summary>
|
||||
private TypeRefMask GetTypeCtxMask(AST.TypeDecl typeCtx)
|
||||
{
|
||||
if (typeCtx != null)
|
||||
{
|
||||
var typeIsFinal = (typeCtx.MemberAttributes & PhpMemberAttributes.Final) != 0;
|
||||
return GetTypeMask(new ClassTypeRef(NameUtils.MakeQualifiedName(typeCtx)), !typeIsFinal);
|
||||
}
|
||||
|
||||
//
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicitly defines late static bind type (type of <c>static</c>).
|
||||
/// </summary>
|
||||
/// <param name="staticTypeMask">Type mask of <c>static</c> or <c>void</c> if this information is unknown.</param>
|
||||
internal void SetLateStaticBindType(TypeRefMask staticTypeMask)
|
||||
{
|
||||
_staticTypeMask = staticTypeMask;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region AddToContext
|
||||
|
||||
/// <summary>
|
||||
/// Ensures given type is in the context.
|
||||
/// </summary>
|
||||
/// <param name="typeRef">Type reference to be in the context.</param>
|
||||
/// <returns>Index of the type within the context. Can return <c>-1</c> if there is too many types in the context already.</returns>
|
||||
public int AddToContext(ITypeRef/*!*/typeRef)
|
||||
{
|
||||
Contract.ThrowIfNull(typeRef);
|
||||
|
||||
var types = _typeRefs;
|
||||
var index = this.GetTypeIndex(typeRef);
|
||||
if (index < 0 && this.Types.Count < TypeRefMask.IndicesCount)
|
||||
index = this.AddToContextNoCheck(typeRef);
|
||||
|
||||
//
|
||||
return index;
|
||||
}
|
||||
|
||||
private int AddToContextNoCheck(ITypeRef/*!*/typeRef)
|
||||
{
|
||||
Contract.ThrowIfNull(typeRef);
|
||||
Debug.Assert(_typeRefs.IndexOf(typeRef) == -1);
|
||||
|
||||
int index = _typeRefs.Count;
|
||||
this.UpdateMasks(typeRef, index);
|
||||
|
||||
_typeRefs.Add(typeRef);
|
||||
|
||||
//
|
||||
return index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates internal masks for newly added type.
|
||||
/// </summary>
|
||||
/// <param name="typeRef">Type.</param>
|
||||
/// <param name="index">Type index.</param>
|
||||
private void UpdateMasks(ITypeRef/*!*/typeRef, int index)
|
||||
{
|
||||
Debug.Assert(index >= 0 && index < TypeRefMask.IndicesCount);
|
||||
|
||||
ulong mask = (ulong)1 << index;
|
||||
|
||||
if (typeRef.IsObject) _isObjectMask |= mask;
|
||||
if (typeRef.IsArray) _isArrayMask |= mask;
|
||||
if (typeRef.IsLambda) _isLambdaMask |= mask;
|
||||
|
||||
if (typeRef.IsPrimitiveType)
|
||||
{
|
||||
_isPrimitiveMask |= mask;
|
||||
switch (typeRef.TypeCode)
|
||||
{
|
||||
case PhpDataType.Boolean:
|
||||
_isBoolMask = mask;
|
||||
break;
|
||||
case PhpDataType.Long:
|
||||
_isIntMask = mask;
|
||||
break;
|
||||
case PhpDataType.Double:
|
||||
_isDoubleMask = mask;
|
||||
break;
|
||||
case PhpDataType.String:
|
||||
_isStringMask = mask;
|
||||
break;
|
||||
case PhpDataType.Null:
|
||||
_nullTypeMask = mask;
|
||||
break;
|
||||
case PhpDataType.Resource:
|
||||
_isResourceMask |= mask;
|
||||
break;
|
||||
case PhpDataType.Object:
|
||||
_AnObjectMask = mask;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds properly types from another context.
|
||||
/// </summary>
|
||||
/// <param name="other">Another type context which types will be added to this one.</param>
|
||||
internal void AddToContext(TypeRefContext/*!*/other)
|
||||
{
|
||||
Contract.ThrowIfNull(other);
|
||||
|
||||
foreach (var typeref in other.Types)
|
||||
{
|
||||
AddToContext(typeref.Transfer(other, this));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds properly types from another context matching given mask.
|
||||
/// </summary>
|
||||
/// <param name="context">Context of <paramref name="mask"/>.</param>
|
||||
/// <param name="mask">Type mask representing types in <paramref name="context"/>.</param>
|
||||
/// <returns>Returns type mask in this context representing <paramref name="mask"/> as <paramref name="context"/>.</returns>
|
||||
public TypeRefMask AddToContext(TypeRefContext/*!*/context, TypeRefMask mask)
|
||||
{
|
||||
Contract.ThrowIfNull(context);
|
||||
|
||||
if (mask.IsAnyType || mask.IsVoid || object.ReferenceEquals(this, context))
|
||||
return mask;
|
||||
|
||||
var result = default(TypeRefMask);
|
||||
|
||||
var types = context.Types;
|
||||
var count = Math.Min(types.Count, TypeRefMask.IndicesCount);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (mask.HasType(i))
|
||||
{
|
||||
var index = AddToContext(types[i].Transfer(context, this));
|
||||
result.AddType(index);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
result.IsHint = mask.IsHint;
|
||||
result.IncludesSubclasses = mask.IncludesSubclasses;
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets enumeration of types matching given masks.
|
||||
/// </summary>
|
||||
private IList<ITypeRef>/*!!*/GetTypes(TypeRefMask typemask, ulong bitmask)
|
||||
{
|
||||
var mask = typemask.Mask & bitmask & ~TypeRefMask.FlagsMask;
|
||||
if (mask == (ulong)0 || typemask.IsAnyType)
|
||||
return EmptyArray<ITypeRef>.Instance;
|
||||
|
||||
var result = new List<ITypeRef>(1);
|
||||
for (int i = 0; mask != 0; i++, mask = (mask & ~(ulong)1) >> 1)
|
||||
if ((mask & 1) != 0)
|
||||
{
|
||||
result.Add(_typeRefs[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private TypeRefMask GetPrimitiveTypeRefMask(PrimitiveTypeRef/*!*/typeref)
|
||||
{
|
||||
// primitive type cannot include subclasses
|
||||
var index = AddToContext(typeref);
|
||||
return TypeRefMask.CreateFromTypeIndex(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does not lookup existing types whether there is typeref already.
|
||||
/// </summary>
|
||||
private TypeRefMask GetPrimitiveTypeRefMaskNoCheck(PrimitiveTypeRef/*!*/typeref)
|
||||
{
|
||||
if (this.Types.Count < TypeRefMask.IndicesCount)
|
||||
{
|
||||
var index = AddToContextNoCheck(typeref);
|
||||
return TypeRefMask.CreateFromTypeIndex(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
return TypeRefMask.AnyType;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets type mask for reserved class name (self, parent, static).
|
||||
/// </summary>
|
||||
private TypeRefMask GetTypeMaskOfReservedClassName(Name name)
|
||||
{
|
||||
if (name == Name.SelfClassName) return GetSelfTypeMask();
|
||||
if (name == Name.ParentClassName) return GetParentTypeMask();
|
||||
if (name == Name.StaticClassName) return GetStaticTypeMask();
|
||||
|
||||
throw new ArgumentException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetTypeMask
|
||||
|
||||
/// <summary>
|
||||
/// Helper method that builds <see cref="TypeRefMask"/> for given type in this context.
|
||||
/// </summary>
|
||||
public TypeRefMask GetTypeMask(ITypeRef/*!*/typeref, bool includesSubclasses)
|
||||
{
|
||||
var index = AddToContext(typeref);
|
||||
var mask = TypeRefMask.CreateFromTypeIndex(index);
|
||||
|
||||
if (includesSubclasses)
|
||||
mask.SetIncludesSubclasses();
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets type mask corresponding to given qualified name within this context.
|
||||
/// </summary>
|
||||
public TypeRefMask GetTypeMask(QualifiedName qname, bool includesSubclasses = true)
|
||||
{
|
||||
if (qname.IsReservedClassName)
|
||||
return GetTypeMaskOfReservedClassName(qname.Name);
|
||||
|
||||
return GetTypeMask(new ClassTypeRef(qname), includesSubclasses);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets type mask corresponding to given TypeRef within this context.
|
||||
/// </summary>
|
||||
public TypeRefMask GetTypeMask(AST.TypeRef/*!*/tref, bool includesSubclasses = true)
|
||||
{
|
||||
Contract.ThrowIfNull(tref);
|
||||
|
||||
var dtype = tref as AST.DirectTypeRef;
|
||||
if (dtype != null)
|
||||
{
|
||||
return GetTypeMask(dtype.ClassName, includesSubclasses);
|
||||
}
|
||||
else
|
||||
{
|
||||
var itype = tref as AST.IndirectTypeRef;
|
||||
if (itype != null)
|
||||
{
|
||||
return GetTypeMask(itype, includesSubclasses);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return TypeRefMask.AnyType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets type mask corresponding to given TypeRef within this context.
|
||||
/// </summary>
|
||||
private TypeRefMask GetTypeMask(AST.IndirectTypeRef/*!*/tref, bool includesSubclasses)
|
||||
{
|
||||
Contract.ThrowIfNull(tref);
|
||||
|
||||
var dvar = tref.ClassNameVar as AST.DirectVarUse;
|
||||
if (dvar != null && dvar.IsMemberOf == null && dvar.VarName.IsThisVariableName)
|
||||
return GetThisTypeMask();
|
||||
|
||||
//
|
||||
return TypeRefMask.AnyType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets type mask corresponding to the parameter type hint.
|
||||
/// </summary>
|
||||
/// <param name="typeHint">Type hint. Can be <c>null</c>, <see cref="GenericQualifiedName"/> or <see cref="PrimitiveTypeName"/>.</param>
|
||||
/// <returns>Type mask of type hint if it was provided, otherwise AnyType.</returns>
|
||||
public TypeRefMask GetTypeMaskFromTypeHint(object typeHint)
|
||||
{
|
||||
if (typeHint != null)
|
||||
{
|
||||
var value = new TypeHintValue(typeHint);
|
||||
|
||||
if (value.IsGenericQualifiedName)
|
||||
{
|
||||
return GetTypeMask(value.GenericQualifiedName.QualifiedName);
|
||||
}
|
||||
|
||||
if (value.IsPrimitiveType)
|
||||
{
|
||||
var pname = value.PrimitiveTypeName.Name;
|
||||
if (pname == QualifiedName.Callable.Name) return GetPrimitiveTypeRefMask(CallableTypeRef);
|
||||
if (pname == QualifiedName.Array.Name) return GetArrayTypeMask();
|
||||
if (pname == QualifiedName.String.Name) return GetStringTypeMask();
|
||||
if (pname == QualifiedName.Boolean.Name) return GetBooleanTypeMask();
|
||||
if (pname == QualifiedName.Integer.Name) return GetIntTypeMask();
|
||||
if (pname == QualifiedName.LongInteger.Name) return GetIntTypeMask();
|
||||
if (pname == QualifiedName.Double.Name) return GetDoubleTypeMask();
|
||||
}
|
||||
}
|
||||
|
||||
return TypeRefMask.AnyType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets <c>string</c> type for this context.
|
||||
/// </summary>
|
||||
public TypeRefMask GetStringTypeMask()
|
||||
{
|
||||
if (_isStringMask != 0)
|
||||
{
|
||||
return new TypeRefMask(_isStringMask);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetPrimitiveTypeRefMaskNoCheck(StringTypeRef);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets <c>int</c> type for this context.
|
||||
/// </summary>
|
||||
public TypeRefMask GetIntTypeMask()
|
||||
{
|
||||
if (_isIntMask != 0)
|
||||
{
|
||||
return new TypeRefMask(_isIntMask);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetPrimitiveTypeRefMaskNoCheck(IntTypeRef);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets <c>bool</c> type for this context.
|
||||
/// </summary>
|
||||
public TypeRefMask GetBooleanTypeMask()
|
||||
{
|
||||
if (_isBoolMask != 0)
|
||||
{
|
||||
return new TypeRefMask(_isBoolMask);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetPrimitiveTypeRefMaskNoCheck(BoolTypeRef);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets <c>double</c> type for this context.
|
||||
/// </summary>
|
||||
public TypeRefMask GetDoubleTypeMask()
|
||||
{
|
||||
if (_isDoubleMask != 0)
|
||||
{
|
||||
return new TypeRefMask(_isDoubleMask);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetPrimitiveTypeRefMaskNoCheck(DoubleTypeRef);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets <c>number</c> (<c>int</c> and <c>double</c>) type for this context.
|
||||
/// </summary>
|
||||
public TypeRefMask GetNumberTypeMask()
|
||||
{
|
||||
return GetIntTypeMask() | GetDoubleTypeMask();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets <c>null</c> type for this context.
|
||||
/// </summary>
|
||||
public TypeRefMask GetNullTypeMask()
|
||||
{
|
||||
if (_nullTypeMask != 0)
|
||||
{
|
||||
return new TypeRefMask(_nullTypeMask);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetPrimitiveTypeRefMaskNoCheck(NullTypeRef);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets type mask of generic <c>array</c> with element of any type.
|
||||
/// </summary>
|
||||
public TypeRefMask GetArrayTypeMask()
|
||||
{
|
||||
return GetPrimitiveTypeRefMask(ArrayTypeRef);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets type mask of <c>array</c> with elements of given type.
|
||||
/// </summary>
|
||||
public TypeRefMask GetArrayTypeMask(TypeRefMask elementType)
|
||||
{
|
||||
TypeRefMask result;
|
||||
|
||||
if (elementType.IsAnyType)
|
||||
{
|
||||
result = GetArrayTypeMask(); // generic array
|
||||
}
|
||||
else if (elementType.IsVoid)
|
||||
{
|
||||
result = GetTypeMask(new ArrayTypeRef(null, 0), false); // empty array
|
||||
}
|
||||
else if (elementType.IsSingleType)
|
||||
{
|
||||
result = GetTypeMask(new ArrayTypeRef(null, elementType), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = 0;
|
||||
|
||||
if ((elementType & _isArrayMask) != 0) // array elements contain another arrays
|
||||
{
|
||||
// simplify this to generic arrays
|
||||
elementType &= ~_isArrayMask;
|
||||
elementType |= GetArrayTypeMask();
|
||||
}
|
||||
|
||||
// construct array type mask from array types with single element type
|
||||
|
||||
// go through all array types
|
||||
var mask = elementType & ~(ulong)TypeRefMask.FlagsMask;
|
||||
for (int i = 0; mask != 0; i++, mask = (mask & ~(ulong)1) >> 1)
|
||||
if ((mask & 1) != 0) // _typeRefs[i].IsArray
|
||||
{
|
||||
result |= GetTypeMask(new ArrayTypeRef(null, (ulong)1 << i), false);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
result.IsHint = elementType.IsHint;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets <c>self</c> type for this context.
|
||||
/// </summary>
|
||||
public TypeRefMask GetSelfTypeMask()
|
||||
{
|
||||
var result = _typeCtxMask;
|
||||
result.IncludesSubclasses = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets type of <c>$this</c> in current context.
|
||||
/// </summary>
|
||||
public TypeRefMask GetThisTypeMask()
|
||||
{
|
||||
return _typeCtxMask; // the same as $this
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets <c>parent</c> type for this context.
|
||||
/// </summary>
|
||||
public TypeRefMask GetParentTypeMask()
|
||||
{
|
||||
if (_parentTypeMask == 0 && _parentTypeCtx.HasValue)
|
||||
_parentTypeMask = GetTypeMask(new ClassTypeRef(_parentTypeCtx.Value), false);
|
||||
|
||||
return _parentTypeMask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets <c>static</c> type for this context.
|
||||
/// </summary>
|
||||
public TypeRefMask GetStaticTypeMask()
|
||||
{
|
||||
if (_staticTypeMask == 0)
|
||||
{
|
||||
var mask = _typeCtxMask;
|
||||
if (mask != 0)
|
||||
mask.IncludesSubclasses = true;
|
||||
|
||||
_staticTypeMask = mask;
|
||||
}
|
||||
|
||||
return _staticTypeMask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets mask representing only array types in given mask.
|
||||
/// (Only bits corresponding to an array type will be set).
|
||||
/// </summary>
|
||||
public TypeRefMask GetArraysFromMask(TypeRefMask mask)
|
||||
{
|
||||
if (mask.IsAnyType) return 0;
|
||||
return mask & _isArrayMask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets mask representing only object types in given mask.
|
||||
/// (Only bits corresponding to an object type will be set).
|
||||
/// </summary>
|
||||
public TypeRefMask GetObjectsFromMask(TypeRefMask mask)
|
||||
{
|
||||
if (mask.IsAnyType) return 0;
|
||||
return mask & _isObjectMask;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets enumeration of all types in the context.
|
||||
/// </summary>
|
||||
public IList<ITypeRef>/*!*/Types { get { return _typeRefs; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets types referenced by given type mask.
|
||||
/// </summary>
|
||||
public IList<ITypeRef>/*!*/GetTypes(TypeRefMask mask)
|
||||
{
|
||||
return GetTypes(mask, TypeRefMask.AnyTypeMask);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets types of type <c>object</c> (classes, interfaces, traits) referenced by given type mask.
|
||||
/// </summary>
|
||||
public IList<ITypeRef>/*!*/GetObjectTypes(TypeRefMask mask)
|
||||
{
|
||||
if (mask.IsAnyType)
|
||||
return EmptyArray<ITypeRef>.Instance;
|
||||
|
||||
return GetTypes(mask, _isObjectMask);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets string representation of types contained in given type mask.
|
||||
/// </summary>
|
||||
public string ToString(TypeRefMask mask)
|
||||
{
|
||||
if (!mask.IsVoid)
|
||||
{
|
||||
if (mask.IsAnyType)
|
||||
return TypeRefMask.MixedTypeName;
|
||||
|
||||
//
|
||||
var types = new List<string>(1);
|
||||
|
||||
// handle arrays separately
|
||||
var arrmask = mask & _isArrayMask;
|
||||
if (arrmask != 0)
|
||||
{
|
||||
mask &= ~_isArrayMask;
|
||||
ITypeRef elementtype = null;
|
||||
var elementmask = GetElementType(arrmask);
|
||||
if (elementmask.IsSingleType)
|
||||
elementtype = GetTypes(elementmask).FirstOrDefault();
|
||||
|
||||
if (elementtype != null)
|
||||
types.Add(elementtype.QualifiedName.ToString() + "[]");
|
||||
else
|
||||
types.Add(ArrayTypeRef.QualifiedName.ToString());
|
||||
}
|
||||
|
||||
//// int|double => number
|
||||
//var isNumber = (_isIntMask != 0 && _isDoubleMask != 0 && (mask & IsNumberMask) == IsNumberMask);
|
||||
//if (isNumber)
|
||||
// mask &= ~IsNumberMask;
|
||||
|
||||
//
|
||||
types.AddRange(GetTypes(mask).Select(t => t.QualifiedName.ToString()));
|
||||
|
||||
//if (isNumber)
|
||||
// types.Add("number");
|
||||
|
||||
//
|
||||
if (types.Count != 0)
|
||||
{
|
||||
types.Sort();
|
||||
return string.Join(PHPDocBlock.TypeVarDescTag.TypeNamesSeparator.ToString(), types.Distinct());
|
||||
}
|
||||
}
|
||||
|
||||
return TypeRefMask.VoidTypeName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets index of the given type within the context. Returns <c>-1</c> if such type is not present.
|
||||
/// </summary>
|
||||
public int GetTypeIndex(ITypeRef/*!*/typeref) { return _typeRefs.IndexOf(typeref); }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether given type mask represents a number.
|
||||
/// </summary>
|
||||
public bool IsNumber(TypeRefMask mask) { return (mask.Mask & IsNumberMask) != 0; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether given type mask represents a string type.
|
||||
/// </summary>
|
||||
public bool IsString(TypeRefMask mask) { return (mask.Mask & _isStringMask) != 0; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether given type mask represents a boolean.
|
||||
/// </summary>
|
||||
public bool IsBoolean(TypeRefMask mask) { return (mask.Mask & _isBoolMask) != 0; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether given type mask represents an integer type.
|
||||
/// </summary>
|
||||
public bool IsInteger(TypeRefMask mask) { return (mask.Mask & _isIntMask) != 0; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether given type mask represents a double type.
|
||||
/// </summary>
|
||||
public bool IsDouble(TypeRefMask mask) { return (mask.Mask & _isDoubleMask) != 0; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether given type mask represents an object.
|
||||
/// </summary>
|
||||
public bool IsObject(TypeRefMask mask) { return (mask.Mask & _isObjectMask) != 0; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether given type mask represents an array.
|
||||
/// </summary>
|
||||
public bool IsArray(TypeRefMask mask) { return (mask.Mask & _isArrayMask) != 0; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether given type mask represents a lambda function or <c>callable</c> primitive type.
|
||||
/// </summary>
|
||||
public bool IsLambda(TypeRefMask mask) { return (mask.Mask & _isLambdaMask) != 0; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether given type mask represents a resource.
|
||||
/// </summary>
|
||||
public bool IsResource(TypeRefMask mask) { return (mask.Mask & _isResourceMask) != 0; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether given type mask represents a primitive type.
|
||||
/// </summary>
|
||||
public bool IsPrimitiveType(TypeRefMask mask) { return (mask.Mask & _isPrimitiveMask) != 0; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether the type mask represents <c>null</c>.
|
||||
/// </summary>
|
||||
public bool IsNull(TypeRefMask mask)
|
||||
{
|
||||
return (mask.Mask & _nullTypeMask) != 0 && !mask.IsAnyType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether given type can be <c>null</c>.
|
||||
/// </summary>
|
||||
public bool IsNullable(TypeRefMask mask) { return (mask.Mask & IsNullableMask) != 0; }
|
||||
|
||||
public bool IsArrayKey(TypeRefMask mask) { return (mask.Mask & IsArrayKeyMask) != 0; } // TODO: type can be of type object with method __toString() ?
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether given mask represents an unspecified <c>object</c> instance.
|
||||
/// </summary>
|
||||
public bool IsAnObject(TypeRefMask mask)
|
||||
{
|
||||
return (mask.Mask & _AnObjectMask) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// In case of array type, gets its possible element types.
|
||||
/// </summary>
|
||||
public TypeRefMask GetElementType(TypeRefMask mask)
|
||||
{
|
||||
TypeRefMask result;
|
||||
if (IsArray(mask) && !mask.IsAnyType)
|
||||
{
|
||||
result = default(TypeRefMask); // uninitalized
|
||||
|
||||
var arrtypes = GetTypes(mask, _isArrayMask);
|
||||
foreach (var t in arrtypes)
|
||||
{
|
||||
Debug.Assert(t.IsArray);
|
||||
result |= t.ElementType;
|
||||
}
|
||||
|
||||
if (result.IsVoid)
|
||||
{
|
||||
// empty array
|
||||
//result = TypeRefMask.AnyType;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = TypeRefMask.AnyType;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,334 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Pchp.CodeAnalysis.FlowAnalysis
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents type mask for <see cref="TypeRefContext"/>.
|
||||
/// </summary>
|
||||
public struct TypeRefMask : IEquatable<TypeRefMask>, IEquatable<ulong>
|
||||
{
|
||||
#region Constants
|
||||
|
||||
public const string MixedTypeName = "mixed";
|
||||
public const string VoidTypeName = "void";
|
||||
|
||||
private const int BitsCount = sizeof(ulong) * 8;
|
||||
|
||||
/// <summary>
|
||||
/// Gets maximum number of types that can be handled by <see cref="TypeRefMask"/>.
|
||||
/// </summary>
|
||||
public const int IndicesCount = BitsCount - 2; // BitsCount - bits_reserved_for_flags
|
||||
|
||||
/// <summary>
|
||||
/// Bit mask of all flags supported by the <see cref="TypeRefMask"/>.
|
||||
/// </summary>
|
||||
public const ulong FlagsMask = (ulong)MaskFlags.Mask;
|
||||
|
||||
/// <summary>
|
||||
/// Mask of any type.
|
||||
/// </summary>
|
||||
public const ulong AnyTypeMask = ~(ulong)0;
|
||||
|
||||
#endregion
|
||||
|
||||
#region enum MaskFlags
|
||||
|
||||
/// <summary>
|
||||
/// Additional type mask flags.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
private enum MaskFlags : ulong
|
||||
{
|
||||
IsHint = (ulong)1 << (BitsCount - 1),
|
||||
IncludesSubclasses = (ulong)1 << (BitsCount - 2),
|
||||
|
||||
/// <summary>
|
||||
/// Mask of all flags.
|
||||
/// </summary>
|
||||
Mask = IsHint | IncludesSubclasses
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields & Properties
|
||||
|
||||
/// <summary>
|
||||
/// Each bit corresponds to a type within its <see cref="TypeRefContext"/>.
|
||||
/// </summary>
|
||||
public ulong Mask { get { return _mask; } }
|
||||
private ulong _mask;
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether the type represents an any type.
|
||||
/// </summary>
|
||||
public bool IsAnyType { get { return _mask == AnyTypeMask; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether the type information is not initialized.
|
||||
/// </summary>
|
||||
/// <remarks>Also represents <c>void</c> type.</remarks>
|
||||
public bool IsUninitialized { get { return _mask == (ulong)0; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether the type represents <c>void</c>.
|
||||
/// </summary>
|
||||
public bool IsVoid { get { return (_mask & ~(ulong)(MaskFlags.Mask)) == 0; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets value indicating whether the type represents a hint.
|
||||
/// </summary>
|
||||
public bool IsHint
|
||||
{
|
||||
get { return (_mask & (ulong)MaskFlags.IsHint) != 0; }
|
||||
set
|
||||
{
|
||||
if (value) SetIsHint();
|
||||
else RemoveIsHint();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether given type mask represents a type including its subclasses.
|
||||
/// </summary>
|
||||
public bool IncludesSubclasses
|
||||
{
|
||||
get { return (_mask & (ulong)MaskFlags.IncludesSubclasses) != 0; }
|
||||
set
|
||||
{
|
||||
if (value) SetIncludesSubclasses();
|
||||
else RemoveIncludesSubclasses();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether the mask represents just a single type reference.
|
||||
/// </summary>
|
||||
public bool IsSingleType
|
||||
{
|
||||
get
|
||||
{
|
||||
var mask = _mask & ~FlagsMask; // remove flags
|
||||
return mask != 0 && (mask & (mask - 1)) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets type mask representing any type.
|
||||
/// </summary>
|
||||
public static TypeRefMask AnyType { get { return new TypeRefMask(AnyTypeMask); } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Construction
|
||||
|
||||
public TypeRefMask(ulong mask)
|
||||
{
|
||||
_mask = mask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates type mask corresponding to a single type of given index.
|
||||
/// </summary>
|
||||
public static TypeRefMask CreateFromTypeIndex(int index)
|
||||
{
|
||||
var typemask = default(TypeRefMask);
|
||||
typemask.AddType(index);
|
||||
|
||||
//
|
||||
return typemask;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether this type mask represents the type with given index.
|
||||
/// </summary>
|
||||
public bool HasType(int index)
|
||||
{
|
||||
return ((_mask & ((ulong)1 << index)) != 0 && index < IndicesCount && index >= 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds type with given index to the mask.
|
||||
/// </summary>
|
||||
public void AddType(int index)
|
||||
{
|
||||
if (index >= 0 && index < IndicesCount)
|
||||
{
|
||||
_mask |= ((ulong)1 << index);
|
||||
}
|
||||
else
|
||||
{
|
||||
_mask = AnyTypeMask; // AnyType
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks this type as a hint.
|
||||
/// </summary>
|
||||
public void SetIsHint()
|
||||
{
|
||||
_mask |= (ulong)MaskFlags.IsHint;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks this type as not a hint.
|
||||
/// </summary>
|
||||
public void RemoveIsHint()
|
||||
{
|
||||
if (!this.IsAnyType)
|
||||
{
|
||||
_mask &= ~(ulong)MaskFlags.IsHint;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks this type as a hint.
|
||||
/// </summary>
|
||||
public void SetIncludesSubclasses()
|
||||
{
|
||||
_mask |= (ulong)MaskFlags.IncludesSubclasses;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks this type as not a hint.
|
||||
/// </summary>
|
||||
internal void RemoveIncludesSubclasses()
|
||||
{
|
||||
if (!this.IsAnyType)
|
||||
{
|
||||
_mask &= ~(ulong)MaskFlags.IncludesSubclasses;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets value indicating whether the mask represents a type at given index.
|
||||
/// </summary>
|
||||
/// <remarks>Index higher than or equal to <see cref="IndicesCount"/> are ignored.</remarks>
|
||||
public bool this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return HasType(index);
|
||||
}
|
||||
set
|
||||
{
|
||||
Debug.Assert(index >= 0);
|
||||
if (value) AddType(index);
|
||||
else if (index < IndicesCount && !this.IsAnyType) _mask &= ~((ulong)1 << index);
|
||||
}
|
||||
}
|
||||
|
||||
#region Object Members
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _mask.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is TypeRefMask && base.Equals((TypeRefMask)obj);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (IsAnyType) return MixedTypeName;
|
||||
|
||||
string value = null;
|
||||
if (IsVoid)
|
||||
{
|
||||
value = VoidTypeName;
|
||||
}
|
||||
else
|
||||
{
|
||||
ulong mask = this.Mask & ~(ulong)FlagsMask;
|
||||
for (int i = 0; mask != 0; i++, mask = (mask & ~(ulong)1) >> 1)
|
||||
{
|
||||
if ((mask & 1) != 0)
|
||||
{
|
||||
if (value != null) value += ",";
|
||||
value += (i).ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
if (IsHint)
|
||||
{
|
||||
value += " (hint)";
|
||||
}
|
||||
|
||||
//
|
||||
return value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEquatable<TypeRefMask> Members
|
||||
|
||||
public bool Equals(TypeRefMask other)
|
||||
{
|
||||
return Equals(other.Mask);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
||||
public static bool operator ==(TypeRefMask a, TypeRefMask b)
|
||||
{
|
||||
return a.Mask == b.Mask;
|
||||
}
|
||||
|
||||
public static bool operator !=(TypeRefMask a, TypeRefMask b)
|
||||
{
|
||||
return a.Mask != b.Mask;
|
||||
}
|
||||
|
||||
public static TypeRefMask operator +(TypeRefMask a, TypeRefMask b)
|
||||
{
|
||||
return new TypeRefMask(a.Mask | b.Mask);
|
||||
}
|
||||
|
||||
public static TypeRefMask operator |(TypeRefMask a, TypeRefMask b)
|
||||
{
|
||||
return new TypeRefMask(a.Mask | b.Mask);
|
||||
}
|
||||
|
||||
public static TypeRefMask operator |(TypeRefMask a, ulong b)
|
||||
{
|
||||
return new TypeRefMask(a.Mask | b);
|
||||
}
|
||||
|
||||
public static TypeRefMask Or (TypeRefMask a, TypeRefMask b)
|
||||
{
|
||||
return a | b;
|
||||
}
|
||||
|
||||
public static implicit operator ulong(TypeRefMask type)
|
||||
{
|
||||
return type.Mask;
|
||||
}
|
||||
|
||||
public static implicit operator TypeRefMask(ulong mask)
|
||||
{
|
||||
return new TypeRefMask(mask);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEquatable<ulong> Members
|
||||
|
||||
public bool Equals(ulong other)
|
||||
{
|
||||
return _mask == other;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using Pchp.CodeAnalysis.Semantics;
|
||||
using Pchp.CodeAnalysis.Utilities;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pchp.CodeAnalysis.FlowAnalysis
|
||||
{
|
||||
/// <summary>
|
||||
/// Queue of work items to do.
|
||||
/// </summary>
|
||||
internal class Worklist
|
||||
{
|
||||
/// <summary>
|
||||
/// List of blocks to be processed.
|
||||
/// </summary>
|
||||
readonly DistinctQueue<BoundBlock> _queue = new DistinctQueue<BoundBlock>();
|
||||
}
|
||||
}
|
|
@ -50,6 +50,13 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Compilation\SourceCompiler.cs" />
|
||||
<Compile Include="FlowAnalysis\TypeRef\CallInfo.cs" />
|
||||
<Compile Include="FlowAnalysis\TypeRef\ITypeRef.cs" />
|
||||
<Compile Include="FlowAnalysis\TypeRef\PhpDataType.cs" />
|
||||
<Compile Include="FlowAnalysis\TypeRef\TypeRef.cs" />
|
||||
<Compile Include="FlowAnalysis\TypeRef\TypeRefContext.cs" />
|
||||
<Compile Include="FlowAnalysis\TypeRef\TypeRefMask.cs" />
|
||||
<Compile Include="FlowAnalysis\Worklist.cs" />
|
||||
<Compile Include="Semantics\BoundMethodBody.cs" />
|
||||
<Compile Include="Semantics\Expressions.cs" />
|
||||
<Compile Include="Semantics\SemanticsBinder.cs" />
|
||||
|
@ -61,6 +68,7 @@
|
|||
<Compile Include="CommandLine\PhpParseOptions.cs" />
|
||||
<Compile Include="Compilation\PhpCompilation.cs" />
|
||||
<Compile Include="Compilation\PhpCompilationOptions.cs" />
|
||||
<Compile Include="FlowAnalysis\TypeRef\TypeHelpers.cs" />
|
||||
<Compile Include="Symbols\ReferenceManager.cs" />
|
||||
<Compile Include="Constants.cs" />
|
||||
<Compile Include="Emitter\Model\AssemblyReference.cs" />
|
||||
|
@ -105,14 +113,15 @@
|
|||
<Compile Include="Semantics\ISemanticModel.cs" />
|
||||
<Compile Include="Symbols\TypeSymbol.cs" />
|
||||
<Compile Include="Utilities\Contract.cs" />
|
||||
<Compile Include="Utilities\DistinctQueue.cs" />
|
||||
<Compile Include="Utilities\EnumeratorExtension.cs" />
|
||||
<Compile Include="Utilities\NameUtils.cs" />
|
||||
<Compile Include="Utilities\TypeHintValue.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="project.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="FlowAnalysis\" />
|
||||
<Folder Include="Symbols\Tables\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -21,12 +21,18 @@ namespace Pchp.CodeAnalysis.Semantics
|
|||
/// Gets type symbol by its name in current context.
|
||||
/// Can be <c>null</c> if type cannot be found.
|
||||
/// </summary>
|
||||
INamedTypeSymbol GetType(QualifiedName name);
|
||||
INamedTypeSymbol GetClass(QualifiedName name);
|
||||
|
||||
/// <summary>
|
||||
/// Get global function symbol by its name in current context.
|
||||
/// Can be <c>null</c> if function cannot be found.
|
||||
/// </summary>
|
||||
IMethodSymbol GetFunction(QualifiedName name);
|
||||
|
||||
/// <summary>
|
||||
/// Gets value determining whether <paramref name="qname"/> type can be assigned from <paramref name="from"/>.
|
||||
/// </summary>
|
||||
/// <remarks>Gets <c>true</c>, if <paramref name="qname"/> is equal to or is a base type of <paramref name="from"/>.</remarks>
|
||||
bool IsAssignableFrom(QualifiedName qname, INamedTypeSymbol from);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -166,15 +166,16 @@ namespace Pchp.CodeAnalysis.Symbols
|
|||
// If this is a nested type generic parameters in metadata include generic parameters of the outer types.
|
||||
int firstIndex = _genericParameterHandles.Count - _arity;
|
||||
|
||||
var ownedParams = new ITypeParameterSymbol[_arity];
|
||||
for (int i = 0; i < ownedParams.Length; i++)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
//ownedParams[i] = new PETypeParameterSymbol(moduleSymbol, this, (ushort)i, _genericParameterHandles[firstIndex + i]);
|
||||
}
|
||||
throw new NotImplementedException();
|
||||
|
||||
ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeParameters,
|
||||
ImmutableArray.Create<ITypeParameterSymbol>(ownedParams));
|
||||
//var ownedParams = new ITypeParameterSymbol[_arity];
|
||||
//for (int i = 0; i < ownedParams.Length; i++)
|
||||
//{
|
||||
// ownedParams[i] = new PETypeParameterSymbol(moduleSymbol, this, (ushort)i, _genericParameterHandles[firstIndex + i]);
|
||||
//}
|
||||
|
||||
//ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeParameters,
|
||||
// ImmutableArray.Create<ITypeParameterSymbol>(ownedParams));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace Pchp.CodeAnalysis.Symbols
|
|||
Contract.ThrowIfNull(trees);
|
||||
|
||||
var visitor = new PopulatorVisitor(compilation, this);
|
||||
trees.Foreach(visitor.VisitSourceUnit);
|
||||
trees.ForEach(visitor.VisitSourceUnit);
|
||||
}
|
||||
|
||||
public IMethodSymbol GetFunction(QualifiedName name) => _functions.TryGetOrDefault(name);
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pchp.CodeAnalysis.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents queue where items are enqueued just once and queue can be accessed in parralel.
|
||||
/// </summary>
|
||||
internal sealed class DistinctQueue<T>
|
||||
{
|
||||
readonly object _syncRoot = new object();
|
||||
|
||||
readonly HashSet<T> _set = new HashSet<T>();
|
||||
readonly Queue<T> _queue = new Queue<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Count of items in the queue.
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get { return _queue.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues item into the queue.
|
||||
/// </summary>
|
||||
public bool Enqueue(T value)
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
if (_set.Add(value))
|
||||
{
|
||||
_queue.Enqueue(value);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dequeues item from the queue.
|
||||
/// </summary>
|
||||
public T TryDequeue()
|
||||
{
|
||||
T value;
|
||||
|
||||
lock (_syncRoot)
|
||||
{
|
||||
if (_queue.Count != 0)
|
||||
{
|
||||
value = _queue.Dequeue();
|
||||
_set.Remove(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
value = default(T);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -50,7 +50,7 @@ namespace Pchp.CodeAnalysis
|
|||
/// <summary>
|
||||
/// Calls given action for each element in given enumerable.
|
||||
/// </summary>
|
||||
public static void Foreach<T>(this IEnumerable<T>/*!*/enumerable, Action<T>/*!*/func)
|
||||
public static void ForEach<T>(this IEnumerable<T>/*!*/enumerable, Action<T>/*!*/func)
|
||||
{
|
||||
Contract.ThrowIfNull(enumerable);
|
||||
Contract.ThrowIfNull(func);
|
||||
|
|
|
@ -62,5 +62,31 @@ namespace Pchp.CodeAnalysis
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether given qualified name was not set.
|
||||
/// </summary>
|
||||
public static bool IsEmpty(this QualifiedName qname)
|
||||
{
|
||||
return qname.IsSimpleName && string.IsNullOrEmpty(qname.Name.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Special PHP type and function names.
|
||||
/// </summary>
|
||||
public struct SpecialNames
|
||||
{
|
||||
public static QualifiedName ArrayAccess { get { return new QualifiedName(new Name("ArrayAccess")); } }
|
||||
public static QualifiedName Iterator { get { return new QualifiedName(new Name("Iterator")); } }
|
||||
public static QualifiedName Traversable { get { return new QualifiedName(new Name("Traversable")); } }
|
||||
public static QualifiedName Closure { get { return new QualifiedName(new Name("Closure")); } }
|
||||
public static QualifiedName Exception { get { return new QualifiedName(new Name("Exception")); } }
|
||||
|
||||
public static Name offsetGet { get { return new Name("offsetGet"); } }
|
||||
public static Name offsetSet { get { return new Name("offsetSet"); } }
|
||||
public static Name current { get { return new Name("current"); } }
|
||||
public static Name __invoke { get { return new Name("__invoke"); } }
|
||||
public static Name __toString { get { return new Name("__toString"); } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
using Pchp.Syntax;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pchp.CodeAnalysis.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Wraps type hint <see cref="object"/> provided by parser and provides properties to access real pimitive type or generic qualified name.
|
||||
/// </summary>
|
||||
public struct TypeHintValue
|
||||
{
|
||||
/// <summary>
|
||||
/// Hint object.
|
||||
/// </summary>
|
||||
private readonly object _obj;
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether the hint does not define any type.
|
||||
/// </summary>
|
||||
public bool IsEmpty { get { return object.ReferenceEquals(_obj, null); } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether the value represents a primitive type.
|
||||
/// </summary>
|
||||
public bool IsPrimitiveType { get { return _obj is PrimitiveTypeName; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether the value represents a class type.
|
||||
/// </summary>
|
||||
public bool IsGenericQualifiedName { get { return _obj is GenericQualifiedName; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating whether the value represents a class type.
|
||||
/// </summary>
|
||||
public bool IsQualifiedName { get { return IsGenericQualifiedName; } }
|
||||
|
||||
public PrimitiveTypeName PrimitiveTypeName { get { return (PrimitiveTypeName)_obj; } }
|
||||
|
||||
public GenericQualifiedName GenericQualifiedName { get { return (GenericQualifiedName)_obj; } }
|
||||
|
||||
public QualifiedName QualifiedName { get { return this.GenericQualifiedName.QualifiedName; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets name of the type or <c>null</c>.
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
if (!this.IsEmpty)
|
||||
{
|
||||
if (this.IsPrimitiveType) return this.PrimitiveTypeName.Name.Value;
|
||||
if (this.IsQualifiedName) return this.QualifiedName.ToString();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#region construction
|
||||
|
||||
/// <summary>
|
||||
/// Wraps type hint object.
|
||||
/// </summary>
|
||||
/// <param name="hint">Boxed primitive type or generic qualified type.</param>
|
||||
public TypeHintValue(object hint)
|
||||
{
|
||||
Debug.Assert(hint == null || hint is PrimitiveTypeName || hint is GenericQualifiedName);
|
||||
_obj = hint;
|
||||
}
|
||||
|
||||
public TypeHintValue(PrimitiveTypeName hint)
|
||||
: this((object)hint)
|
||||
{ }
|
||||
|
||||
public TypeHintValue(GenericQualifiedName hint)
|
||||
: this((object)hint)
|
||||
{ }
|
||||
|
||||
public TypeHintValue(QualifiedName hint)
|
||||
: this(new GenericQualifiedName(hint))
|
||||
{ }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче